Skip to content

Commit db9f920

Browse files
authored
Merge pull request #994 from onkelandy/lms
LMS Plugin: major fixes and improvements, restructured commands
2 parents 56a368f + 36a82e3 commit db9f920

File tree

11 files changed

+3525
-457
lines changed

11 files changed

+3525
-457
lines changed

lms/__init__.py

Lines changed: 91 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,13 @@
2020
# You should have received a copy of the GNU General Public License
2121
# along with SmartHomeNG If not, see <http://www.gnu.org/licenses/>.
2222
#########################################################################
23+
from __future__ import annotations
24+
from typing import Any, Tuple
2325

2426
import builtins
2527
import os
2628
import sys
29+
import re
2730

2831
if __name__ == '__main__':
2932
builtins.SDP_standalone = True
@@ -40,7 +43,8 @@ class SmartPluginWebIf():
4043
else:
4144
builtins.SDP_standalone = False
4245

43-
from lib.model.sdp.globals import (CUSTOM_SEP, PLUGIN_ATTR_NET_HOST, PLUGIN_ATTR_RECURSIVE, PLUGIN_ATTR_CMD_CLASS, PLUGIN_ATTR_CONNECTION, PLUGIN_ATTR_CONN_TERMINATOR)
46+
from lib.model.sdp.globals import (CUSTOM_SEP, PLUGIN_ATTR_NET_HOST, PLUGIN_ATTR_RECURSIVE, PLUGIN_ATTR_CMD_CLASS,
47+
PLUGIN_ATTR_CONNECTION, PLUGIN_ATTR_CONN_TERMINATOR)
4448
from lib.model.smartdeviceplugin import SmartDevicePlugin, Standalone
4549
from lib.model.sdp.command import SDPCommandParseStr
4650

@@ -50,7 +54,7 @@ class SmartPluginWebIf():
5054
class lms(SmartDevicePlugin):
5155
""" Device class for Logitech Mediaserver/Squeezebox function. """
5256

53-
PLUGIN_VERSION = '1.6.0'
57+
PLUGIN_VERSION = '2.0.0'
5458

5559
def _set_device_defaults(self):
5660
self.custom_commands = 1
@@ -75,10 +79,17 @@ def _set_device_defaults(self):
7579
self._parameters['web_host'] = host
7680
else:
7781
self._parameters['web_host'] = f'http://{host}'
82+
self._parameters['CURRENT_LIST_ID'] = {}
7883

7984
def on_connect(self, by=None):
80-
self.logger.debug("Activating listen mode after connection.")
85+
self.logger.debug(f"Activating listen mode after connection.")
8186
self.send_command('server.listenmode', True)
87+
self.logger.debug(f"Subscribing all players to playlist changes.")
88+
for player in self._custom_values.get(1):
89+
if player == '-':
90+
continue
91+
else:
92+
self.send_command('player.info.player.status_subscribe' + CUSTOM_SEP + player, True)
8293

8394
def _transform_send_data(self, data=None, **kwargs):
8495
if isinstance(data, dict):
@@ -88,23 +99,79 @@ def _transform_send_data(self, data=None, **kwargs):
8899

89100
def _transform_received_data(self, data):
90101
# fix weird representation of MAC address (%3A = :), etc.
91-
return urllib.parse.unquote_plus(data)
102+
data_temp = data.replace("%20", "PPLACEHOLDERR")
103+
data = urllib.parse.unquote(data_temp)
104+
data = data.replace("PPLACEHOLDERR", "%20")
105+
return data
92106

93-
def _process_additional_data(self, command, data, value, custom, by):
107+
def _process_additional_data(self, command: str, data: Any, value: Any, custom: int, by: str | None = None):
94108

95109
def trigger_read(command):
96110
self.send_command(command + CUSTOM_SEP + custom)
97111

112+
if command == f'server.newclient':
113+
self.logger.debug(f"Got new client connection {command}, re-reading players")
114+
self.send_command('server.players')
115+
116+
if command == f'server.players':
117+
self.logger.debug(f"Got command players {command} data {data} value {value} by {by}")
118+
for player in self._custom_values.get(1):
119+
if player == '-':
120+
continue
121+
elif player in value.keys():
122+
self._dispatch_callback('player.info.player.modelname' + CUSTOM_SEP + player, value[player].get('modelname'), by)
123+
self._dispatch_callback('player.info.player.firmware' + CUSTOM_SEP + player, value[player].get('firmware'), by)
124+
125+
if command == f'database.rescan.running' and value is False:
126+
self.logger.debug(f"Got command rescan not running, {command} data {data} value {value} by {by}")
127+
self._dispatch_callback('database.rescan.progress', "", by)
128+
129+
if command == f'server.playlists.delete':
130+
self.logger.debug(f"Got command delete playlist {command}, re-reading playlists")
131+
self.send_command('server.playlists.available')
132+
133+
if command == f'server.syncgroups.members' and data:
134+
def find_player_index(target, mac_list):
135+
for index, item in enumerate(mac_list):
136+
if re.search(rf'\b{re.escape(target)}\b', item):
137+
return index # Return the index where the match is found
138+
return -1
139+
140+
self.logger.debug(f"Got command syncgroups {command} data {data} value {value} by {by}")
141+
for player in self._custom_values.get(1):
142+
idx = find_player_index(player, value)
143+
if idx >= 0:
144+
synced = value[idx].split(",")
145+
synced.remove(player)
146+
self.logger.debug(f"Updating syncstatus of player {player} to {synced}")
147+
self._dispatch_callback('player.control.sync_status' + CUSTOM_SEP + player, synced, by)
148+
else:
149+
self._dispatch_callback('player.control.sync_status' + CUSTOM_SEP + player, [], by)
150+
self._dispatch_callback('player.control.sync' + CUSTOM_SEP + player, '-', by)
151+
98152
if not custom:
99153
return
100154

101-
if command == f'player.info.playlists.names{CUSTOM_SEP}{custom}':
102-
self.logger.debug(f"Got command playlist names {command} data {data} value {value} custom {custom} by {by}")
103-
trigger_read('player.playlist.id')
104-
trigger_read('player.playlist.name')
155+
if command == f'player.playlist.rename_current{CUSTOM_SEP}{custom}':
156+
self.logger.debug(f"Got command rename_current {command}, re-reading playlists")
157+
self.send_command('server.playlists.available')
158+
159+
if command == f'player.playlist.delete_current{CUSTOM_SEP}{custom}':
160+
self.logger.debug(f"Got command delete_current {command}, re-reading playlists")
161+
self.send_command('server.playlists.available')
162+
163+
if command == f'player.playlist.save{CUSTOM_SEP}{custom}':
164+
self.logger.debug(f"Got command save playlist {command}, re-reading playlists")
165+
self.send_command('server.playlists.available')
166+
167+
if command == f'player.playlist.clear{CUSTOM_SEP}{custom}':
168+
self.logger.debug(f"Got command playlist clear {command}")
169+
trigger_read('player.info.player.status')
170+
171+
if command == f'player.playlist.tracks{CUSTOM_SEP}{custom}':
172+
self.logger.debug(f"Got command playlist tracks, most likely because playlist was changed. Check modified {command}")
173+
trigger_read('player.playlist.modified')
105174

106-
if command == 'playlist.rename':
107-
trigger_read('info.playlists.names')
108175
# set alarm
109176
if command == f'player.control.alarms{CUSTOM_SEP}{custom}':
110177
return
@@ -123,7 +190,7 @@ def trigger_read(command):
123190
self.logger.error(f"Error setting alarm: {e}")
124191

125192
# set album art URL
126-
if command == f'player.info.album{CUSTOM_SEP}{custom}':
193+
if command == f'player.info.currentsong.album{CUSTOM_SEP}{custom}':
127194
self.logger.debug(f"Got command album {command} data {data} value {value} custom {custom} by {by}")
128195
host = self._parameters['web_host']
129196
port = self._parameters['web_port']
@@ -132,7 +199,7 @@ def trigger_read(command):
132199
else:
133200
url = f'{host}:{port}/music/current/cover.jpg?player={custom}'
134201
self.logger.debug(f"Setting albumarturl to {url}")
135-
self._dispatch_callback('player.info.albumarturl' + CUSTOM_SEP + custom, url, by)
202+
self._dispatch_callback('player.info.player.albumarturl' + CUSTOM_SEP + custom, url, by)
136203

137204
# set playlist ID
138205
if command == f'player.playlist.load{CUSTOM_SEP}{custom}':
@@ -142,41 +209,34 @@ def trigger_read(command):
142209

143210
if command == f'player.playlist.id{CUSTOM_SEP}{custom}':
144211
self.logger.debug(f"Got command id {command} data {data} value {value} custom {custom} by {by}")
212+
self._parameters['CURRENT_LIST_ID'][custom] = value
145213
trigger_read('player.playlist.name')
214+
trigger_read('player.playlist.url')
146215

147216
if command == f'player.control.sync{CUSTOM_SEP}{custom}':
148217
self.logger.debug(f"Got command sync {command} data {data} value {value} custom {custom} by {by}")
149-
trigger_read('server.syncgroups.members')
218+
self.send_command('server.syncgroups.members')
150219

151220
# update on new song
152-
if command == f'player.info.title{CUSTOM_SEP}{custom}':
221+
if command == f'player.info.currentsong.title{CUSTOM_SEP}{custom}':
153222
# trigger_read('player.control.playmode')
154223
# trigger_read('player.playlist.index')
155-
trigger_read('player.info.duration')
156-
trigger_read('player.info.album')
157-
trigger_read('player.info.artist')
158-
trigger_read('player.info.genre')
159-
trigger_read('player.info.path')
224+
trigger_read('player.info.currentsong.duration')
225+
trigger_read('player.info.currentsong.album')
226+
trigger_read('player.info.currentsong.artist')
227+
trigger_read('player.info.currentsong.genre')
228+
trigger_read('player.info.currentsong.path')
160229

161230
# update on new song
162231
if command == f'player.control.playpause{CUSTOM_SEP}{custom}' and value:
163232
trigger_read('player.control.playmode')
164-
trigger_read('player.info.duration')
165-
trigger_read('player.info.album')
166-
trigger_read('player.info.artist')
167-
trigger_read('player.info.genre')
168-
trigger_read('player.info.path')
233+
self.read_all_commands('player.info.currentsong' + CUSTOM_SEP + custom)
169234

170235
# update on new song
171236
if command == f'player.playlist.index{CUSTOM_SEP}{custom}':
172237
self.logger.debug(f"Got command index {command} data {data} value {value} custom {custom} by {by}")
173238
trigger_read('player.control.playmode')
174-
trigger_read('player.info.duration')
175-
trigger_read('player.info.album')
176-
trigger_read('player.info.artist')
177-
trigger_read('player.info.genre')
178-
trigger_read('player.info.path')
179-
trigger_read('player.info.title')
239+
self.read_all_commands('player.info.currentsong' + CUSTOM_SEP + custom)
180240

181241
# update current time info
182242
if command in [f'player.control.forward{CUSTOM_SEP}{custom}', f'player.control.rewind{CUSTOM_SEP}{custom}']:

0 commit comments

Comments
 (0)