Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sonos Plugin: improve dpt3 handling, make plugin restartable #975

Merged
merged 4 commits into from
Dec 8, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 49 additions & 32 deletions sonos/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2994,7 +2994,7 @@ class Sonos(SmartPlugin):
"""
Main class of the Plugin. Does all plugin specific stuff
"""
PLUGIN_VERSION = "1.8.8"
PLUGIN_VERSION = "1.8.9"

def __init__(self, sh):
"""Initializes the plugin."""
Expand All @@ -3007,12 +3007,12 @@ def __init__(self, sh):
self._tts = self.get_parameter_value("tts")
self._snippet_duration_offset = float(self.get_parameter_value("snippet_duration_offset"))
self._discover_cycle = self.get_parameter_value("discover_cycle")
self.webif_pagelength = self.get_parameter_value('webif_pagelength')
local_webservice_path = self.get_parameter_value("local_webservice_path")
local_webservice_path_snippet = self.get_parameter_value("local_webservice_path_snippet")
webservice_ip = self.get_parameter_value("webservice_ip")
webservice_port = self.get_parameter_value("webservice_port")
speaker_ips = self.get_parameter_value("speaker_ips")
self._pause_item_path = self.get_parameter_value('pause_item')
except KeyError as e:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ist keine deiner Änderungen, aber wenn die Parameter in plugin.yaml definiert sind, sind sie immer vorhanden, wenn sie required oder defaults vorgegeben sind.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so ist die aktuelle Definition:

  pause_item:
    type: str
    default: ''
    description:
      de: 'Item, um die Ausführung des Plugins zu steuern'
      en: 'item for controlling plugin execution'

Somit muss das Plugin auch laufen, wenn der parameter nicht gesetzt ist.

Copy link
Contributor Author

@sisamiwe sisamiwe Nov 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Morg42
Schau mal in Zeile 3059. Ich glaube da liegt der Fehler.

Es muss dort sicher heißen:
if self._pause_item_path:

oder man muss in der init noch den self._pause_item = None setzen.

Nach der Methode init wird doch parse_item abgearbeitet. Nur wenn self._pause_item_path gesetzt ist, wir auch self._pause_item gesetzt, wenn nicht gibt es diese Variable nicht. Somit kann die auch bei, ausführen von run nicht gefunden werden und das plugin hängt, oder?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nee. Die Variable gibt es schon, die kommt direkt aus dem SmartPlugin.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Und zu "muss auch laufen" - die Korrekturen in SmartPlugin sind erst im 1.10er branch vorhanden, mit 1.9.x gibt es Fehler.

self.logger.critical(f"Plugin '{self.get_shortname()}': Inconsistent plugin (invalid metadata definition: {e} not defined)")
self._init_complete = False
Expand Down Expand Up @@ -3054,6 +3054,10 @@ def __init__(self, sh):

def run(self):
self.logger.debug("Run method called")

# let the plugin change the state of pause_item
if self._pause_item:
self._pause_item(False, self.get_fullname())

# do initial speaker discovery and set scheduler
self._discover()
Expand All @@ -3063,13 +3067,16 @@ def run(self):
self.alive = True

def stop(self):
self.logger.debug("Stop method called")
self.logger.dbghigh(self.translate("Methode '{method}' aufgerufen", {'method': 'stop()'}))

# let the plugin change the state of pause_item
if self._pause_item:
self._pause_item(True, self.get_fullname())

if self.webservice:
self.webservice.stop()

if self.scheduler_get('sonos_discover_scheduler'):
self.scheduler_remove('sonos_discover_scheduler')
self.scheduler_remove_all()

for uid, speaker in sonos_speaker.items():
speaker.dispose()
Expand All @@ -3084,6 +3091,12 @@ def parse_item(self, item: Items) -> object:
:param item: item to parse
:return: update function or None
"""

if item.property.path == self._pause_item_path:
self.logger.debug(f'pause item {item.property.path} registered')
self._pause_item = item
self.add_item(item, updating=True)
return self.update_item

item_config = dict()

Expand Down Expand Up @@ -3147,22 +3160,22 @@ def parse_item(self, item: Items) -> object:
self.logger.warning("volume_dpt3 item has no volume parent item. Ignoring!")
return

# make sure there is a child helper item
child_helper = None
# make sure there is a child volume helper item
volume_helper_item = None
for child in item.return_children():
if self.has_iattr(child.conf, 'sonos_attrib'):
if self.get_iattr_value(child.conf, 'sonos_attrib').lower() == 'dpt3_helper':
child_helper = child
volume_helper_item = child
break

if child_helper is None:
self.logger.warning("volume_dpt3 item has no helper item. Ignoring!")
if volume_helper_item is None:
self.logger.warning("volume_dpt3 item has no volume helper item. Ignoring!")
return

dpt3_step = self.get_iattr_value(item.conf, 'sonos_dpt3_step')
dpt3_time = self.get_iattr_value(item.conf, 'sonos_dpt3_time')

item_config.update({'volume_item': parent_item, 'helper': child_helper, 'dpt3_step': dpt3_step, 'dpt3_time': dpt3_time})
item_config.update({'volume_item': parent_item, 'helper_item': volume_helper_item, 'dpt3_step': dpt3_step, 'dpt3_time': dpt3_time})
self.add_item(item, config_data_dict=item_config, updating=True)
return self._handle_dpt3

Expand Down Expand Up @@ -3226,36 +3239,31 @@ def play_alert_all_speakers(self, alert_uri, speaker_list=[], alert_volume=20, a
zone.snap.restore(fade=fade_back)

def _handle_dpt3(self, item, caller=None, source=None, dest=None):
if caller != self.get_shortname():
"""Handle relative volumen change via a received relative dim command (dpt3) by making use of internal fadeing"""

if caller != self.get_shortname():
item_config = self.get_item_config(item)
volume_item = item_config['volume_item']
volume_helper = item_config['helper']
volume_helper_item = item_config['helper']
vol_step = item_config['dpt3_step']
vol_time = item_config['dpt3_time']
vol_max = self._resolve_max_volume_command(item)

if vol_max < 0:
vol_max = 100

current_volume = int(volume_item())
if current_volume < 0:
current_volume = 0
if current_volume > 100:
current_volume = 100

volume_helper(current_volume)
vol_max = max(0, self._resolve_max_volume_command(item)) or 100
_current_volume = max(0, min(100, int(volume_item())))
volume_helper_item(_current_volume, self.get_shortname())

if item()[1] == 1:
self.logger.debug(f"Starte relative Lautstärkeänderung.")
if item()[0] == 1:
# up
volume_helper.fade(vol_max, vol_step, vol_time)
self.logger.debug(f"erhöhe Lautstärke mit {vol_step} Stufe(n) pro {vol_time}s")
volume_helper_item.fade(vol_max, vol_step, vol_time)
else:
# down
volume_helper.fade(0 - vol_step, vol_step, vol_time)
self.logger.debug(f"reduziere Lautstärke mit {vol_step} Stufe(n) pro {vol_time}s")
volume_helper_item.fade(0 - vol_step, vol_step, vol_time)
else:
volume_helper(int(volume_helper() + 1))
volume_helper(int(volume_helper() - 1))
self.logger.debug(f"Stoppe relative Lautstärkeänderung.")
volume_helper_item(int(volume_helper_item()), self.get_shortname())

def _check_webservice_ip(self, webservice_ip: str) -> bool:
if not webservice_ip == '' and not webservice_ip == '0.0.0.0':
Expand Down Expand Up @@ -3457,10 +3465,19 @@ def update_item(self, item: Items, caller: object, source: object, dest: object)
:param source: if given it represents the source
:param dest: if given it represents the dest
"""

if self.alive and caller != self.get_fullname():

self.logger.debug(f"update_item called for {item.path()} with value {item()}")
# check for pause item
if item is self._pause_item and caller != self.get_shortname():
Morg42 marked this conversation as resolved.
Show resolved Hide resolved
self.logger.debug(f'pause item changed to {item()}')
if item() and self.alive:
self.stop()
elif not item() and not self.alive:
self.run()
return

# check for sonos item
if self.alive and caller != self.get_fullname():
self.logger.debug(f"update_item called for {item.path()} with value {item()} {caller=}")
item_config = self.get_item_config(item)
command = item_config.get('sonos_send', '').lower()
uid = item_config.get('uid')
Expand Down
11 changes: 9 additions & 2 deletions sonos/plugin.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ plugin:
documentation: https://github.com/smarthomeNG/plugins/blob/master/sonos/README.md
support: https://knx-user-forum.de/forum/supportforen/smarthome-py/25151-sonos-anbindung

version: 1.8.8 # Plugin version
version: 1.8.9 # Plugin version
sh_minversion: '1.5.1' # minimum shNG version to use this plugin
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hier mindestens 1.10.0 angeben :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ich habe nun 1.10.3 eingetragen

py_minversion: '3.8' # minimum Python version to use for this plugin
Morg42 marked this conversation as resolved.
Show resolved Hide resolved
multi_instance: False # plugin supports multi instance
restartable: unknown
restartable: yes
classname: Sonos # class containing the plugin

parameters:
Expand Down Expand Up @@ -85,6 +85,13 @@ parameters:
de: "(optional) Verlängert die Dauer von Snippet Audio Dateien um einen festen Offset in Sekunden."
en: "(optional) Extend snippet duration by a fixed offset specified in seconds"

pause_item:
type: str
default: ''
description:
de: 'Item, um die Ausführung des Plugins zu steuern'
en: 'item for controlling plugin execution'

item_attributes:
sonos_uid:
type: str
Expand Down
Loading