Skip to content
Snippets Groups Projects
service.py 6.67 KiB
Newer Older
  • Learn to ignore specific revisions
  • owagner's avatar
    owagner committed
    #!/usr/bin/python
    # -*- coding: utf-8 -*-
    
    import xbmc,xbmcaddon
    import json
    
    import threading
    import time
    
    owagner's avatar
    owagner committed
    from lib import client as mqtt
    
    __addon__      = xbmcaddon.Addon()
    __version__    = __addon__.getAddonInfo('version')
    
    
    activeplayertype=""
    
    lasttitle=""
    lastdetail={}
    
    
    def sendrpc(method,params):
        res=xbmc.executeJSONRPC(json.dumps({"jsonrpc":"2.0","method":method,"params":params,"id":1}))
        xbmc.log("MQTT: JSON-RPC call "+method+" returned "+res)
        return json.loads(res)
    
    
    owagner's avatar
    owagner committed
    #
    # Publishes a MQTT message. The topic is built from the configured
    # topic prefix and the suffix. The message itself is JSON encoded,
    # with the "val" field set, and possibly more fields merged in.
    #
    
    owagner's avatar
    owagner committed
    def publish(suffix,val,more):
        global topic,mqc
        robj={}
        robj["val"]=val
        if more is not None:
            robj.update(more)
        jsonstr=json.dumps(robj)
        fulltopic=topic+"status/"+suffix
        xbmc.log("MQTT: Publishing @"+fulltopic+": "+jsonstr)
        mqc.publish(fulltopic,jsonstr,qos=0,retain=True)
    
    
    owagner's avatar
    owagner committed
    #
    # Set and publishes the playback state. Publishes more info if
    # the state is "playing"
    #
    
    def setplaystate(state,detail):
    
        global activeplayerid,activeplayertype
    
        if state==1:
            res=sendrpc("Player.GetActivePlayers",{})
            activeplayerid=res["result"][0]["playerid"]        
    
            activeplayertype=res["result"][0]["type"]
    
            res=sendrpc("Player.GetProperties",{"playerid":activeplayerid,"properties":["speed","currentsubtitle","currentaudiostream","repeat","subtitleenabled"]})
    
            publish("playbackstate",state,{"kodi_state":detail,"kodi_playbackdetails":res["result"],"kodi_playerid":activeplayerid,"kodi_playertype":activeplayertype})
    
            publish("playbackstate",state,{"kodi_state":detail,"kodi_playerid":activeplayerid,"kodi_playertype":activeplayertype})
    
    
    def convtime(ts):
        return("%02d:%02d:%02d" % (ts/3600,(ts/60)%60,ts%60))
    
    owagner's avatar
    owagner committed
    
    #
    # Publishes playback progress
    #
    
    def publishprogress():
    
    owagner's avatar
    owagner committed
        global player
    
        if not player.isPlaying():
    
    owagner's avatar
    owagner committed
            return
    
        pt=player.getTime()
        tt=player.getTotalTime()
        if pt<0:
            pt=0
    
    owagner's avatar
    owagner committed
        if tt>0:
            progress=(pt*100)/tt
        else:
            progress=0
    
        state={"kodi_time":convtime(pt),"kodi_totaltime":convtime(tt)}
        publish("progress",round(progress,1),state)
    
    owagner's avatar
    owagner committed
    #
    # Publish more details about the currently playing item
    #
    
    def publishdetails():
        global player,activeplayerid
    
        global lasttitle,lastdetail
    
        if not player.isPlaying():
    
    owagner's avatar
    owagner committed
            return
    
        res=sendrpc("Player.GetItem",{"playerid":activeplayerid,"properties":["title","streamdetails","file","thumbnail","fanart"]})
    
        if "result" in res:
            newtitle=res["result"]["item"]["title"]
            newdetail={"kodi_details":res["result"]["item"]}
            if newtitle!=lasttitle or newdetail!=lastdetail:
                lasttitle=newtitle
                lastdetail=newdetail
                publish("title",newtitle,newdetail)
    
    owagner's avatar
    owagner committed
    #
    # Notification subclasses
    #
    
    owagner's avatar
    owagner committed
    class MQTTMonitor(xbmc.Monitor):
        def onSettingsChanged(self):
            global mqc
            xbmc.log("MQTT: Settings changed, reconnecting broker")
            mqc.loop_stop(True)
            startmqtt()
    
    class MQTTPlayer(xbmc.Player):
        def onPlayBackStarted(self):
    
            setplaystate(1,"started")
    
    owagner's avatar
    owagner committed
    
        def onPlayBackPaused(self):
    
            setplaystate(2,"paused")
    
    owagner's avatar
    owagner committed
    
        def onPlayBackResumed(self):
    
            setplaystate(1,"resumed")
    
    owagner's avatar
    owagner committed
    
        def onPlayBackEnded(self):
    
            setplaystate(0,"ended")
    
    owagner's avatar
    owagner committed
            
        def onPlayBackStopped(self):
    
            setplaystate(0,"stopped")
            
        def onPlayBackSeek(self):
            publishprogress()
    
        def onPlayBackSeek(self):
            publishprogress()
            
        def onPlayBackSeekChapter(self):
            publishprogress()
        
        def onPlayBackSpeedChanged(speed):
            setplaystate(1,"speed")
    
    owagner's avatar
    owagner committed
        def onQueueNextItem():
            xbmc.log("MQTT onqn");
    
    #
    # Handles commands
    #
    def processnotify(data):
        try:
            params=json.loads(data)
        except ValueError:
            parts=data.split(None,2)
            params={"title":parts[0],"message":parts[1]}
        sendrpc("GUI.ShowNotification",params)
    
    def processplay(data):
        try:
            params=json.loads(data)
            sendrpc("Player.Open",params)
        except ValueError:
            player.play(data)
    
    def processplaybackstate(data):
        if data=="0" or data=="stop":
            player.stop()
        elif data=="1" or data=="resume":
            if not player.isPlaying():
                player.pause()
        elif data=="2" or data=="pause":
            if player.isPlaying():
                player.pause()
        elif data=="next":
            player.playnext()
        elif data=="previous":
            player.playprevious()        
    
    def processcommand(topic,data):
        if topic=="notify":
            processnotify(data)
        elif topic=="play":
            processplay(data)
        elif topic=="playbackstate":
            processplaybackstate(data)
        else:
            xbmc.log("MQTT: Unknown command "+topic)
    
    #
    # Handles incoming MQTT messages
    #
    
    owagner's avatar
    owagner committed
    def msghandler(mqc,userdata,msg):
        try:
            global topic
            if msg.retain:
                return
            mytopic=msg.topic[len(topic):]
            if mytopic.startswith("command/"):
                processcommand(mytopic[8:],msg.payload)
        except Exception as e:
            xbmc.log("MQTT: Error processing message %s: %s" % (type(e).__name__,e))
    
    def connecthandler(mqc,userdata,rc):
        xbmc.log("MQTT: Connected to MQTT broker with rc=%d" % (rc))
        mqc.subscribe(topic+"command/#",qos=0)
    
    def disconnecthandler(mqc,userdata,rc):
        xbmc.log("MQTT: Disconnected from MQTT broker with rc=%d" % (rc))
        time.sleep(5)
        mqc.reconnect()
    
    
    owagner's avatar
    owagner committed
    #
    # Starts connection to the MQTT broker, sets the will
    # and subscribes to the command topic
    #
    
    owagner's avatar
    owagner committed
    def startmqtt():
        global topic,mqc
        mqc=mqtt.Client()
        mqc.on_message=msghandler
        mqc.on_connect=connecthandler
        mqc.on_disconnect=disconnecthandler
    
        if __addon__.getSetting("mqttanonymousconnection")=='false':
    
    drlobo's avatar
    drlobo committed
            mqc.username_pw_set(__addon__.getSetting("mqttusername"), __addon__.getSetting("mqttpassword"))
    
    owagner's avatar
    owagner committed
        topic=__addon__.getSetting("mqtttopic")
        if not topic.endswith("/"):
            topic+="/"
        mqc.will_set(topic+"connected",0,qos=2,retain=True)
        xbmc.log("MQTT: Connecting to MQTT broker at %s:%s" % (__addon__.getSetting("mqtthost"),__addon__.getSetting("mqttport")))
        mqc.connect(__addon__.getSetting("mqtthost"),__addon__.getSetting("mqttport"),60)
    
        mqc.publish(topic+"connected",2,qos=1,retain=True)
    
    owagner's avatar
    owagner committed
        mqc.loop_start()
    
    
    owagner's avatar
    owagner committed
    #
    # Addon initialization and shutdown
    #
    
    owagner's avatar
    owagner committed
    if (__name__ == "__main__"):
        global monitor,player
        xbmc.log('MQTT: MQTT Adapter Version %s started' % __version__)
        monitor=MQTTMonitor()
        player=MQTTPlayer()
        startmqtt()
    
        while not monitor.waitForAbort(20):
            publishdetails()
    
    owagner's avatar
    owagner committed
        mqc.loop_stop(True)