From 9d1952bcffde995041f5307c836d86221ce9c902 Mon Sep 17 00:00:00 2001 From: owagner <owagner@tellerulam.com> Date: Sun, 22 Mar 2015 22:43:47 +0100 Subject: [PATCH] V0.3 - 2015-03-22 - owagner - fixed division by zero when switching TV channels - now supports command/notify to send notifications - now supports command/play to start playback of files or items - now supports command/playbackstate to control the playback state --- README.md | 22 ++++++++-- service.mqtt/addon.xml | 2 +- service.mqtt/changelog.txt | 6 +++ service.mqtt/service.py | 90 +++++++++++++++++++++++++++++++++----- 4 files changed, 105 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 5027fd4..c41c12d 100644 --- a/README.md +++ b/README.md @@ -30,13 +30,13 @@ Settings The addon has three settings: * the MQTT broker's IP address (defaults to 127.0.0.1) -* the MQTT broker's port. This defaults to 1883, which is standard. +* the MQTT broker's port. This defaults to 1883, which is the MQTT standard port for unencrypted connections. * the topic prefix which to use in all published and subscribed topics. Defaults to "kodi/". Topics ------ -The addon publishes on the following topics: +The addon publishes on the following topics (prefixed with the configured topic prefix): * connected: 2 if the addon is currently connected to the broker, 0 otherwise. This topic is set to 0 with a MQTT will. * status/playbackstate: a JSON-encoded object with the fields @@ -52,10 +52,24 @@ The addon publishes on the following topics: - "val": the title of the current playback item - "kodi_details": an object with further details about the current playback items. This is effectivly the result of a JSON-RPC call Player.GetItem with the properties "title", "streamdetails" and "file" - - + +The addon listens to the following topics (prefixed with the configured topic prefix): + +* command/notify: Either a simple string, or a JSON encoded object with the fields "message" and "title". Shows + a popup notification in Kodi +* command/play: Either a simple string which is a filename or URL, or a JSON encoded object which correspondents + to the Player.Open() JSON_RPC call +* command/playbackstate: A simple string or numeric with the values: + - "0" or "stop" to stop playback + - "1" or "resume" to resume playback (when paused) + - "2" or "pause" to stop playback (when playing) + - "next" to play the next track + - "previous" to play the previous track + + See also -------- +- JSON-RPC API v6 in Kodi: http://kodi.wiki/view/JSON-RPC_API/v6 - Project overview: https://github.com/mqtt-smarthome diff --git a/service.mqtt/addon.xml b/service.mqtt/addon.xml index 67d735f..6526d4f 100644 --- a/service.mqtt/addon.xml +++ b/service.mqtt/addon.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" standalone="yes"?> -<addon id="service.mqtt" name="MQTT Adapter" version="0.2" provider-name="owagner"> +<addon id="service.mqtt" name="MQTT Adapter" version="0.3" provider-name="owagner"> <requires> <import addon="xbmc.python" version="2.19.0"/> </requires> diff --git a/service.mqtt/changelog.txt b/service.mqtt/changelog.txt index efd664b..ace3705 100644 --- a/service.mqtt/changelog.txt +++ b/service.mqtt/changelog.txt @@ -1,3 +1,9 @@ +V0.3 - 2015-03-22 - owagner + - fixed division by zero when switching TV channels + - now supports command/notify to send notifications + - now supports command/play to start playback of files or items + - now supports command/playbackstate to control the playback state + V0.2 - 2015-03-22 - owagner - refactored as a Kodi addon \ No newline at end of file diff --git a/service.mqtt/service.py b/service.mqtt/service.py index 5cf524e..e128edb 100644 --- a/service.mqtt/service.py +++ b/service.mqtt/service.py @@ -17,6 +17,11 @@ def sendrpc(method,params): xbmc.log("MQTT: JSON-RPC call "+method+" returned "+res) return json.loads(res) +# +# 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. +# def publish(suffix,val,more): global topic,mqc robj={} @@ -28,6 +33,10 @@ def publish(suffix,val,more): xbmc.log("MQTT: Publishing @"+fulltopic+": "+jsonstr) mqc.publish(fulltopic,jsonstr,qos=0,retain=True) +# +# Set and publishes the playback state. Publishes more info if +# the state is "playing" +# def setplaystate(state,detail): global activeplayerid if state==1: @@ -41,7 +50,10 @@ def setplaystate(state,detail): def convtime(ts): return("%02d:%02d:%02d" % (ts/3600,(ts/60)%60,ts%60)) - + +# +# Publishes playback progress +# def publishprogress(): global player if not player.isPlaying(): @@ -50,15 +62,16 @@ def publishprogress(): tt=player.getTotalTime() if pt<0: pt=0 - progress=(pt*100)/tt + 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) -def reportprogress(): - global monitor - while not monitor.waitForAbort(30): - publishprogress() - +# +# Publish more details about the currently playing item +# def publishdetails(): global player,activeplayerid if not player.isPlaying(): @@ -67,6 +80,9 @@ def publishdetails(): publish("title",res["result"]["item"]["title"],{"kodi_details":res["result"]["item"]}) publishprogress() +# +# Notification subclasses +# class MQTTMonitor(xbmc.Monitor): def onSettingsChanged(self): global mqc @@ -102,6 +118,54 @@ class MQTTPlayer(xbmc.Player): def onPlayBackSpeedChanged(speed): setplaystate(1,"speed") + 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 +# def msghandler(mqc,userdata,msg): try: global topic @@ -122,6 +186,10 @@ def disconnecthandler(mqc,userdata,rc): time.sleep(5) mqc.reconnect() +# +# Starts connection to the MQTT broker, sets the will +# and subscribes to the command topic +# def startmqtt(): global topic,mqc mqc=mqtt.Client() @@ -137,14 +205,16 @@ def startmqtt(): mqc.publish(topic+"connected",2,qos=1,retain=True) mqc.loop_start() +# +# Addon initialization and shutdown +# if (__name__ == "__main__"): global monitor,player xbmc.log('MQTT: MQTT Adapter Version %s started' % __version__) monitor=MQTTMonitor() player=MQTTPlayer() - progressthread=threading.Thread(target=reportprogress) - progressthread.start() startmqtt() - monitor.waitForAbort() + while not monitor.waitForAbort(30): + publishprogress() mqc.loop_stop(True) \ No newline at end of file -- GitLab