diff --git a/lms/__init__.py b/lms/__init__.py index d72febd90..0af7f7e9f 100755 --- a/lms/__init__.py +++ b/lms/__init__.py @@ -24,6 +24,7 @@ import builtins import os import sys +import re if __name__ == '__main__': builtins.SDP_standalone = True @@ -105,6 +106,8 @@ def trigger_read(command): if command == f'database.players': self.logger.debug(f"Got command players {command} data {data} value {value} by {by}") for player in value.keys(): + if player == '-': + continue self._dispatch_callback('player.info.modelname' + CUSTOM_SEP + player, value[player].get('modelname'), by) self._dispatch_callback('player.info.firmware' + CUSTOM_SEP + player, value[player].get('firmware'), by) self._dispatch_callback('player.info.players' + CUSTOM_SEP + player, value, by) @@ -114,6 +117,25 @@ def trigger_read(command): for player in self._custom_values.get(1): self._dispatch_callback('player.info.playlists' + CUSTOM_SEP + player, value, by) + if command == f'server.syncgroups.members' and data: + def find_player_index(target, mac_list): + for index, item in enumerate(mac_list): + if re.search(rf'\b{re.escape(target)}\b', item): + return index # Return the index where the match is found + return -1 + + self.logger.debug(f"Got command syncgroups {command} data {data} value {value} by {by}") + for player in self._custom_values.get(1): + idx = find_player_index(player, value) + self.logger.debug(f"Testing player {player} vs value {value} idx {idx}") + if idx >= 0: + synced = value[idx].split(",") + synced.remove(player) + self.logger.debug(f"Updating syncstatus of player {player} to {synced}") + self._dispatch_callback('player.control.syncstatus' + CUSTOM_SEP + player, synced, by) + else: + self._dispatch_callback('player.control.syncstatus' + CUSTOM_SEP + player, [], by) + if not custom: return @@ -162,7 +184,7 @@ def trigger_read(command): 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') + self.send_command('server.syncgroups.members') # update on new song if command == f'player.info.title{CUSTOM_SEP}{custom}': diff --git a/lms/commands.py b/lms/commands.py index fedc118be..9a96acb70 100755 --- a/lms/commands.py +++ b/lms/commands.py @@ -12,8 +12,8 @@ 'playercount': {'read': True, 'write': False, 'read_cmd': 'player count ?', 'item_type': 'num', 'dev_datatype': 'str', 'reply_pattern': [r'^player count (\d+)', r'^players 0 100 count:(\d+) '], 'custom_disabled': 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_disabled': 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_disabled': 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_disabled': True, 'item_attrs': {'initial': False}}, + 'members': {'read': True, 'write': False, 'read_cmd': 'syncgroups ?', 'item_type': 'list', 'dev_datatype': 'LMSSyncmembers', 'reply_pattern': r'^syncgroups\s?(.*)?$', 'custom_disabled': True, 'item_attrs': {'initial': True}}, + 'names': {'read': True, 'write': False, 'read_cmd': 'syncgroups ?', 'item_type': 'list', 'dev_datatype': 'LMSSyncnames', 'reply_pattern': r'^syncgroups\s?(.*)?$', 'custom_disabled': True, 'item_attrs': {'initial': False}}, }, 'playlists': { 'rename': {'read': False, 'write': True, 'write_cmd': 'playlists rename {VALUE}', 'item_type': 'str', 'dev_datatype': 'LMSPlaylistrename', 'reply_pattern': r'^playlists rename\s+(.*)', 'custom_disabled': True, 'item_attrs': {'attributes': {'remark': 'needed value: with a space inbetween'}}}, @@ -54,7 +54,8 @@ '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': 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}}, + '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 (?:.*,)?{LOOKUP}(?:,.*)?', r'^{CUSTOM_PATTERN1} sync (\-)'], 'lookup': 'PLAYERS', 'item_attrs': {'initial': True, 'enforce': True}}, + 'syncstatus': {'read': False, 'write': False, 'item_type': 'list', 'dev_datatype': 'raw'}, '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': r'^{CUSTOM_PATTERN1} connect (.*)', 'item_attrs': {'attributes': {'remark': 'ip|www.mysqueezebox.com|www.test.mysqueezebox.com'}}}, @@ -127,8 +128,8 @@ '1': 'SONG', '2': 'ALBUM' }, - 'TEST': { - '0': 'OFF' + 'PLAYERS': { + '-': 'NONE' } } @@ -138,7 +139,7 @@ 'lu_names': { 'type': 'dict', - 'sqb_lookup@instance': 'TEST#fwd' + 'sqb_lookup@instance': 'PLAYERS#fwd' } }, 'playlists': { diff --git a/lms/datatypes.py b/lms/datatypes.py index ce8084eee..70dafa947 100755 --- a/lms/datatypes.py +++ b/lms/datatypes.py @@ -32,6 +32,28 @@ def get_shng_data(self, data, type=None, **kwargs): return True if data in ["play", "0"] else False +class DT_LMSSyncnames(DT.Datatype): + def get_shng_data(self, data, type=None, **kwargs): + pattern=r"sync_member_names:([^s]+(?: [^s]+)*)(?= sync_members|$)" + return re.findall(pattern, data) + + +class DT_LMSSyncmembers(DT.Datatype): + def get_shng_data(self, data, type=None, **kwargs): + pattern=r"sync_members:([^s]+(?: [^s]+)*)(?= sync_member_names|$)" + return re.findall(pattern, data) + + +class DT_LMSSyncstatus(DT.Datatype): + def get_shng_data(self, data, type=None, **kwargs): + if data in ["-", "?"]: + return [] + elif data: + return data.split(",") + else: + return [] + + class DT_LMSAlarms(DT.Datatype): def get_shng_data(self, data, type=None, **kwargs): dic = {} @@ -90,6 +112,7 @@ def get_shng_data(self, data, type=None, **kwargs): player_id = player_data.pop('playerid', None) if player_id: players_dict[player_id] = player_data + players_dict['-'] = {'ip:': None, 'name': 'NONE', 'model': None, 'firmware': None} return players_dict class DT_LMSPlaylists(DT.Datatype): diff --git a/lms/plugin.yaml b/lms/plugin.yaml index 46c24a334..470caeffd 100755 --- a/lms/plugin.yaml +++ b/lms/plugin.yaml @@ -331,7 +331,7 @@ item_structs: sqb_read_group_trigger@instance: server.syncgroups members: - type: str + type: list sqb_command@instance: server.syncgroups.members sqb_read@instance: true sqb_write@instance: false @@ -341,7 +341,7 @@ item_structs: sqb_read_initial@instance: true names: - type: str + type: list sqb_command@instance: server.syncgroups.names sqb_read@instance: true sqb_write@instance: false @@ -677,8 +677,15 @@ item_structs: sqb_read_group@instance: - player - player.control + enforce_updates: true sqb_read_initial@instance: true + syncstatus: + type: list + sqb_command@instance: player.control.syncstatus + sqb_read@instance: false + sqb_write@instance: false + unsync: type: bool sqb_command@instance: player.control.unsync @@ -1012,7 +1019,7 @@ item_structs: lu_names: type: dict - sqb_lookup@instance: TEST#fwd + sqb_lookup@instance: PLAYERS#fwd playlists: type: dict @@ -1225,7 +1232,7 @@ item_structs: sqb_read_group_trigger@instance: ALL.server.syncgroups members: - type: str + type: list sqb_command@instance: server.syncgroups.members sqb_read@instance: true sqb_write@instance: false @@ -1236,7 +1243,7 @@ item_structs: sqb_read_initial@instance: true names: - type: str + type: list sqb_command@instance: server.syncgroups.names sqb_read@instance: true sqb_write@instance: false @@ -1589,8 +1596,15 @@ item_structs: - ALL - ALL.player - ALL.player.control + enforce_updates: true sqb_read_initial@instance: true + syncstatus: + type: list + sqb_command@instance: player.control.syncstatus + sqb_read@instance: false + sqb_write@instance: false + unsync: type: bool sqb_command@instance: player.control.unsync @@ -1936,7 +1950,7 @@ item_structs: lu_names: type: dict - sqb_lookup@instance: TEST#fwd + sqb_lookup@instance: PLAYERS#fwd playlists: type: dict