diff --git a/lms/__init__.py b/lms/__init__.py index 8cdc17b36..a5ae74906 100755 --- a/lms/__init__.py +++ b/lms/__init__.py @@ -50,7 +50,7 @@ class SmartPluginWebIf(): class lms(SmartDevicePlugin): """ Device class for Logitech Mediaserver/Squeezebox function. """ - PLUGIN_VERSION = '1.5.3' + PLUGIN_VERSION = '1.6.0' def _set_device_defaults(self): self.custom_commands = 1 @@ -107,6 +107,7 @@ def trigger_read(command): trigger_read('info.playlists.names') # set alarm if command == f'player.control.alarms{CUSTOM_SEP}{custom}': + return # This does not really work currently. The created string is somehow correct. # However, much more logic has to be included to add/update/delete alarms, etc. try: @@ -115,7 +116,7 @@ def trigger_read(command): alarm = f"id:{i} " for k, v in d.items(): alarm += f"{k}:{v} " - alarm = f"alarm add {alarm.strip()}" + alarm = f"add {alarm.strip()}" self.logger.debug(f"Set alarm: {alarm}") self.send_command('player.control.set_alarm' + CUSTOM_SEP + custom, alarm) except Exception as e: @@ -143,6 +144,10 @@ def trigger_read(command): self.logger.debug(f"Got command id {command} data {data} value {value} custom {custom} by {by}") trigger_read('player.playlist.name') + if command == f'player.control.sync{CUSTOM_SEP}{custom}': + self.logger.debug(f"Got command sync {command} data {data} value {value} custom {custom} by {by}") + trigger_read('server.syncgroups.members') + # update on new song if command == f'player.info.title{CUSTOM_SEP}{custom}': # trigger_read('player.control.playmode') diff --git a/lms/commands.py b/lms/commands.py index c1401eb30..b19a3ff10 100755 --- a/lms/commands.py +++ b/lms/commands.py @@ -6,98 +6,101 @@ commands = { 'server': { - 'listenmode': {'read': True, 'write': True, 'write_cmd': 'listen {RAW_VALUE:01}', 'item_type': 'bool', 'dev_datatype': 'LMSonoff', 'reply_pattern': r'listen (\d)', 'item_attrs': {'custom1': ''}}, - 'playercount': {'read': True, 'write': False, 'read_cmd': 'player count ?', 'item_type': 'num', 'dev_datatype': 'str', 'reply_pattern': r'player count (\d+)', 'item_attrs': {'initial': True, 'custom1': ''}}, - 'favoritescount': {'read': True, 'write': False, 'read_cmd': 'favorites items', 'item_type': 'num', 'dev_datatype': 'str', 'reply_pattern': r'favorites items\s+ count:(\d+)', 'item_attrs': {'initial': True, 'custom1': ''}}, + 'listenmode': {'read': True, 'write': True, 'write_cmd': 'listen {RAW_VALUE:01}', 'item_type': 'bool', 'dev_datatype': 'LMSonoff', 'reply_pattern': r'^listen (\d)', 'custom_disable': True}, + 'playercount': {'read': True, 'write': False, 'read_cmd': 'player count ?', 'item_type': 'num', 'dev_datatype': 'str', 'reply_pattern': r'^player count (\d+)', 'custom_disable': True, 'item_attrs': {'initial': True}}, + 'favoritescount': {'read': True, 'write': False, 'read_cmd': 'favorites items', 'item_type': 'num', 'dev_datatype': 'str', 'reply_pattern': r'^favorites items\s+ count:(\d+)', 'custom_disable': True, 'item_attrs': {'initial': True}}, + 'syncgroups': { + 'members': {'read': True, 'write': False, 'read_cmd': 'syncgroups ?', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': [r'^syncgroups sync_members:(.*) sync_member_names:', r'^syncgroups(.*)?$'], 'custom_disable': True, 'item_attrs': {'initial': True}}, + 'names': {'read': True, 'write': False, 'read_cmd': 'syncgroups ?', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': [r'^syncgroups sync_members:.* sync_member_names:(.*)', r'^syncgroups(.*)?$'], 'custom_disable': True, 'item_attrs': {'initial': False}}, + } }, 'database': { 'rescan': { - 'start': {'read': False, 'write': True, 'write_cmd': 'rescan {VALUE}', 'item_type': 'str', 'dev_datatype': 'str', 'cmd_settings': {'valid_list': ['playlists', 'onlinelibrary', 'external', 'full']}, 'item_attrs': {'attributes': {'remark': 'playlists|onlinelibrary|external|full|full file://some/path'}, 'custom1': ''}}, - 'running': {'read': True, 'write': False, 'read_cmd': 'rescan ?', 'item_type': 'bool', 'dev_datatype': 'LMSRescan', 'reply_pattern': 'rescan (.*)', 'item_attrs': {'cycle': '120', 'initial': True, 'custom1': ''}}, - 'progress': {'read': True, 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': 'scanner notify progress:(.*)', 'item_attrs': {'custom1': ''}}, - 'runningtime': {'read': True, 'read_cmd': 'rescanprogress totaltime', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': 'rescanprogress totaltime .* rescan:([0-9]{2}:[0-9]{2}:[0-9]{2})', 'item_attrs': {'custom1': ''}}, - 'fail': {'read': True, 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': 'rescanprogress totaltime rescan:0 lastscanfailed:(.*)', 'item_attrs': {'custom1': ''}}, - 'abortscan': {'read': True, 'write': True, 'write_cmd': 'abortscan', 'item_type': 'bool', 'dev_datatype': 'str', 'reply_pattern': 'abortscan', 'item_attrs': {'custom1': ''}}, - 'wipecache': {'read': True, 'write': True, 'write_cmd': 'wipecache', 'item_type': 'bool', 'dev_datatype': 'LMSWipecache', 'reply_pattern': 'wipecache', 'item_attrs': {'custom1': ''}} + 'start': {'read': False, 'write': True, 'write_cmd': 'rescan {VALUE}', 'item_type': 'str', 'dev_datatype': 'str', 'cmd_settings': {'valid_list_re': ['playlists', 'onlinelibrary', 'external', 'full', r'full file://.*']}, 'custom_disable': True, 'item_attrs': {'attributes': {'remark': 'playlists|onlinelibrary|external|full|full file://some/path'}}}, + 'running': {'read': True, 'write': False, 'read_cmd': 'rescan ?', 'item_type': 'bool', 'dev_datatype': 'LMSRescan', 'reply_pattern': [r'^rescanprogress rescan:(.*)', r'^rescan (.*)', r'^scanner notify progress:(.*)'], 'custom_disable': True, 'item_attrs': {'cycle': '120', 'initial': True}}, + 'progress': {'read': True, 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': r'^scanner notify progress:(.*)', 'custom_disable': True, 'item_attrs': {'item_template': 'rescanprogress'}}, + 'runningtime': {'read': True, 'read_cmd': 'rescanprogress totaltime', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': r'^(?:rescanprogress totaltime.*)?rescan:(0|[0-9]{2}:[0-9]{2}:[0-9]{2})', 'custom_disable': True}, + 'fail': {'read': True, 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': r'^rescanprogress totaltime rescan:0 lastscanfailed:(.*)', 'custom_disable': True}, + 'abortscan': {'read': True, 'write': True, 'write_cmd': 'abortscan', 'item_type': 'bool', 'dev_datatype': 'str', 'reply_pattern': r'^abortscan$', 'custom_disable': True}, + 'wipecache': {'read': True, 'write': True, 'write_cmd': 'wipecache', 'item_type': 'bool', 'dev_datatype': 'LMSWipecache', 'reply_pattern': r'^wipecache$', 'custom_disable': True} }, - 'totalgenres': {'read': True, 'write': False, 'read_cmd': 'info total genres ?', 'item_type': 'num', 'dev_datatype': 'str', 'reply_pattern': r'info total genres (\d+)', 'item_attrs': {'initial': True, 'custom1': ''}}, - 'totalduration': {'read': True, 'write': False, 'read_cmd': 'info total duration ?', 'item_type': 'num', 'dev_datatype': 'str', 'reply_pattern': r'info total duration ([0-9.]*)', 'item_attrs': {'item_template': 'duration', 'initial': True, 'custom1': ''}}, - 'totalartists': {'read': True, 'write': False, 'read_cmd': 'info total artists ?', 'item_type': 'num', 'dev_datatype': 'str', 'reply_pattern': r'info total artists (\d+)', 'item_attrs': {'initial': True, 'custom1': ''}}, - 'totalalbums': {'read': True, 'write': False, 'read_cmd': 'info total albums ?', 'item_type': 'num', 'dev_datatype': 'str', 'reply_pattern': r'info total albums (\d+)', 'item_attrs': {'initial': True, 'custom1': ''}}, - 'totalsongs': {'read': True, 'write': False, 'read_cmd': 'info total songs ?', 'item_type': 'num', 'dev_datatype': 'str', 'reply_pattern': r'info total songs (\d+)', 'item_attrs': {'initial': True, 'custom1': ''}} + 'totalgenres': {'read': True, 'write': False, 'read_cmd': 'info total genres ?', 'item_type': 'num', 'dev_datatype': 'str', 'reply_pattern': r'^info total genres (\d+)', 'custom_disable': True, 'item_attrs': {'initial': True}}, + 'totalduration': {'read': True, 'write': False, 'read_cmd': 'info total duration ?', 'item_type': 'num', 'dev_datatype': 'str', 'reply_pattern': r'^info total duration ([0-9.]*)', 'custom_disable': True, 'item_attrs': {'item_template': 'duration', 'initial': True}}, + 'totalartists': {'read': True, 'write': False, 'read_cmd': 'info total artists ?', 'item_type': 'num', 'dev_datatype': 'str', 'reply_pattern': r'^info total artists (\d+)', 'custom_disable': True, 'item_attrs': {'initial': True}}, + 'totalalbums': {'read': True, 'write': False, 'read_cmd': 'info total albums ?', 'item_type': 'num', 'dev_datatype': 'str', 'reply_pattern': r'^info total albums (\d+)', 'custom_disable': True, 'item_attrs': {'initial': True}}, + 'totalsongs': {'read': True, 'write': False, 'read_cmd': 'info total songs ?', 'item_type': 'num', 'dev_datatype': 'str', 'reply_pattern': r'^info total songs (\d+)', 'custom_disable': True, 'item_attrs': {'initial': True}} }, 'player': { 'control': { - 'power': {'read': True, 'write': True, 'read_cmd': '{CUSTOM_ATTR1} power ?', 'item_type': 'bool', 'write_cmd': '{CUSTOM_ATTR1} power {RAW_VALUE:01}', 'dev_datatype': 'LMSonoff', 'reply_pattern': [r'{CUSTOM_PATTERN1} (?:prefset server\s)?power (\d)', '{CUSTOM_PATTERN1} status(?:.*)power:([^\s]+)'], 'item_attrs': {'enforce': True}}, - 'playmode': {'read': True, 'write': True, 'read_cmd': '{CUSTOM_ATTR1} mode ?', 'item_type': 'str', 'write_cmd': '{CUSTOM_ATTR1} mode {VALUE}', 'dev_datatype': 'LMSPlayMode', 'cmd_settings': {'valid_list_ci': ['PLAY', 'PAUSE', 'STOP']}, 'reply_pattern': [r'{CUSTOM_PATTERN1} mode {VALID_LIST_CI}', r'{CUSTOM_PATTERN1} playlist (pause \d|stop)', '{CUSTOM_PATTERN1} status(?:.*)mode:([^\s]+)'], 'item_attrs': {'enforce': True}}, - 'playpause': {'read': True, 'write': True, 'item_type': 'bool', 'write_cmd': '{CUSTOM_ATTR1} {VALUE}', 'dev_datatype': 'LMSPlay', 'reply_pattern': [r'{CUSTOM_PATTERN1} (?:playlist\s)?(play|pause)(?:\s3)?$', '{CUSTOM_PATTERN1} pause (0|1)'], 'item_attrs': {'enforce': True}}, - 'stop': {'read': True, 'write': True, 'item_type': 'bool', 'write_cmd': '{CUSTOM_ATTR1} {VALUE}', 'dev_datatype': 'LMSStop', 'reply_pattern': r'{CUSTOM_PATTERN1} (?:playlist\s)?(stop)$', 'item_attrs': {'enforce': True}}, - 'mute': {'read': True, 'write': True, 'read_cmd': '{CUSTOM_ATTR1} mixer muting ?', 'item_type': 'bool', 'write_cmd': '{CUSTOM_ATTR1} mixer muting {RAW_VALUE:01}', 'dev_datatype': 'LMSonoff', 'reply_pattern': r'{CUSTOM_PATTERN1} (?:mixer muting|prefset server mute) (\d)', 'item_attrs': {'initial': True, 'enforce': True}}, - 'volume': {'read': True, 'write': True, 'read_cmd': '{CUSTOM_ATTR1} mixer volume ?', 'item_type': 'num', 'write_cmd': '{CUSTOM_ATTR1} mixer volume {VALUE}', 'dev_datatype': 'str', 'reply_pattern': [r'{CUSTOM_PATTERN1} (?:mixer volume \-?|prefset server volume \-?)(\d{1,3})', '{CUSTOM_PATTERN1} status(?:.*)mixer volume:([^\s]+)']}, + 'power': {'read': True, 'write': True, 'read_cmd': '{CUSTOM_ATTR1} power ?', 'item_type': 'bool', 'write_cmd': '{CUSTOM_ATTR1} power {RAW_VALUE:01}', 'dev_datatype': 'LMSonoff', 'reply_pattern': [r'^{CUSTOM_PATTERN1} (?:prefset server\s)?power (\d)$', r'^{CUSTOM_PATTERN1} status(?:.*)power:([^\s]+)$'], 'item_attrs': {'initial': True, 'enforce': True}}, + 'playmode': {'read': True, 'write': True, 'read_cmd': '{CUSTOM_ATTR1} mode ?', 'item_type': 'str', 'write_cmd': '{CUSTOM_ATTR1} mode {VALUE}', 'dev_datatype': 'LMSPlayMode', 'cmd_settings': {'valid_list_ci': ['PLAY', 'PAUSE', 'STOP']}, 'reply_pattern': [r'^{CUSTOM_PATTERN1} mode {VALID_LIST_CI}$', r'^{CUSTOM_PATTERN1} playlist (pause \d|stop)$', r'^{CUSTOM_PATTERN1} status(?:.*)mode:([^\s]+)$'], 'item_attrs': {'enforce': True}}, + 'playpause': {'read': True, 'write': True, 'item_type': 'bool', 'write_cmd': '{CUSTOM_ATTR1} {VALUE}', 'dev_datatype': 'LMSPlay', 'reply_pattern': [r'^{CUSTOM_PATTERN1} (?:playlist\s)?(play|pause)(?:\s3)?$', r'^{CUSTOM_PATTERN1} pause (0|1)'], 'item_attrs': {'enforce': True}}, + 'stop': {'read': True, 'write': True, 'item_type': 'bool', 'write_cmd': '{CUSTOM_ATTR1} {VALUE}', 'dev_datatype': 'LMSStop', 'reply_pattern': r'^{CUSTOM_PATTERN1} (?:playlist\s)?(stop)$', 'item_attrs': {'enforce': True}}, + 'mute': {'read': True, 'write': True, 'read_cmd': '{CUSTOM_ATTR1} mixer muting ?', 'item_type': 'bool', 'write_cmd': '{CUSTOM_ATTR1} mixer muting {RAW_VALUE:01}', 'dev_datatype': 'LMSonoff', 'reply_pattern': [r'^{CUSTOM_PATTERN1} mixer muting$', r'^{CUSTOM_PATTERN1} (?:mixer muting|prefset server mute) (\d)'], 'item_attrs': {'initial': True, 'enforce': True}}, + 'volume': {'read': True, 'write': True, 'read_cmd': '{CUSTOM_ATTR1} mixer volume ?', 'item_type': 'num', 'write_cmd': '{CUSTOM_ATTR1} mixer volume {VALUE}', 'dev_datatype': 'str', 'reply_pattern': [r'^{CUSTOM_PATTERN1} (?:mixer volume |prefset server volume )(\-?\d{1,3})', r'^{CUSTOM_PATTERN1} status(?:.*)mixer volume:([^\s]+)']}, 'volume_fading': {'read': False, 'write': True, 'item_type': 'num', 'write_cmd': '{CUSTOM_ATTR1} mixer volume {VALUE}', 'dev_datatype': 'str', 'item_attrs': {'item_template': 'volume_fading'}}, 'volume_low': {'read': False, 'write': True, 'item_type': 'num', 'write_cmd': '{CUSTOM_ATTR1} mixer volume {VALUE}', 'dev_datatype': 'str', 'item_attrs': {'attributes': {'cache': True, 'enforce_updates': True, 'initial_value': 60}}}, 'volume_high': {'read': False, 'write': True, 'item_type': 'num', 'write_cmd': '{CUSTOM_ATTR1} mixer volume {VALUE}', 'dev_datatype': 'str', 'item_attrs': {'attributes': {'cache': True, 'enforce_updates': True, 'initial_value': 80}}}, 'volumeup': {'read': False, 'write': True, 'item_type': 'num', 'write_cmd': '{CUSTOM_ATTR1} mixer volume +{VALUE}', 'dev_datatype': 'str', 'item_attrs': {'attributes': {'cache': True, 'enforce_updates': True, 'initial_value': 1}}}, 'volumedown': {'read': False, 'write': True, 'item_type': 'num', 'write_cmd': '{CUSTOM_ATTR1} mixer volume -{VALUE}', 'dev_datatype': 'str', 'item_attrs': {'attributes': {'cache': True, 'enforce_updates': True, 'initial_value': 1}}}, - 'set_alarm': {'read': True, 'write': True, 'item_type': 'str', 'write_cmd': '{CUSTOM_ATTR1} alarm {VALUE}', 'dev_datatype': 'str', 'reply_pattern': '{CUSTOM_PATTERN1} alarm (.*)'}, - 'alarms': {'read': True, 'write': False, 'item_type': 'dict', 'read_cmd': '{CUSTOM_ATTR1} alarms 0 100 all', 'dev_datatype': 'LMSAlarms', 'reply_pattern': r'{CUSTOM_PATTERN1} alarms 0 100 all fade:\d+ count:\d+ (.*)', 'item_attrs': {'initial': True, 'read_groups': [{'name': 'player.control.alarms', 'trigger': 'query'}]}}, - 'sync': {'read': True, 'write': True, 'read_cmd': '{CUSTOM_ATTR1} sync ?', 'write_cmd': '{CUSTOM_ATTR1} sync {VALUE}', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': '{CUSTOM_PATTERN1} sync (.*)', 'item_attrs': {'initial': True}}, + 'set_alarm': {'read': True, 'write': True, 'item_type': 'str', 'write_cmd': '{CUSTOM_ATTR1} alarm {VALUE}', 'dev_datatype': 'str', 'reply_pattern': r'^{CUSTOM_PATTERN1} alarm (.*)', 'item_attrs': {'attributes': {'on_change': '..alarms.query = True'}}}, + 'alarms': {'read': True, 'write': False, 'item_type': 'dict', 'read_cmd': '{CUSTOM_ATTR1} alarms 0 100 all', 'dev_datatype': 'LMSAlarms', 'reply_pattern': r'^{CUSTOM_PATTERN1} alarms 0 100 all fade:\d+ count:\d+\s?(.*)?', 'item_attrs': {'initial': True, 'read_groups': [{'name': 'player.control.alarms', 'trigger': 'query'}]}}, + 'sync': {'read': True, 'write': True, 'read_cmd': '{CUSTOM_ATTR1} sync ?', 'write_cmd': '{CUSTOM_ATTR1} sync {VALUE}', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': r'^{CUSTOM_PATTERN1} sync (.*)', 'item_attrs': {'initial': True}}, 'unsync': {'read': False, 'write': True, 'write_cmd': '{CUSTOM_ATTR1} sync -', 'item_type': 'bool', 'dev_datatype': 'LMSonoff', 'item_attrs': {'attributes': {'autotimer': '1s = 0'}}}, - 'display': {'read': True, 'write': True, 'read_cmd': '{CUSTOM_ATTR1} display ? ?', 'item_type': 'str', 'write_cmd': '{CUSTOM_ATTR1} display {VALUE}', 'dev_datatype': 'str', 'reply_pattern': r'{CUSTOM_PATTERN1} display\s?(.*)', 'item_attrs': {'initial': True}}, - 'connect': {'read': True, 'write': True, 'item_type': 'str', 'write_cmd': '{CUSTOM_ATTR1} connect {VALUE}', 'dev_datatype': 'str', 'reply_pattern': '{CUSTOM_PATTERN1} connect (.*)', 'item_attrs': {'attributes': {'remark': 'ip|www.mysqueezebox.com|www.test.mysqueezebox.com'}}}, - 'disconnect': {'read': True, 'write': True, 'item_type': 'str', 'write_cmd': 'disconnect {CUSTOM_ATTR1} {VALUE}', 'dev_datatype': 'str', 'reply_pattern': 'disconnect {CUSTOM_PATTERN1} (.*)', 'item_attrs': {'attributes': {'remark': 'ip|www.mysqueezebox.com|www.test.mysqueezebox.com'}}}, - 'time': {'read': True, 'write': True, 'read_cmd': '{CUSTOM_ATTR1} time ?', 'write_cmd': '{CUSTOM_ATTR1} time {VALUE}', 'item_type': 'num', 'dev_datatype': 'str', 'reply_pattern': [r'{CUSTOM_PATTERN1} time (\d+(?:\.\d{2})?)', '{CUSTOM_PATTERN1} status(?:.*)time:([^\s]+)'], 'item_attrs': {'item_template': 'time', 'enforce': True, 'read_groups': [{'name': 'player.control.time_poll', 'trigger': 'poll'}]}}, - 'forward': {'read': True, 'write': True, 'write_cmd': '{CUSTOM_ATTR1} time +{VALUE}', 'item_type': 'num', 'dev_datatype': 'str', 'reply_pattern': r'{CUSTOM_PATTERN1} time \+(\d+(?:\.\d{2})?)', 'item_attrs': {'enforce': True, 'attributes': {'initial_value': 10}}}, - 'rewind': {'read': True, 'write': True, 'write_cmd': '{CUSTOM_ATTR1} time -{VALUE}', 'item_type': 'num', 'dev_datatype': 'str', 'reply_pattern': r'{CUSTOM_PATTERN1} time \-(\d+(?:\.\d{2})?)', 'item_attrs': {'enforce': True, 'attributes': {'initial_value': 10}}}, - 'playsong': {'read': False, 'write': True, 'write_cmd': '{CUSTOM_ATTR1} playlist play {VALUE}', 'item_type': 'str', 'dev_datatype': 'str', 'item_attrs': {'attributes': {'remark': 'song URL, playlist or directory'}}}, - 'sleep': {'read': True, 'write': True, 'read_cmd': '{CUSTOM_ATTR1} sleep ?', 'write_cmd': '{CUSTOM_ATTR1} sleep {VALUE}', 'item_type': 'num', 'dev_datatype': 'str', 'reply_pattern': '{CUSTOM_PATTERN1} sleep (.*[^?])', 'item_attrs': {'initial': True}} + 'display': {'read': True, 'write': True, 'read_cmd': '{CUSTOM_ATTR1} display ? ?', 'item_type': 'str', 'write_cmd': '{CUSTOM_ATTR1} display {VALUE}', 'dev_datatype': 'str', 'reply_pattern': r'^{CUSTOM_PATTERN1} display\s?(.*)', 'item_attrs': {'initial': True}}, + 'connect': {'read': True, 'write': True, 'item_type': 'str', 'write_cmd': '{CUSTOM_ATTR1} connect {VALUE}', 'dev_datatype': 'str', 'reply_pattern': r'^{CUSTOM_PATTERN1} connect (.*)', 'item_attrs': {'attributes': {'remark': 'ip|www.mysqueezebox.com|www.test.mysqueezebox.com'}}}, + 'disconnect': {'read': True, 'write': True, 'item_type': 'str', 'write_cmd': 'disconnect {CUSTOM_ATTR1} {VALUE}', 'dev_datatype': 'str', 'reply_pattern': r'^disconnect {CUSTOM_PATTERN1} (.*)', 'item_attrs': {'attributes': {'remark': 'ip|www.mysqueezebox.com|www.test.mysqueezebox.com'}}}, + 'time': {'read': True, 'write': True, 'read_cmd': '{CUSTOM_ATTR1} time ?', 'write_cmd': '{CUSTOM_ATTR1} time {VALUE}', 'item_type': 'num', 'dev_datatype': 'str', 'reply_pattern': [r'^{CUSTOM_PATTERN1} time (\d+(?:\.\d{2})?)', r'^{CUSTOM_PATTERN1} status(?:.*)time:([^\s]+)'], 'item_attrs': {'item_template': 'time', 'enforce': True, 'read_groups': [{'name': 'player.control.time_poll', 'trigger': 'poll'}]}}, + 'forward': {'read': True, 'write': True, 'write_cmd': '{CUSTOM_ATTR1} time +{VALUE}', 'item_type': 'num', 'dev_datatype': 'str', 'reply_pattern': r'^{CUSTOM_PATTERN1} time \+(\d+(?:\.\d{2})?)', 'item_attrs': {'enforce': True, 'attributes': {'initial_value': 10}}}, + 'rewind': {'read': True, 'write': True, 'write_cmd': '{CUSTOM_ATTR1} time -{VALUE}', 'item_type': 'num', 'dev_datatype': 'str', 'reply_pattern': r'^{CUSTOM_PATTERN1} time \-(\d+(?:\.\d{2})?)', 'item_attrs': {'enforce': True, 'attributes': {'initial_value': 10}}}, + 'playsong': {'read': False, 'write': True, 'write_cmd': '{CUSTOM_ATTR1} playlist play {VALUE}', 'item_type': 'str', 'dev_datatype': 'LMSConvertSpaces', 'item_attrs': {'attributes': {'remark': 'song URL, playlist or directory'}}}, + 'sleep': {'read': True, 'write': True, 'read_cmd': '{CUSTOM_ATTR1} sleep ?', 'write_cmd': '{CUSTOM_ATTR1} sleep {VALUE}', 'item_type': 'num', 'dev_datatype': 'str', 'reply_pattern': r'^{CUSTOM_PATTERN1} sleep (.*[^?])', 'item_attrs': {'initial': True}} }, 'playlist': { - 'rename': {'read': False, 'write': False, 'item_type': 'str', 'dev_datatype': 'raw', 'reply_pattern': r'{CUSTOM_PATTERN1} playlists rename\s+(.*)'}, - 'repeat': {'read': True, 'write': True, 'read_cmd': '{CUSTOM_ATTR1} playlist repeat ?', 'item_type': 'str', 'write_cmd': '{CUSTOM_ATTR1} playlist repeat {VALUE}', 'dev_datatype': 'str', 'reply_pattern': [r'{CUSTOM_PATTERN1} playlist repeat {LOOKUP}', '{CUSTOM_PATTERN1} status(?:.*)playlist repeat:{LOOKUP}'], 'lookup': 'REPEAT', 'item_attrs': {'attributes': {'remark': '0 = Off, 1 = Song, 2 = Playlist'}, 'lookup_item': True}}, - 'shuffle': {'read': True, 'write': True, 'read_cmd': '{CUSTOM_ATTR1} playlist shuffle ?', 'item_type': 'str', 'write_cmd': '{CUSTOM_ATTR1} playlist shuffle {VALUE}', 'dev_datatype': 'str', 'reply_pattern': [r'{CUSTOM_PATTERN1} playlist shuffle {LOOKUP}', '{CUSTOM_PATTERN1} status(?:.*)playlist shuffle:{LOOKUP}'], 'lookup': 'SHUFFLE', 'item_attrs': {'attributes': {'remark': '0 = Off, 1 = Song, 2 = Album'}, 'lookup_item': True}}, - 'index': {'read': True, 'write': True, 'read_cmd': '{CUSTOM_ATTR1} playlist index ?', 'write_cmd': '{CUSTOM_ATTR1} playlist index {VALUE}', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': [r'{CUSTOM_PATTERN1} playlist (?:index|newsong .*) (\d+)$', '{CUSTOM_PATTERN1} status(?:.*)playlist index:(\d*[^\s]+)', '{CUSTOM_PATTERN1} prefset server currentSong (\d+)$', '{CUSTOM_PATTERN1} playlist jump (\d+)', '{CUSTOM_PATTERN1} play (\d*)'], 'item_attrs': {'initial': True}}, - 'name': {'read': True, 'write': True, 'read_cmd': '{CUSTOM_ATTR1} playlist name ?', 'write_cmd': '{CUSTOM_ATTR1} playlist name {VALUE}', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': [r'{CUSTOM_PATTERN1} playlistcontrol cmd:load playlist_name:(.*) count:(?:\d+)', '{CUSTOM_PATTERN1} playlist name (.*[^?])'], 'item_attrs': {'initial': True}}, - 'id': {'read': True, 'write': True, 'read_cmd': '{CUSTOM_ATTR1} playlist playlistsinfo', 'write_cmd': '{CUSTOM_ATTR1} playlistcontrol cmd:load playlist_id:{VALUE}', 'item_type': 'num', 'dev_datatype': 'str', 'reply_pattern': [r'{CUSTOM_PATTERN1} (?:status - 1 .*|playlist playlistsinfo |playlistcontrol cmd:load playlist_)id:(\d+)', '{CUSTOM_PATTERN1} playlist loadtracks playlist.id=(\d+)\s']}, - 'save': {'read': True, 'write': True, 'write_cmd': '{CUSTOM_ATTR1} playlist save {VALUE}', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': '{CUSTOM_PATTERN1} playlist save (.*)', 'item_attrs': {'enforce': True}}, - 'load': {'read': True, 'write': True, 'write_cmd': '{CUSTOM_ATTR1} playlistcontrol cmd:load playlist_name:{VALUE}', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': ['{CUSTOM_PATTERN1} playlist resume (.*)', '{CUSTOM_PATTERN1} playlist loadtracks playlist.name:(.*)\s'], 'item_attrs': {'enforce': True}}, - 'loadalbum': {'read': True, 'write': True, 'write_cmd': '{CUSTOM_ATTR1} playlist loadalbum {VALUE}', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': '{CUSTOM_PATTERN1} playlist loadalbum (.*)', 'item_attrs': {'enforce': True}}, - 'loadtracks': {'read': True, 'write': True, 'write_cmd': '{CUSTOM_ATTR1} playlist loadtracks {VALUE}', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': '{CUSTOM_PATTERN1} playlist loadtracks (.*)', 'item_attrs': {'enforce': True}}, - 'add': {'read': True, 'write': True, 'write_cmd': '{CUSTOM_ATTR1} playlist add {VALUE}', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': '{CUSTOM_PATTERN1} playlist add (.*)', 'item_attrs': {'enforce': True}}, - 'addalbum': {'read': True, 'write': True, 'write_cmd': '{CUSTOM_ATTR1} playlist addalbum {VALUE}', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': '{CUSTOM_PATTERN1} playlist addalbum (.*)', 'item_attrs': {'enforce': True}}, - 'addtracks': {'read': True, 'write': True, 'write_cmd': '{CUSTOM_ATTR1} playlist addtracks {VALUE}', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': '{CUSTOM_PATTERN1} playlist addtracks (.*)', 'item_attrs': {'enforce': True}}, - 'insertalbum': {'read': True, 'write': True, 'write_cmd': '{CUSTOM_ATTR1} playlist insertalbum {VALUE}', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': '{CUSTOM_PATTERN1} playlist insertalbum (.*)', 'item_attrs': {'enforce': True}}, - 'inserttracks': {'read': True, 'write': True, 'write_cmd': '{CUSTOM_ATTR1} playlist insert {VALUE}', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': '{CUSTOM_PATTERN1} playlist insert (.*)', 'item_attrs': {'enforce': True}}, - 'tracks': {'read': True, 'write': False, 'read_cmd': '{CUSTOM_ATTR1} playlist tracks ?', 'item_type': 'num', 'dev_datatype': 'str', 'reply_pattern': [r'{CUSTOM_PATTERN1} playlistcontrol cmd:load .* count:(\d+)', '{CUSTOM_PATTERN1} playlist_tracks (\d+[^?])', '{CUSTOM_PATTERN1} status(?:.*)playlist tracks:(\d*[^\s]+)']}, - 'clear': {'read': True, 'write': True, 'write_cmd': '{CUSTOM_ATTR1} playlist clear', 'item_type': 'bool', 'dev_datatype': 'str', 'reply_pattern': '{CUSTOM_PATTERN1} playlist clear$', 'item_attrs': {'enforce': True, 'attributes': {'eval': 'True if value else None'}}}, - 'delete': {'read': True, 'write': True, 'write_cmd': '{CUSTOM_ATTR1} playlist delete {VALUE}', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': '{CUSTOM_PATTERN1} playlist delete (.*)', 'item_attrs': {'enforce': True}}, - 'deleteitem': {'read': True, 'write': True, 'write_cmd': '{CUSTOM_ATTR1} playlist deleteitem {VALUE}', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': '{CUSTOM_PATTERN1} playlist deleteitem (.*)', 'item_attrs': {'enforce': True}}, - 'deletealbum': {'read': True, 'write': True, 'write_cmd': '{CUSTOM_ATTR1} playlist deletealbum {VALUE}', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': '{CUSTOM_PATTERN1} playlist deletealbum (.*)', 'item_attrs': {'enforce': True}}, - 'preview': {'read': True, 'write': True, 'write_cmd': '{CUSTOM_ATTR1} playlist preview {VALUE}', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': '{CUSTOM_PATTERN1} playlist preview (.*)'}, + 'rename': {'read': False, 'write': True, 'write_cmd': '{CUSTOM_ATTR1} playlists rename {VALUE}', 'item_type': 'str', 'dev_datatype': 'raw', 'reply_pattern': r'^{CUSTOM_PATTERN1} playlists rename\s+(.*)'}, + 'rename_current': {'read': False, 'write': True, 'item_type': 'str', 'write_cmd': '{CUSTOM_ATTR1} playlists rename playlist_id:{CURRENT_LIST_ID} newvalue:{VALUE}', 'dev_datatype': 'str', 'reply_pattern': r'^{CUSTOM_PATTERN1} playlists rename\s+(.*)', 'item_attrs': {'eval': 'f"playlist_id:{sh...id()} newvalue:{value}" if not ":" in value else value'}}, + 'repeat': {'read': True, 'write': True, 'read_cmd': '{CUSTOM_ATTR1} playlist repeat ?', 'item_type': 'str', 'write_cmd': '{CUSTOM_ATTR1} playlist repeat {VALUE}', 'dev_datatype': 'str', 'reply_pattern': [r'^{CUSTOM_PATTERN1} playlist repeat {LOOKUP}$', r'^{CUSTOM_PATTERN1} status(?:.*)playlist repeat:{LOOKUP}$'], 'lookup': 'REPEAT', 'item_attrs': {'initial': True, 'attributes': {'remark': '0 = Off, 1 = Song, 2 = Playlist'}, 'lookup_item': True}}, + 'shuffle': {'read': True, 'write': True, 'read_cmd': '{CUSTOM_ATTR1} playlist shuffle ?', 'item_type': 'str', 'write_cmd': '{CUSTOM_ATTR1} playlist shuffle {VALUE}', 'dev_datatype': 'str', 'reply_pattern': [r'^{CUSTOM_PATTERN1} playlist shuffle {LOOKUP}$', r'^{CUSTOM_PATTERN1} status(?:.*)playlist shuffle:{LOOKUP}$'], 'lookup': 'SHUFFLE', 'item_attrs': {'initial': True, 'attributes': {'remark': '0 = Off, 1 = Song, 2 = Album'}, 'lookup_item': True}}, + 'index': {'read': True, 'write': True, 'read_cmd': '{CUSTOM_ATTR1} playlist index ?', 'write_cmd': '{CUSTOM_ATTR1} playlist index {VALUE}', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': [r'^{CUSTOM_PATTERN1} playlist (?:index|newsong .*) (\d+)$', r'^{CUSTOM_PATTERN1} status(?:.*)playlist index:(\d*[^\s]+)', r'^{CUSTOM_PATTERN1} prefset server currentSong (\d+)$', r'^{CUSTOM_PATTERN1} playlist jump (\d+)', r'^{CUSTOM_PATTERN1} play (\d*)'], 'item_attrs': {'initial': True}}, + 'name': {'read': True, 'write': True, 'read_cmd': '{CUSTOM_ATTR1} playlist name ?', 'write_cmd': '{CUSTOM_ATTR1} playlist name {VALUE}', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': [r'^{CUSTOM_PATTERN1} playlistcontrol cmd:load playlist_name:(.*) count:(?:\d+)$', r'^{CUSTOM_PATTERN1} playlist name (.*[^?])'], 'item_attrs': {'initial': True}}, + 'id': {'read': True, 'write': True, 'read_cmd': '{CUSTOM_ATTR1} playlist playlistsinfo', 'write_cmd': '{CUSTOM_ATTR1} playlistcontrol cmd:load playlist_id:{VALUE}', 'item_type': 'num', 'dev_datatype': 'str', 'reply_pattern': [r'^{CUSTOM_PATTERN1} (?:status - 1 .*|playlist playlistsinfo |playlistcontrol cmd:load playlist_)id:(\d+)', r'^{CUSTOM_PATTERN1} playlist loadtracks playlist.id=(\d+)\s']}, + 'save': {'read': True, 'write': True, 'write_cmd': '{CUSTOM_ATTR1} playlist save {VALUE}', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': r'^{CUSTOM_PATTERN1} playlist save (.*)', 'item_attrs': {'enforce': True}}, + 'load': {'read': True, 'write': True, 'write_cmd': '{CUSTOM_ATTR1} playlistcontrol cmd:load playlist_name:{VALUE}', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': [r'^{CUSTOM_PATTERN1} playlistcontrol cmd:load playlist_name:(.*) count:(?:\d+)', r'^{CUSTOM_PATTERN1} playlist resume (.*)', r'^{CUSTOM_PATTERN1} playlist loadtracks playlist.name:(.*)\s'], 'item_attrs': {'enforce': True}}, + 'loadalbum': {'read': True, 'write': True, 'write_cmd': '{CUSTOM_ATTR1} playlist loadalbum {VALUE}', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': r'^{CUSTOM_PATTERN1} playlist loadalbum (.*)', 'item_attrs': {'enforce': True, 'attributes': {'remark': ' . You can use * for any of the entries. Spaces need to be replaced by %20'}}}, + 'loadtracks': {'read': True, 'write': True, 'write_cmd': '{CUSTOM_ATTR1} playlist loadtracks {VALUE}', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': r'^{CUSTOM_PATTERN1} playlist loadtracks (.*)', 'item_attrs': {'enforce': True}}, + 'add': {'read': True, 'write': True, 'write_cmd': '{CUSTOM_ATTR1} playlist add {VALUE}', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': r'^{CUSTOM_PATTERN1} playlist add (.*)', 'item_attrs': {'enforce': True}}, + 'addalbum': {'read': True, 'write': True, 'write_cmd': '{CUSTOM_ATTR1} playlist addalbum {VALUE}', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': r'^{CUSTOM_PATTERN1} playlist addalbum (.*)', 'item_attrs': {'enforce': True}}, + 'addtracks': {'read': True, 'write': True, 'write_cmd': '{CUSTOM_ATTR1} playlist addtracks {VALUE}', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': r'^{CUSTOM_PATTERN1} playlist addtracks (.*)', 'item_attrs': {'enforce': True}}, + 'insertalbum': {'read': True, 'write': True, 'write_cmd': '{CUSTOM_ATTR1} playlist insertalbum {VALUE}', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': r'^{CUSTOM_PATTERN1} playlist insertalbum (.*)', 'item_attrs': {'enforce': True}}, + 'inserttracks': {'read': True, 'write': True, 'write_cmd': '{CUSTOM_ATTR1} playlist insert {VALUE}', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': r'^{CUSTOM_PATTERN1} playlist insert (.*)', 'item_attrs': {'enforce': True}}, + 'tracks': {'read': True, 'write': False, 'read_cmd': '{CUSTOM_ATTR1} playlist tracks ?', 'item_type': 'num', 'dev_datatype': 'str', 'reply_pattern': [r'^{CUSTOM_PATTERN1} playlistcontrol cmd:load .* count:(\d+)', r'^{CUSTOM_PATTERN1} playlist_tracks (\d+[^?])', r'^{CUSTOM_PATTERN1} status(?:.*)playlist tracks:(\d*[^\s]+)']}, + 'clear': {'read': True, 'write': True, 'write_cmd': '{CUSTOM_ATTR1} playlist clear', 'item_type': 'bool', 'dev_datatype': 'str', 'reply_pattern': r'^{CUSTOM_PATTERN1} playlist clear$', 'item_attrs': {'enforce': True, 'attributes': {'autotimer': '1s = 0', 'remark': 'Might go berserk, use with care!'}}}, + 'delete': {'read': True, 'write': True, 'write_cmd': '{CUSTOM_ATTR1} playlist delete {VALUE}', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': r'^{CUSTOM_PATTERN1} playlist delete (.*)', 'item_attrs': {'enforce': True}}, + 'deleteitem': {'read': True, 'write': True, 'write_cmd': '{CUSTOM_ATTR1} playlist deleteitem {VALUE}', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': r'^{CUSTOM_PATTERN1} playlist deleteitem (.*)', 'item_attrs': {'enforce': True}}, + 'deletealbum': {'read': True, 'write': True, 'write_cmd': '{CUSTOM_ATTR1} playlist deletealbum {VALUE}', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': r'^{CUSTOM_PATTERN1} playlist deletealbum (.*)', 'item_attrs': {'enforce': True}}, + 'preview': {'read': True, 'write': True, 'write_cmd': '{CUSTOM_ATTR1} playlist preview {VALUE}', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': r'^{CUSTOM_PATTERN1} playlist preview (.*)'}, 'next': {'read': False, 'write': True, 'write_cmd': '{CUSTOM_ATTR1} playlist index +{VALUE}', 'item_type': 'num', 'dev_datatype': 'str', 'item_attrs': {'enforce': True, 'attributes': {'initial_value': 1}}}, 'previous': {'read': False, 'write': True, 'write_cmd': '{CUSTOM_ATTR1} playlist index -{VALUE}', 'item_type': 'num', 'dev_datatype': 'str', 'item_attrs': {'enforce': True, 'attributes': {'initial_value': 1}}}, 'customskip': {'read': False, 'write': True, 'item_type': 'str', 'write_cmd': '{CUSTOM_ATTR1} customskip setfilter filter{VALUE}.cs.xml', 'dev_datatype': 'str', 'item_attrs': {'attributes': {'cache': True}}} }, 'info': { 'playlists': { - 'count': {'read': True, 'write': False, 'read_cmd': '{CUSTOM_ATTR1} playlists', 'item_type': 'num', 'dev_datatype': 'raw', 'reply_pattern': r'{CUSTOM_PATTERN1} playlists\s+count:(\d+)', 'item_attrs': {'initial': True}}, - 'names': {'read': True, 'write': False, 'read_cmd': '{CUSTOM_ATTR1} playlists name', 'item_type': 'dict', 'dev_datatype': 'LMSPlaylists', 'reply_pattern': r'{CUSTOM_PATTERN1} playlists name\s+(.*)\s+count:(?:\d+)', 'item_attrs': {'initial': True}}, - + 'count': {'read': True, 'write': False, 'read_cmd': '{CUSTOM_ATTR1} playlists', 'item_type': 'num', 'dev_datatype': 'raw', 'reply_pattern': r'^{CUSTOM_PATTERN1} playlists\s+count:(\d+)', 'item_attrs': {'initial': True}}, + 'names': {'read': True, 'write': False, 'read_cmd': '{CUSTOM_ATTR1} playlists name', 'item_type': 'dict', 'dev_datatype': 'LMSPlaylists', 'reply_pattern': r'^{CUSTOM_PATTERN1} playlists name\s+(.*)\s+count:(?:\d+)', 'item_attrs': {'initial': True}} }, - 'status': {'read': True, 'write': False, 'read_cmd': '{CUSTOM_ATTR1} status', 'item_type': 'str', 'dev_datatype': 'raw', 'reply_pattern': r'{CUSTOM_PATTERN1} status\s+(.*)', 'item_attrs': {'initial': True}}, - 'connected': {'read': True, 'write': False, 'read_cmd': '{CUSTOM_ATTR1} connected ?', 'item_type': 'bool', 'dev_datatype': 'LMSConnection', 'reply_pattern': [r'{CUSTOM_PATTERN1} (?:connected|client) (\d|disconnect|reconnect)', '{CUSTOM_PATTERN1} status(?:.*)player_connected:([^\s]+)']}, - 'ip': {'read': True, 'write': False, 'read_cmd': '{CUSTOM_ATTR1} ip ?', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': ['{CUSTOM_PATTERN1} ip (.*)', '{CUSTOM_PATTERN1} status(?:.*)player_ip:([^:\s]+)']}, - 'name': {'read': True, 'write': False, 'read_cmd': '{CUSTOM_ATTR1} name ?', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': ['{CUSTOM_PATTERN1} name (.*)', '{CUSTOM_PATTERN1} status(?:.*)player_name:([^\s]+)']}, - 'syncgroups': {'read': True, 'write': False, 'read_cmd': '{CUSTOM_ATTR1} syncgroups ?', 'item_type': 'num', 'dev_datatype': 'str', 'reply_pattern': r'{CUSTOM_PATTERN1} syncgroups (\d+)', 'item_attrs': {'initial': True}}, - 'signalstrength': {'read': True, 'write': False, 'read_cmd': '{CUSTOM_ATTR1} signalstrength ?', 'item_type': 'num', 'dev_datatype': 'str', 'reply_pattern': ['{CUSTOM_PATTERN1} signalstrength (\d+)', '{CUSTOM_PATTERN1} status(?:.*)signalstrength:([^\s]+)']}, - 'genre': {'read': True, 'write': False, 'read_cmd': '{CUSTOM_ATTR1} genre ?', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': '{CUSTOM_PATTERN1} genre (.*)'}, - 'artist': {'read': True, 'write': False, 'read_cmd': '{CUSTOM_ATTR1} artist ?', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': '{CUSTOM_PATTERN1} artist (.*)'}, - 'album': {'read': True, 'write': False, 'read_cmd': '{CUSTOM_ATTR1} album ?', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': '{CUSTOM_PATTERN1} album (.*)', 'item_attrs': {'initial': True}}, - 'title': {'read': True, 'write': False, 'read_cmd': '{CUSTOM_ATTR1} current_title ?', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': r'{CUSTOM_PATTERN1} (?:current_title|playlist newsong) (.*?)(?:\s\d+)?$', 'item_attrs': {'initial': True}}, - 'path': {'read': True, 'write': False, 'read_cmd': '{CUSTOM_ATTR1} path ?', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': ['{CUSTOM_PATTERN1} path (.*)', '{CUSTOM_PATTERN1} playlist open (.*)', '{CUSTOM_PATTERN1} playlist play (.*)']}, - 'duration': {'read': True, 'write': False, 'read_cmd': '{CUSTOM_ATTR1} duration ?', 'item_type': 'num', 'dev_datatype': 'str', 'reply_pattern': r'{CUSTOM_PATTERN1} duration (\d+)'}, - 'trackstat': {'read': True, 'write': False, 'item_type': 'str', 'dev_datatype': 'raw', 'reply_pattern': r'{CUSTOM_PATTERN1} trackstat changedstatistic (.*)'}, - 'albumarturl': {'read': True, 'write': False, 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': '(http://.*)', 'item_attrs': {'attributes': {'remark': 'This item gets automatically defined and overwritten based on (web_)host and web_port'}}} + 'status': {'read': True, 'write': False, 'read_cmd': '{CUSTOM_ATTR1} status', 'item_type': 'str', 'dev_datatype': 'raw', 'reply_pattern': r'^{CUSTOM_PATTERN1} status\s+(.*)', 'item_attrs': {'initial': True}}, + 'connected': {'read': True, 'write': False, 'read_cmd': '{CUSTOM_ATTR1} connected ?', 'item_type': 'bool', 'dev_datatype': 'LMSConnection', 'reply_pattern': [r'^{CUSTOM_PATTERN1} (?:connected|client) (\d|disconnect|reconnect)', r'^{CUSTOM_PATTERN1} status(?:.*)player_connected:([^\s]+)']}, + 'ip': {'read': True, 'write': False, 'read_cmd': '{CUSTOM_ATTR1} ip ?', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': [r'^{CUSTOM_PATTERN1} ip (.*)', r'^{CUSTOM_PATTERN1} status(?:.*)player_ip:([^:\s]+)']}, + 'name': {'read': True, 'write': False, 'read_cmd': '{CUSTOM_ATTR1} name ?', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': [r'^{CUSTOM_PATTERN1} name (.*)', r'^{CUSTOM_PATTERN1} status(?:.*)player_name:([^\s]+)']}, + 'signalstrength': {'read': True, 'write': False, 'read_cmd': '{CUSTOM_ATTR1} signalstrength ?', 'item_type': 'num', 'dev_datatype': 'str', 'reply_pattern': [r'^{CUSTOM_PATTERN1} signalstrength (\d+)', r'^{CUSTOM_PATTERN1} status(?:.*)signalstrength:([^\s]+)']}, + 'genre': {'read': True, 'write': False, 'read_cmd': '{CUSTOM_ATTR1} genre ?', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': r'^{CUSTOM_PATTERN1} genre (.*)'}, + 'artist': {'read': True, 'write': False, 'read_cmd': '{CUSTOM_ATTR1} artist ?', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': r'^{CUSTOM_PATTERN1} artist (.*)'}, + 'album': {'read': True, 'write': False, 'read_cmd': '{CUSTOM_ATTR1} album ?', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': r'^{CUSTOM_PATTERN1} album (.*)', 'item_attrs': {'initial': True}}, + 'title': {'read': True, 'write': False, 'read_cmd': '{CUSTOM_ATTR1} current_title ?', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': r'^{CUSTOM_PATTERN1} (?:current_title|playlist newsong) (.*?)(?:\s\d+)?$', 'item_attrs': {'initial': True}}, + 'path': {'read': True, 'write': False, 'read_cmd': '{CUSTOM_ATTR1} path ?', 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': [r'^{CUSTOM_PATTERN1} path (.*)', r'^{CUSTOM_PATTERN1} playlist open (.*)', r'^{CUSTOM_PATTERN1} playlist play (.*)']}, + 'duration': {'read': True, 'write': False, 'read_cmd': '{CUSTOM_ATTR1} duration ?', 'item_type': 'num', 'dev_datatype': 'str', 'reply_pattern': r'^{CUSTOM_PATTERN1} duration (\d+)'}, + 'trackstat': {'read': True, 'write': False, 'item_type': 'str', 'dev_datatype': 'raw', 'reply_pattern': r'^{CUSTOM_PATTERN1} trackstat changedstatistic (.*)'}, + 'albumarturl': {'read': True, 'write': False, 'item_type': 'str', 'dev_datatype': 'str', 'reply_pattern': '^(https?://.*)', 'item_attrs': {'attributes': {'remark': 'This item gets automatically defined and overwritten based on (web_)host and web_port'}}} } } } @@ -116,6 +119,33 @@ } item_templates = { + 'rescanprogress': { + 'start': + { + 'type': 'num', + 'eval_trigger': '..', + 'eval': '0 if len(sh...()) == 0 else sh...().split("||")[0]' + }, + 'info': + { + 'type': 'str', + 'eval_trigger': '..', + 'eval': '0 if len(sh...()) < 3 else sh...().split("||")[2]' + }, + 'step': + { + 'type': 'num', + 'eval_trigger': '..', + 'eval': '0 if len(sh...()) < 4 else sh...().split("||")[3]' + }, + 'totalsteps': + { + 'type': 'num', + 'eval_trigger': '..', + 'eval': '0 if len(sh...()) <5 else sh...().split("||")[4]' + }, + + }, 'duration': { 'duration_format': { diff --git a/lms/datatypes.py b/lms/datatypes.py index 42c10b6a6..ca28bb8c5 100755 --- a/lms/datatypes.py +++ b/lms/datatypes.py @@ -8,7 +8,7 @@ # handle feedback if rescan is running or not class DT_LMSRescan(DT.Datatype): def get_shng_data(self, data, type=None, **kwargs): - return True if data == "1" else False + return False if data in ["0", "done"] else True class DT_LMSWipecache(DT.Datatype): @@ -73,4 +73,10 @@ def get_shng_data(self, data, type=None, **kwargs): class DT_LMSonoff(DT.Datatype): def get_shng_data(self, data, type=None, **kwargs): - return True if data == "1" else False + return True if data == "1" else False if data == "0" else None + +class DT_LMSConvertSpaces(DT.Datatype): + def get_send_data(self, data, type=None, **kwargs): + return data.replace(" ", "%20") + def get_shng_data(self, data, type=None, **kwargs): + return data.replace("%20", " ") diff --git a/lms/plugin.yaml b/lms/plugin.yaml index ae83616c7..dd4d97390 100755 --- a/lms/plugin.yaml +++ b/lms/plugin.yaml @@ -1,17 +1,18 @@ plugin: type: interface + description: - de: Logitech Mediaserver - en: Logitech Mediaserver + de: Logitech Mediaserver + en: Logitech Mediaserver maintainer: OnkelAndy tester: Morg state: develop keywords: iot device logitechmediaserver lms sdp av - version: '1.5.3' + version: '1.6.0' sh_minversion: '1.9.5' py_minversion: '3.7' - sdp_minversion: '1.0.3' + sdp_minversion: '1.0.4' multi_instance: false restartable: true classname: lms @@ -139,12 +140,12 @@ parameters: en: number of sending retries description_long: - de: | + de: |4 Anzahl Sendeversuche\n Kommt keine (passende) Antwort nach dem Senden eines Commands zurück, wird das Kommando nochmals gesendet, sofern der Wert über 0 liegt. - en: | + en: |4 number of sending retries\n If no (suiting) answer is received after sending a command the command is resent as long as this @@ -160,12 +161,12 @@ parameters: en: wait time between sending retry rounds description_long: - de: | + de: |4 Pause zwischen Durchgängen von Sendeversuchen\n Sind Send Retries aktiv, wird ein Scheduler erstellt, der im angegebenen Sekundentakt Kommandos erneut sendet, zu denen keine (passenden) Antworten erhalten wurden. - en: | + en: |4 wait time between sending retry rounds\n If send retries are active, a scheduler gets added that resends commands in the given cycle value (in seconds) @@ -246,7 +247,7 @@ item_attributes: en: The lookup table with the given name will be assigned to the item in dict or list format once on startup. description_long: - de: |- + de: |4- Der Inhalt der Lookup-Tabelle mit dem angegebenen Namen wird beim Start einmalig als dict oder list in das Item geschrieben. @@ -256,7 +257,7 @@ item_attributes: - rev liefert die Tabelle SmartHomeNG -> Gerät - rci liefert die Tabelle SmarthomeNG -> Gerät in Kleinbuchstaben - list liefert die Liste der Namen für SmartHomeNG (z.B. für Auswahllisten in der Visu) - en: |- + en: |4- The lookup table with the given name will be assigned to the item in dict or list format once on startup. @@ -288,7 +289,6 @@ item_structs: sqb_command@instance: server.listenmode sqb_read@instance: true sqb_write@instance: true - sqb_custom1@instance: '' playercount: type: num @@ -298,7 +298,6 @@ item_structs: sqb_read_group@instance: - server sqb_read_initial@instance: true - sqb_custom1@instance: '' favoritescount: type: num @@ -308,7 +307,32 @@ item_structs: sqb_read_group@instance: - server sqb_read_initial@instance: true - sqb_custom1@instance: '' + + syncgroups: + + read: + type: bool + enforce_updates: true + sqb_read_group_trigger@instance: server.syncgroups + + members: + type: str + sqb_command@instance: server.syncgroups.members + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: + - server + - server.syncgroups + sqb_read_initial@instance: true + + names: + type: str + sqb_command@instance: server.syncgroups.names + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: + - server + - server.syncgroups database: @@ -329,7 +353,6 @@ item_structs: sqb_command@instance: database.rescan.start sqb_read@instance: false sqb_write@instance: true - sqb_custom1@instance: '' remark: playlists|onlinelibrary|external|full|full file://some/path running: @@ -342,14 +365,32 @@ item_structs: - database.rescan sqb_read_initial@instance: true sqb_read_cycle@instance: '120' - sqb_custom1@instance: '' progress: type: str sqb_command@instance: database.rescan.progress sqb_read@instance: true sqb_write@instance: false - sqb_custom1@instance: '' + + start: + type: num + eval_trigger: '..' + eval: 0 if len(sh...()) == 0 else sh...().split("||")[0] + + info: + type: str + eval_trigger: '..' + eval: 0 if len(sh...()) < 3 else sh...().split("||")[2] + + step: + type: num + eval_trigger: '..' + eval: 0 if len(sh...()) < 4 else sh...().split("||")[3] + + totalsteps: + type: num + eval_trigger: '..' + eval: 0 if len(sh...()) <5 else sh...().split("||")[4] runningtime: type: str @@ -359,28 +400,24 @@ item_structs: sqb_read_group@instance: - database - database.rescan - sqb_custom1@instance: '' fail: type: str sqb_command@instance: database.rescan.fail sqb_read@instance: true sqb_write@instance: false - sqb_custom1@instance: '' abortscan: type: bool sqb_command@instance: database.rescan.abortscan sqb_read@instance: true sqb_write@instance: true - sqb_custom1@instance: '' wipecache: type: bool sqb_command@instance: database.rescan.wipecache sqb_read@instance: true sqb_write@instance: true - sqb_custom1@instance: '' totalgenres: type: num @@ -390,7 +427,6 @@ item_structs: sqb_read_group@instance: - database sqb_read_initial@instance: true - sqb_custom1@instance: '' totalduration: type: num @@ -400,7 +436,6 @@ item_structs: sqb_read_group@instance: - database sqb_read_initial@instance: true - sqb_custom1@instance: '' duration_format: type: str @@ -415,7 +450,6 @@ item_structs: sqb_read_group@instance: - database sqb_read_initial@instance: true - sqb_custom1@instance: '' totalalbums: type: num @@ -425,7 +459,6 @@ item_structs: sqb_read_group@instance: - database sqb_read_initial@instance: true - sqb_custom1@instance: '' totalsongs: type: num @@ -435,7 +468,6 @@ item_structs: sqb_read_group@instance: - database sqb_read_initial@instance: true - sqb_custom1@instance: '' player: @@ -460,6 +492,7 @@ item_structs: - player - player.control enforce_updates: true + sqb_read_initial@instance: true playmode: type: str @@ -557,6 +590,7 @@ item_structs: sqb_command@instance: player.control.set_alarm sqb_read@instance: true sqb_write@instance: true + on_change: ..alarms.query = True alarms: type: dict @@ -677,7 +711,13 @@ item_structs: type: str sqb_command@instance: player.playlist.rename sqb_read@instance: false - sqb_write@instance: false + sqb_write@instance: true + + rename_current: + type: str + sqb_command@instance: player.playlist.rename_current + sqb_read@instance: false + sqb_write@instance: true repeat: type: str @@ -687,6 +727,7 @@ item_structs: sqb_read_group@instance: - player - player.playlist + sqb_read_initial@instance: true remark: 0 = Off, 1 = Song, 2 = Playlist lookup: @@ -701,6 +742,7 @@ item_structs: sqb_read_group@instance: - player - player.playlist + sqb_read_initial@instance: true remark: 0 = Off, 1 = Song, 2 = Album lookup: @@ -756,6 +798,7 @@ item_structs: sqb_read@instance: true sqb_write@instance: true enforce_updates: true + remark: . You can use * for any of the entries. Spaces need to be replaced by %20 loadtracks: type: str @@ -814,7 +857,7 @@ item_structs: sqb_read@instance: true sqb_write@instance: true enforce_updates: true - eval: True if value else None + autotimer: 1s = 0 delete: type: str @@ -939,16 +982,6 @@ item_structs: - player - player.info - syncgroups: - type: num - sqb_command@instance: player.info.syncgroups - sqb_read@instance: true - sqb_write@instance: false - sqb_read_group@instance: - - player - - player.info - sqb_read_initial@instance: true - signalstrength: type: num sqb_command@instance: player.info.signalstrength @@ -1046,7 +1079,6 @@ item_structs: sqb_command@instance: server.listenmode sqb_read@instance: true sqb_write@instance: true - sqb_custom1@instance: '' playercount: type: num @@ -1057,7 +1089,6 @@ item_structs: - ALL - ALL.server sqb_read_initial@instance: true - sqb_custom1@instance: '' favoritescount: type: num @@ -1068,7 +1099,34 @@ item_structs: - ALL - ALL.server sqb_read_initial@instance: true - sqb_custom1@instance: '' + + syncgroups: + + read: + type: bool + enforce_updates: true + sqb_read_group_trigger@instance: ALL.server.syncgroups + + members: + type: str + sqb_command@instance: server.syncgroups.members + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: + - ALL + - ALL.server + - ALL.server.syncgroups + sqb_read_initial@instance: true + + names: + type: str + sqb_command@instance: server.syncgroups.names + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: + - ALL + - ALL.server + - ALL.server.syncgroups database: @@ -1089,7 +1147,6 @@ item_structs: sqb_command@instance: database.rescan.start sqb_read@instance: false sqb_write@instance: true - sqb_custom1@instance: '' remark: playlists|onlinelibrary|external|full|full file://some/path running: @@ -1103,14 +1160,32 @@ item_structs: - ALL.database.rescan sqb_read_initial@instance: true sqb_read_cycle@instance: '120' - sqb_custom1@instance: '' progress: type: str sqb_command@instance: database.rescan.progress sqb_read@instance: true sqb_write@instance: false - sqb_custom1@instance: '' + + start: + type: num + eval_trigger: '..' + eval: 0 if len(sh...()) == 0 else sh...().split("||")[0] + + info: + type: str + eval_trigger: '..' + eval: 0 if len(sh...()) < 3 else sh...().split("||")[2] + + step: + type: num + eval_trigger: '..' + eval: 0 if len(sh...()) < 4 else sh...().split("||")[3] + + totalsteps: + type: num + eval_trigger: '..' + eval: 0 if len(sh...()) <5 else sh...().split("||")[4] runningtime: type: str @@ -1121,28 +1196,24 @@ item_structs: - ALL - ALL.database - ALL.database.rescan - sqb_custom1@instance: '' fail: type: str sqb_command@instance: database.rescan.fail sqb_read@instance: true sqb_write@instance: false - sqb_custom1@instance: '' abortscan: type: bool sqb_command@instance: database.rescan.abortscan sqb_read@instance: true sqb_write@instance: true - sqb_custom1@instance: '' wipecache: type: bool sqb_command@instance: database.rescan.wipecache sqb_read@instance: true sqb_write@instance: true - sqb_custom1@instance: '' totalgenres: type: num @@ -1153,7 +1224,6 @@ item_structs: - ALL - ALL.database sqb_read_initial@instance: true - sqb_custom1@instance: '' totalduration: type: num @@ -1164,7 +1234,6 @@ item_structs: - ALL - ALL.database sqb_read_initial@instance: true - sqb_custom1@instance: '' duration_format: type: str @@ -1180,7 +1249,6 @@ item_structs: - ALL - ALL.database sqb_read_initial@instance: true - sqb_custom1@instance: '' totalalbums: type: num @@ -1191,7 +1259,6 @@ item_structs: - ALL - ALL.database sqb_read_initial@instance: true - sqb_custom1@instance: '' totalsongs: type: num @@ -1202,7 +1269,6 @@ item_structs: - ALL - ALL.database sqb_read_initial@instance: true - sqb_custom1@instance: '' player: @@ -1228,6 +1294,7 @@ item_structs: - ALL.player - ALL.player.control enforce_updates: true + sqb_read_initial@instance: true playmode: type: str @@ -1328,6 +1395,7 @@ item_structs: sqb_command@instance: player.control.set_alarm sqb_read@instance: true sqb_write@instance: true + on_change: ..alarms.query = True alarms: type: dict @@ -1453,7 +1521,13 @@ item_structs: type: str sqb_command@instance: player.playlist.rename sqb_read@instance: false - sqb_write@instance: false + sqb_write@instance: true + + rename_current: + type: str + sqb_command@instance: player.playlist.rename_current + sqb_read@instance: false + sqb_write@instance: true repeat: type: str @@ -1464,6 +1538,7 @@ item_structs: - ALL - ALL.player - ALL.player.playlist + sqb_read_initial@instance: true remark: 0 = Off, 1 = Song, 2 = Playlist lookup: @@ -1479,6 +1554,7 @@ item_structs: - ALL - ALL.player - ALL.player.playlist + sqb_read_initial@instance: true remark: 0 = Off, 1 = Song, 2 = Album lookup: @@ -1537,6 +1613,7 @@ item_structs: sqb_read@instance: true sqb_write@instance: true enforce_updates: true + remark: . You can use * for any of the entries. Spaces need to be replaced by %20 loadtracks: type: str @@ -1596,7 +1673,7 @@ item_structs: sqb_read@instance: true sqb_write@instance: true enforce_updates: true - eval: True if value else None + autotimer: 1s = 0 delete: type: str @@ -1727,17 +1804,6 @@ item_structs: - ALL.player - ALL.player.info - syncgroups: - type: num - sqb_command@instance: player.info.syncgroups - sqb_read@instance: true - sqb_write@instance: false - sqb_read_group@instance: - - ALL - - ALL.player - - ALL.player.info - sqb_read_initial@instance: true - signalstrength: type: num sqb_command@instance: player.info.signalstrength