From 541902200abab0d58ffba7ea4934db214a84883a Mon Sep 17 00:00:00 2001 From: Morg42 <43153739+Morg42@users.noreply.github.com> Date: Thu, 26 Oct 2023 10:55:16 +0200 Subject: [PATCH] kodi: fixes, docs, add get_settings --- kodi/__init__.py | 17 ++-- kodi/commands.py | 5 +- kodi/plugin.yaml | 200 +++++++++++++++++++++++++++++++++++++++++++--- kodi/user_doc.rst | 39 ++++++--- 4 files changed, 229 insertions(+), 32 deletions(-) diff --git a/kodi/__init__.py b/kodi/__init__.py index 47cc33b13..70a3489b9 100644 --- a/kodi/__init__.py +++ b/kodi/__init__.py @@ -312,19 +312,22 @@ def on_data_received(self, by, data, command=None): return # if we reach this point, no special handling case was detected, so just go on normally... - try: # try and transform the JSON RPC method into the matching command - command = self._commands.get_command_from_reply(command) - value = self._commands.get_shng_data(command, data) + commands = self._commands.get_commands_from_reply(command) + if not isinstance(commands, list): + commands = [commands] + + for command in commands: + value = self._commands.get_shng_data(command, data) + + # pass on data for regular item assignment + self.logger.debug(f'received data "{data}" for command {command} converted to value {value}') + self._dispatch_callback(command, value, by) except Exception as e: self.logger.info(f'received data "{data}" for command {command}, error occurred while converting. Discarding data. Error was: {e}') return - # pass on data for regular item assignment - self.logger.debug(f'received data "{data}" for command {command} converted to value {value}') - self._dispatch_callback(command, value, by) - def _do_before_send(self, command, value, kwargs): """ Checks for special commands and handles them diff --git a/kodi/commands.py b/kodi/commands.py index 7bd33711a..6d85f8d98 100644 --- a/kodi/commands.py +++ b/kodi/commands.py @@ -17,15 +17,14 @@ 'update': {'read': False, 'write': True, 'opcode': 'update', 'reply_pattern': '*', 'item_type': 'bool', 'dev_datatype': 'raw', 'params': None}, 'ping': {'read': True, 'write': False, 'opcode': 'JSONRPC.Ping', 'reply_pattern': '*', 'item_type': 'bool', 'dev_datatype': 'raw', 'params': None}, 'get_status_au': {'read': True, 'write': False, 'opcode': 'Application.GetProperties', 'reply_pattern': '*', 'item_type': 'bool', 'dev_datatype': 'raw', 'params': {'properties': ['volume', 'muted']}}, - - 'get_players': {'read': True, 'write': False, 'opcode': 'Player.GetPlayers', 'reply_pattern': '*', 'item_type': 'bool', 'dev_datatype': 'raw', 'params': None}, 'get_actplayer': {'read': True, 'write': False, 'opcode': 'Player.GetActivePlayers', 'reply_pattern': '*', 'item_type': 'bool', 'dev_datatype': 'raw', 'params': None}, + 'get_settings': {'read': True, 'write': False, 'opcode': 'Settings.GetSettings', 'reply_pattern': '*', 'item_type': 'dict', 'dev_datatype': 'raw', 'params': None}, 'get_status_play': {'read': True, 'write': False, 'opcode': 'Player.GetProperties', 'reply_pattern': '*', 'item_type': 'bool', 'dev_datatype': 'raw', 'params': {'playerid': '{ID}', 'properties': ['type', 'speed', 'time', 'percentage', 'totaltime', 'position', 'currentaudiostream', 'audiostreams', 'subtitleenabled', 'currentsubtitle', 'subtitles', 'currentvideostream', 'videostreams']}}, 'get_item': {'read': True, 'write': False, 'opcode': 'Player.GetItem', 'reply_pattern': '*', 'item_type': 'bool', 'dev_datatype': 'raw', 'params': {'playerid': '{ID}', 'properties': ['title', 'artist']}}, 'get_favourites': {'read': True, 'write': False, 'opcode': 'Favourites.GetFavourites', 'reply_pattern': '*', 'item_type': 'bool', 'dev_datatype': 'raw', 'params': {'properties': ['window', 'path', 'thumbnail', 'windowparameter']}}, }, 'control': { - 'playpause': {'read': True, 'write': True, 'opcode': 'Player.PlayPause', 'reply_pattern': '*', 'item_type': 'bool', 'dev_datatype': 'raw', 'params': {'playerid': '{ID}', 'play': 'toggle'}, 'item_attrs': {'read_group_levels': 0}}, + 'playpause': {'read': True, 'write': True, 'opcode': 'Player.PlayPause', 'reply_pattern': r'{\'speed\': (\d)}', 'item_type': 'bool', 'dev_datatype': 'raw', 'params': {'playerid': '{ID}', 'play': 'toggle'}, 'item_attrs': {'read_group_levels': 0}}, 'seek': {'read': True, 'write': True, 'opcode': 'Player.Seek', 'reply_pattern': '*', 'item_type': 'num', 'dev_datatype': 'raw', 'params': {'playerid': '{ID}', 'value': '{VALUE}'}, 'cmd_settings': {'force_min': 0.0, 'force_max': 100.0}, 'item_attrs': {'read_group_levels': 0}}, 'audio': {'read': True, 'write': True, 'opcode': 'Player.SetAudioStream', 'reply_pattern': '*', 'item_type': 'foo', 'dev_datatype': 'raw', 'params': {'playerid': '{ID}', 'stream': '{VALUE}'}, 'item_attrs': {'read_group_levels': 0}}, 'speed': {'read': True, 'write': True, 'opcode': 'Player.SetSpeed', 'reply_pattern': '*', 'item_type': 'num', 'dev_datatype': 'raw', 'params': {'playerid': '{ID}', 'speed': '{VALUE}'}, 'cmd_settings': {'valid_list': [-32,-16,-8,-4,-2,-1,1,2,4,8,16,32]}, 'item_attrs': {'read_group_levels': 0}}, diff --git a/kodi/plugin.yaml b/kodi/plugin.yaml index 404cb0a5a..d294c287f 100644 --- a/kodi/plugin.yaml +++ b/kodi/plugin.yaml @@ -288,17 +288,17 @@ item_structs: kodi_read_group@instance: - status - get_players: + get_actplayer: type: bool - kodi_command@instance: status.get_players + kodi_command@instance: status.get_actplayer kodi_read@instance: true kodi_write@instance: false kodi_read_group@instance: - status - get_actplayer: - type: bool - kodi_command@instance: status.get_actplayer + get_settings: + type: dict + kodi_command@instance: status.get_settings kodi_read@instance: true kodi_write@instance: false kodi_read_group@instance: @@ -497,23 +497,22 @@ item_structs: - ALL - ALL.status - get_players: + get_actplayer: type: bool - kodi_command@instance: status.get_players + kodi_command@instance: status.get_actplayer kodi_read@instance: true kodi_write@instance: false kodi_read_group@instance: - ALL - ALL.status - get_actplayer: - type: bool - kodi_command@instance: status.get_actplayer + get_settings: + type: dict + kodi_command@instance: status.get_settings kodi_read@instance: true kodi_write@instance: false kodi_read_group@instance: - - ALL - - ALL.status + - status get_status_play: type: bool @@ -620,5 +619,182 @@ item_structs: kodi_command@instance: control.action kodi_read@instance: true kodi_write@instance: true + + old_info: + + read: + type: bool + enforce_updates: true + kodi_read_group_trigger@instance: old_info + + player: + type: num + kodi_command@instance: info.player + kodi_read@instance: true + kodi_write@instance: false + kodi_read_group@instance: + - old_info + + state: + type: str + kodi_command@instance: info.state + kodi_read@instance: true + kodi_write@instance: false + kodi_read_group@instance: + - old_info + + media: + type: str + kodi_command@instance: info.media + kodi_read@instance: true + kodi_write@instance: false + kodi_read_group@instance: + - old_info + + title: + type: str + kodi_command@instance: info.title + kodi_read@instance: true + kodi_write@instance: false + kodi_read_group@instance: + - old_info + + audiostreams: + type: list + kodi_command@instance: info.streams + kodi_read@instance: true + kodi_write@instance: false + kodi_read_group@instance: + - old_info + + subtitles: + type: list + kodi_command@instance: info.subtitles + kodi_read@instance: true + kodi_write@instance: false + kodi_read_group@instance: + - old_info + + old_control: + + update: + type: bool + kodi_command@instance: status.update + kodi_read@instance: false + kodi_write@instance: true + + play: + type: bool + eval: sh...playpause() and not sh...stop() + enforce_updates: true + eval_trigger: + - ..stop + - ..playpause + + play_cmd: + type: bool + enforce_updates: true + eval: sh....input('play') if not (sh...property.last_update_by.lower()[:4] in ('kodi', 'init', 'eval')) else None + eval_trigger: .. + + pause: + type: bool + eval: not sh...playpause() and not sh...stop() + enforce_updates: true + eval_trigger: + - ..stop + - ..playpause + + pause_cmd: + type: bool + enforce_updates: true + eval: sh....input('pause') if not (sh...property.last_update_by.lower()[:4] in ('kodi', 'init', 'eval')) else None + eval_trigger: .. + + stop: + type: bool + kodi_command@instance: control.stop + kodi_read@instance: true + kodi_write@instance: true + + playpause: + type: bool + kodi_command@instance: control.playpause + kodi_read@instance: true + kodi_write@instance: true + + previous: + type: bool + eval: sh...input('skipprevious') + enforce_updates: true + + next: + type: bool + eval: sh...input('skipnext') + enforce_updates: true + + seek: + type: num + kodi_command@instance: control.seek + kodi_read@instance: true + kodi_write@instance: true + + speed: + type: num + kodi_command@instance: control.speed + kodi_read@instance: true + kodi_write@instance: true + + volume: + type: num + kodi_command@instance: control.volume + kodi_read@instance: true + kodi_write@instance: true + + mute: + type: bool + kodi_command@instance: control.mute + kodi_read@instance: true + kodi_write@instance: true + + macro: + type: bool + kodi_command@instance: info.macro + kodi_read@instance: true + kodi_write@instance: true + + on_off: + type: bool + kodi_command@instance: control.power + kodi_read@instance: true + kodi_write@instance: true + + input: + type: str + kodi_command@instance: control.action + kodi_read@instance: true + kodi_write@instance: true + + get_favourites: + type: bool + kodi_command@instance: status.get_favourites + kodi_read@instance: true + kodi_write@instance: false + kodi_read_group@instance: + - old_info + + audiostream: + type: foo + kodi_command@instance: control.audio + kodi_read@instance: true + kodi_write@instance: true + + subtitle: + type: foo + kodi_command@instance: control.subtitle + kodi_read@instance: true + kodi_write@instance: true + + plugin_functions: NONE logic_parameters: NONE diff --git a/kodi/user_doc.rst b/kodi/user_doc.rst index 2c964c7aa..b3ca5b052 100755 --- a/kodi/user_doc.rst +++ b/kodi/user_doc.rst @@ -12,23 +12,32 @@ Hinweise zur Umstellung Das kodi-Plugin wurde komplett auf smartdeviceplugin umgestellt. Damit gehen sowohl in der Struktur der items als auch der verfügbaren Kommandos erhebliche Änderungen einher, die eine Anpassung der Item-Konfiguration erfordern. -Die Umstellung der Items ist erstmal einfach - das Plugin bringt ein struct mit, das einfach eingebunden werden kann: +Wenn bisher die mitgelieferten Structs genutzt wurden, muss nur die struct-Einbindung geändert werden: aus .. code-block:: yaml # items/media.yaml - media: - kodi: - struct: kodi.ALL + kodi: + struct: + - kodi.control + - kodi.info + + +wird neu + +.. code-block:: yaml + + # items/media.yaml + kodi: + struct: + - kodi.old_control + - kodi.old_info - suspend: - type: bool - cache: true -Damit werden alle verfügbaren Items (für alle verfügbaren Kommandos) aus dem Struct eingebunden. Diese sind nach den Gruppen "Info" (Status des kodi-Players), "Status" (Status und Interna der kodi-Software) und "Control" (Kommandos zur Steuerung der Wiedergabe) gruppiert; diese Gruppierung ist auch in den Items abgebildet. -Eine Visualisierung, ggf. deren Widgets, und andere Items, die mit Kodi verbunden sind, müssen dahingehend entsprechend angepasst werden. +Damit ist die alte Item-Struktur 1:1 übernommen und nutzt die neuen Funktionen. -Alternativ kann die alte Item-Konfiguration manuell umgestellt werden, indem die entsprechenden Item-Attribute des kodi-Plugins ersetzt werden. +Wenn die Items einzeln erstellt und konfiguriert wurden, muss die alte Item-Konfiguration +manuell umgestellt werden, indem die entsprechenden Item-Attribute des kodi-Plugins ersetzt werden. .. code-block:: yaml @@ -69,6 +78,15 @@ Konfiguration host: 10.0.0.42 suspend_item: media.kodi.suspend +.. code-block:: yaml + + # items/media.yaml + kodi: + struct: kodi.ALL + + suspend: + type: bool + cache: true Hinweise zu Verbindungen @@ -78,3 +96,4 @@ Hinweise zu Verbindungen Das weitere Verhalten wird über die Parameter `retry_cycle` (Wartezeit) und `retry_suspend` (Anzahl Zyklen) eingestellt. Nach Ablauf dieser Versuche wartet das Plugin 30 Sekunden (bzw. die in `retry_cycle` eingestellte Zeit), bevor das Ganze wiederholt wird. Wenn `retry_suspend` gesetzt ist, wechselt das Plugin nach dieser Anzahl von `retry_cycles` in den Suspend-Modus und beendet die Verbindungsversuche. Um den Suspend-Modus zu beenden, kann mit dem Plugin-Attribut `suspend_item` ein Item konfiguriert werden, mit dem der Suspend-Modus ein- und ausgeschaltet werden kann. Alternativ stehen die Plugin-Funktionen `suspend()` und `resume()` zur Verfügung. +