From d8468edac520663fad33a090c2ff7d1a330d6a75 Mon Sep 17 00:00:00 2001 From: ivande Date: Sun, 12 May 2024 11:26:34 +0200 Subject: [PATCH 01/23] [modbus_tcp]: Reduced logger output during repeated connection issues, connection pause/resume controllable --- modbus_tcp/__init__.py | 85 +++++++++++++++++++++++++++++++++++------ modbus_tcp/plugin.yaml | 2 +- modbus_tcp/user_doc.rst | 3 ++ 3 files changed, 78 insertions(+), 12 deletions(-) diff --git a/modbus_tcp/__init__.py b/modbus_tcp/__init__.py index cd0cd66f3..50d6057eb 100755 --- a/modbus_tcp/__init__.py +++ b/modbus_tcp/__init__.py @@ -37,6 +37,8 @@ from pymodbus.client.tcp import ModbusTcpClient +import logging + AttrAddress = 'modBusAddress' AttrType = 'modBusDataType' AttrFactor = 'modBusFactor' @@ -46,23 +48,29 @@ AttrObjectType = 'modBusObjectType' AttrDirection = 'modBusDirection' - class modbus_tcp(SmartPlugin): """ This class provides a Plugin for SmarthomeNG to read and or write to modbus devices. """ - PLUGIN_VERSION = '1.0.11' + PLUGIN_VERSION = '1.0.12' def __init__(self, sh, *args, **kwargs): """ Initializes the Modbus TCP plugin. The parameters are retrieved from get_parameter_value(parameter_name) """ - + self.logger.info('Init modbus_tcp plugin') + # Disable logging from imported modul 'pymodbus' + if not self.logger.isEnabledFor(logging.DEBUG): + disable_logger = logging.getLogger('pymodbus') + if disable_logger is not None: + self.logger.info(f'change logging level from: {disable_logger} to CRITICAL') + disable_logger.setLevel(logging.CRITICAL) + # Call init code of parent class (SmartPlugin) super().__init__() @@ -77,7 +85,7 @@ def __init__(self, sh, *args, **kwargs): self._crontab = None if not (self._cycle or self._crontab): self.logger.error(f"{self.get_fullname()}: no update cycle or crontab set. Modbus will not be queried automatically") - + self._slaveUnit = int(self.get_parameter_value('slaveUnit')) self._slaveUnitRegisterDependend = False @@ -101,10 +109,11 @@ def run(self): self.logger.debug(f"Plugin '{self.get_fullname()}': run method called") self.alive = True if self._cycle or self._crontab: + self.error_count = 0 # Initialize error count # self.get_shortname() self.scheduler_add('poll_device_' + self._host, self.poll_device, cycle=self._cycle, cron=self._crontab, prio=5) #self.scheduler_add(self.get_shortname(), self._update_values_callback, prio=5, cycle=self._update_cycle, cron=self._update_crontab, next=shtime.now()) - self.logger.debug(f"Plugin '{self.get_fullname()}': run method finished") + self.logger.debug(f"Plugin '{self.get_fullname()}': run method finished ") def stop(self): """ @@ -117,6 +126,17 @@ def stop(self): self.connected = False self.logger.debug(f"Plugin '{self.get_fullname()}': stop method finished") + # sh.plugins.return_plugin('pluginName').suspend() + def set_suspend(self, suspend_active, by): + """ + enable / disable suspend mode: open/close connections, schedulers + """ + if suspend_active: + self.logger.debug(f"Plugin '{self.get_fullname()}': Suspend mode enabled {self.suspended}") + else: + self.logger.debug(f"Plugin '{self.get_fullname()}': Suspend mode disabled {self.suspended}") + #self.suspend_active = suspend_active + def parse_item(self, item): """ Default plugin parse_item method. Is called when the plugin is initialized. @@ -190,6 +210,22 @@ def parse_item(self, item): self.logger.warning("Invalid data direction -> default(read) is used") self._regToRead.update({reg: regPara}) + def log_error(self, message): + """ + Logs an error message based on error count + """ + if self.logger.isEnabledFor(logging.DEBUG): + self.logger.error(message) + else: + if self.error_count < 10: + self.logger.error(message) + elif self.error_count < 100: + if self.error_count % 10 == 0: + self.logger.error(f"{message} [Logging suppressed every 10th error]") + else: + if self.error_count % 100 == 0: + self.logger.error(f"{message} [Logging suppressed every 100th error]") + def poll_device(self): """ Polls for updates of the device @@ -198,19 +234,32 @@ def poll_device(self): changes on it's own, but has to be polled to get the actual status. It is called by the scheduler which is set within run() method. """ + if self.suspended: + if self.suspend_log_poll is None or self.suspend_log_poll is False: # debug - Nachricht nur 1x ausgeben + self.logger.info(f"poll suspended") + self.suspend_log_poll = True + self.error_count = 0 + return + else: + self.suspend_log_poll = False with self.lock: try: if self._Mclient.connect(): - self.logger.info(f"connected to {str(self._Mclient)}") + self.logger.debug(f"connected to {str(self._Mclient)}") self.connected = True + self.error_count = 0 else: - self.logger.error(f"could not connect to {self._host}:{self._port}") + self.error_count += 1 + # Logs an error message based on error count + self.log_error(f"could not connect to {self._host}:{self._port}, connection_attempts: {self.error_count}") self.connected = False return except Exception as e: - self.logger.error(f"connection exception: {str(self._Mclient)} {e}") + self.error_count += 1 + # Logs an error message based on error count + self.log_error(f"connection exception: {str(self._Mclient)} {e}, errors: {self.error_count}") self.connected = False return @@ -247,6 +296,7 @@ def poll_device(self): except Exception as e: self.logger.error(f"something went wrong in the poll_device function: {e}") + # called each time an item changes. def update_item(self, item, caller=None, source=None, dest=None): """ @@ -265,6 +315,14 @@ def update_item(self, item, caller=None, source=None, dest=None): slaveUnit = self._slaveUnit dataDirection = 'read' + if self.suspended: + if self.suspend_log_update is None or self.suspend_log_update is False: # debug - Nachricht nur 1x ausgeben + self.logger.info('Plugin is suspended, data will not be written') + self.suspend_log_update = True + return + else: + self.suspend_log_update = False + if caller == self.get_fullname(): # self.logger.debug(f'item was changed by the plugin itself - caller:{caller} source:{source} dest:{dest}') return @@ -301,15 +359,20 @@ def update_item(self, item, caller=None, source=None, dest=None): self.logger.debug(f'update_item:{item} value:{item()} regToWrite: {reg}') try: if self._Mclient.connect(): - self.logger.info(f"connected to {str(self._Mclient)}") + self.logger.debug(f"connected to {str(self._Mclient)}") self.connected = True + self.error_count = 0 else: - self.logger.error(f"could not connect to {self._host}:{self._port}") + self.error_count += 1 + # Logs an error message based on error count + self.log_error(f"could not connect to {self._host}:{self._port}, connection_attempts: {self.error_count}") self.connected = False return except Exception as e: - self.logger.error(f"connection exception: {str(self._Mclient)} {e}") + self.error_count += 1 + # Logs an error message based on error count + self.log_error(f"connection exception: {str(self._Mclient)} {e}, errors: {self.error_count}") self.connected = False return diff --git a/modbus_tcp/plugin.yaml b/modbus_tcp/plugin.yaml index 7505b8819..51c6cc90d 100755 --- a/modbus_tcp/plugin.yaml +++ b/modbus_tcp/plugin.yaml @@ -11,7 +11,7 @@ plugin: keywords: modbus_tcp modbus smartmeter inverter heatpump #documentation: http://smarthomeng.de/user/plugins/modbus_tcp/user_doc.html support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1154368-einbindung-von-modbus-tcp - version: 1.0.11 # Plugin version + version: 1.0.12 # Plugin version sh_minversion: 1.8 # minimum shNG version to use this plugin #sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) py_minversion: 3.6 diff --git a/modbus_tcp/user_doc.rst b/modbus_tcp/user_doc.rst index cedaa70a1..360e393cb 100755 --- a/modbus_tcp/user_doc.rst +++ b/modbus_tcp/user_doc.rst @@ -164,6 +164,9 @@ siehe auch example.yaml Changelog --------- +V1.0.12 bei wiederholten Verbindungsproblemen Ausgabe vom Logger reduziert + Verbindungstop mit supend/resume steuerbar + V1.0.11 Verbesserung Umwandlung Byte/Wordorder in Endian-Konstante Fehler beim Schreiben von Register behoben From cfbf5430c367950f9c4152f496be222fa93729de Mon Sep 17 00:00:00 2001 From: msinn Date: Tue, 21 May 2024 14:04:18 +0200 Subject: [PATCH 02/23] hue3: Some optimizations --- hue3/__init__.py | 116 +++++++++++++++++--------------- hue3/user_doc.rst | 2 +- hue3/webif/templates/index.html | 8 +-- 3 files changed, 65 insertions(+), 61 deletions(-) diff --git a/hue3/__init__.py b/hue3/__init__.py index 514bed42b..2a81505a9 100755 --- a/hue3/__init__.py +++ b/hue3/__init__.py @@ -206,7 +206,9 @@ async def plugin_coro(self): self.logger.exception(f"Exception in initialize_items_from_bridge(): {ex}") # block: wait until a stop command is received by the queue - queue_item = await self.run_queue.get() + #queue_item = await self.run_queue.get() + #queue_item = await self.get_command_from_run_queue() + await self.wait_for_asyncio_termination() self.alive = False self.logger.info("plugin_coro: Plugin is stopped (self.alive=False)") @@ -825,7 +827,6 @@ def discover_bridges(self): for br in discovered_bridges: ip = discovered_bridges[br].split('/')[2].split(':')[0] br_info = self.get_bridge_desciption(ip) -# br_config = self.get_bridge_config(ip) bridges.append(br_info) for bridge in bridges: @@ -872,19 +873,68 @@ def create_new_username(self, ip, devicetype=None, timeout=5): return app_key + def disconnect_bridge(self): + """ + Disconnect the plugin from the bridge + + :param disconnect: + :return: + """ + if not self.bridge_is_configured(): + # There is no bridge to disconnect from + return + + self.logger.notice(f"Disconnect: Disconnecting bridge") + self.stop() + self.bridge_ip = '0.0.0.0' + + self.logger.notice(f"disconnect_bridge: self.bridge = {self.bridge}") + + self.bridge = {} + + # update the plugin section in ../etc/plugin.yaml + self.update_plugin_config() + + self.run() + + + # -------------------------------------------------------------------------------------------- + + def get_bridge_config(self, host: str = None) -> dict: + """ + Get configuration info of a bridge + + :param host: IP Address of the bridge + :return: configuration info + """ + if host is None: + if not self.bridge_is_configured(): + return {} + host = self.bridge_ip + + if self.bridge_user == '': + user = None + else: + user = self.bridge_user + + try: + bridge_config = self.run_asyncio_coro(self.get_config(host, user), return_exeption=False) + except Exception as ex: + bridge_config = {} + self.logger.error(f"get_bridge_config: {ex}") + return bridge_config + + from aiohttp import ClientSession async def get_config(self, host: str, app_key: str = None, websession: ClientSession | None = None ) -> dict: """ - Get configuration of the Hue bridge and return it's whitelist. + Get configuration of the Hue bridge using aiohue and return it's whitelist. - The link button on the bridge must be pressed before executing this call, - otherwise a LinkButtonNotPressed error will be raised. - - Parameters: - `host`: the hostname or IP-address of the bridge as string. - `device_type`: provide a name/type for your app for identification. - `websession`: optionally provide a aiohttp ClientSession. + :param host: the hostname or IP-address of the bridge as string. + :param app_key: provide a name/type for your app for identification. + :param websession: optionally provide a aiohttp ClientSession. + :return: """ # https://developers.meethue.com/develop/hue-api/7-configuration-api/#72_get_configuration # this can be used for both V1 and V2 bridges (for now). @@ -916,49 +966,3 @@ async def get_config(self, host: str, app_key: str = None, websession: ClientSes if not websession_provided: await websession.close() - - def get_bridge_config(self, host: str = None) -> dict: - - - if host is None: - if not self.bridge_is_configured(): - return {} - host = self.bridge_ip - - if self.bridge_user == '': - user = None - else: - user = self.bridge_user - - try: - bridge_config = self.run_asyncio_coro(self.get_config(host, user), return_exeption=False) - except Exception as ex: - bridge_config = {} - self.logger.error(f"get_bridge_config: {ex}") - return bridge_config - - - def disconnect_bridge(self): - """ - Disconnect the plugin from the bridge - - :param disconnect: - :return: - """ - if not self.bridge_is_configured(): - # There is no bridge to disconnect from - return - - self.logger.notice(f"Disconnect: Disconnecting bridge") - self.stop() - self.bridge_ip = '0.0.0.0' - - self.logger.notice(f"disconnect_bridge: self.bridge = {self.bridge}") - - self.bridge = {} - - # update the plugin section in ../etc/plugin.yaml - self.update_plugin_config() - - self.run() - diff --git a/hue3/user_doc.rst b/hue3/user_doc.rst index 9f100c966..788cfeabc 100755 --- a/hue3/user_doc.rst +++ b/hue3/user_doc.rst @@ -27,7 +27,7 @@ Plugin notwendig wurde, um das neue API zu unterstützen. Die wichtigsten Features/Änderungen beim API v2: -- Aktive Meldung von Veränderungen durch die Bridge +- Aktive Meldung von Veränderungen durch die Bridge, das Plugin muss den Status nicht mehr periodisch pollen - https Verbindung statt http - neue (längere) Ids (z.B.: 2915002c-6c8f-4d9b-9134-6b1a8ded4be3) - Unterstützung mehrerer Lights in einem Device diff --git a/hue3/webif/templates/index.html b/hue3/webif/templates/index.html index c65d66141..a9d168f19 100755 --- a/hue3/webif/templates/index.html +++ b/hue3/webif/templates/index.html @@ -287,7 +287,7 @@ order: {idx: 2, dir: 'asc'}, columnDefs: [ { - title: "{{ _('Device Id') }}", + title: "{{ _('Leuchten Id') }}", targets: [1], "className": "deviceid", }, { @@ -327,7 +327,7 @@ order: {idx: 2, dir: 'asc'}, columnDefs: [ { - title: "{{ _('Device Id') }}", + title: "{{ _('Szenen Id') }}", targets: [1], "className": "deviceid", }, { @@ -351,7 +351,7 @@ order: {idx: 2, dir: 'asc'}, columnDefs: [ { - title: "{{ _('Device Id') }}", + title: "{{ _('Gruppen Id') }}", targets: [1], "className": "deviceid", }, { @@ -383,7 +383,7 @@ targets: [2], "className": "aligncenter" }, { - title: "{{ _('Device Id') }}", + title: "{{ _('Sensor Id') }}", targets: [3], "className": "deviceid", }, { From 39fadf900ffeda4f9ffc96ca8ec3692b78e0c18c Mon Sep 17 00:00:00 2001 From: ivande Date: Tue, 21 May 2024 15:35:01 +0200 Subject: [PATCH 03/23] suspend and resume the connection via item --- modbus_tcp/__init__.py | 146 +++++++++++++++++++++++++++-------------- 1 file changed, 96 insertions(+), 50 deletions(-) diff --git a/modbus_tcp/__init__.py b/modbus_tcp/__init__.py index 50d6057eb..073e78697 100755 --- a/modbus_tcp/__init__.py +++ b/modbus_tcp/__init__.py @@ -1,31 +1,30 @@ #!/usr/bin/env python3 # vim: set encoding=utf-8 tabstop=4 softtabstop=4 shiftwidth=4 expandtab ######################################################################### -# Copyright 2022 De Filippis Ivan -# Copyright 2022 Ronny Schulz -# Copyright 2023 Bernd Meiners +# Copyright 2022 De Filippis Ivan +# Copyright 2022 Ronny Schulz +# Copyright 2023 Bernd Meiners ######################################################################### -# This file is part of SmartHomeNG. +# This file is part of SmartHomeNG. # -# Modbus_TCP plugin for SmartHomeNG +# Modbus_TCP plugin for SmartHomeNG # -# SmartHomeNG is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# SmartHomeNG is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. # -# SmartHomeNG is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# SmartHomeNG is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with SmartHomeNG. If not, see . +# You should have received a copy of the GNU General Public License +# along with SmartHomeNG. If not, see . # ######################################################################### -from lib.model.smartplugin import * -from lib.item import Items +from lib.model.smartplugin import SmartPlugin from datetime import datetime import threading @@ -61,7 +60,7 @@ def __init__(self, sh, *args, **kwargs): Initializes the Modbus TCP plugin. The parameters are retrieved from get_parameter_value(parameter_name) """ - + self.logger.info('Init modbus_tcp plugin') # Disable logging from imported modul 'pymodbus' @@ -70,17 +69,17 @@ def __init__(self, sh, *args, **kwargs): if disable_logger is not None: self.logger.info(f'change logging level from: {disable_logger} to CRITICAL') disable_logger.setLevel(logging.CRITICAL) - + # Call init code of parent class (SmartPlugin) super().__init__() self._host = self.get_parameter_value('host') self._port = self.get_parameter_value('port') - self._cycle = self.get_parameter_value('cycle') # the frequency in seconds how often the device should be accessed + self._cycle = self.get_parameter_value('cycle') # the frequency in seconds how often the device should be accessed if self._cycle == 0: self._cycle = None - self._crontab = self.get_parameter_value('crontab') # the more complex way to specify the device query frequency + self._crontab = self.get_parameter_value('crontab') # the more complex way to specify the device query frequency if self._crontab == '': self._crontab = None if not (self._cycle or self._crontab): @@ -107,36 +106,68 @@ def run(self): Run method for the plugin """ self.logger.debug(f"Plugin '{self.get_fullname()}': run method called") + if self.alive: + return self.alive = True + self.set_suspend(by='run()') + if self._cycle or self._crontab: - self.error_count = 0 # Initialize error count - # self.get_shortname() - self.scheduler_add('poll_device_' + self._host, self.poll_device, cycle=self._cycle, cron=self._crontab, prio=5) - #self.scheduler_add(self.get_shortname(), self._update_values_callback, prio=5, cycle=self._update_cycle, cron=self._update_crontab, next=shtime.now()) + self.error_count = 0 # Initialize error count + self._create_cyclic_scheduler() self.logger.debug(f"Plugin '{self.get_fullname()}': run method finished ") + def _create_cyclic_scheduler(self): + self.scheduler_add('poll_device_' + self._host, self.poll_device, cycle=self._cycle, cron=self._crontab, prio=5) + + def _remove_cyclic_scheduler(self): + self.scheduler_remove('poll_device_' + self._host) + def stop(self): """ Stop method for the plugin """ self.alive = False self.logger.debug(f"Plugin '{self.get_fullname()}': stop method called") - self.scheduler_remove('poll_device_' + self._host) + self._remove_cyclic_scheduler() self._Mclient.close() self.connected = False self.logger.debug(f"Plugin '{self.get_fullname()}': stop method finished") # sh.plugins.return_plugin('pluginName').suspend() - def set_suspend(self, suspend_active, by): + def set_suspend(self, suspend_active=None, by=None): """ enable / disable suspend mode: open/close connections, schedulers """ + + if suspend_active is None: + if self._suspend_item is not None: + # if no parameter set, try to use item setting + suspend_active = bool(self._suspend_item()) + else: + # if not available, default to "resume" (non-breaking default) + suspend_active = False + + # print debug logging if suspend_active: - self.logger.debug(f"Plugin '{self.get_fullname()}': Suspend mode enabled {self.suspended}") + msg = 'Suspend mode enabled' else: - self.logger.debug(f"Plugin '{self.get_fullname()}': Suspend mode disabled {self.suspended}") - #self.suspend_active = suspend_active - + msg = 'Suspend mode disabled' + if by: + msg += f' (set by {by})' + self.logger.debug(msg) + + # activate selected mode, use smartplugin methods + if suspend_active: + self.suspend(by) + else: + self.resume(by) + + if suspend_active: + self._remove_cyclic_scheduler() + else: + self._create_cyclic_scheduler() + + def parse_item(self, item): """ Default plugin parse_item method. Is called when the plugin is initialized. @@ -145,6 +176,14 @@ def parse_item(self, item): :param item: The item to process. """ + + # check for suspend item + if item.property.path == self._suspend_item_path: + self.logger.debug(f'suspend item {item.property.path} registered') + self._suspend_item = item + self.add_item(item, updating=True) + return self.update_item + if self.has_iattr(item.conf, AttrAddress): self.logger.debug(f"parse item: {item}") regAddr = int(self.get_iattr_value(item.conf, AttrAddress)) @@ -167,7 +206,7 @@ def parse_item(self, item): if self.has_iattr(item.conf, AttrObjectType): objectType = self.get_iattr_value(item.conf, AttrObjectType) - reg = str(objectType) # dictionary key: objectType.regAddr.slaveUnit // HoldingRegister.528.1 + reg = str(objectType) # dictionary key: objectType.regAddr.slaveUnit // HoldingRegister.528.1 reg += '.' reg += str(regAddr) reg += '.' @@ -181,19 +220,19 @@ def parse_item(self, item): byteOrderStr = self.get_iattr_value(item.conf, AttrByteOrder) if self.has_iattr(item.conf, AttrWordOrder): wordOrderStr = self.get_iattr_value(item.conf, AttrWordOrder) - + try: # den letzten Teil des Strings extrahieren, in Großbuchstaben und in Endian-Konstante wandeln - byteOrder = Endian[(str(byteOrderStr).split('.')[-1]).upper()] + byteOrder = Endian[(str(byteOrderStr).split('.')[-1]).upper()] except Exception as e: self.logger.warning(f"Invalid byteOrder -> default(Endian.BIG) is used. Error:{e}") byteOrder = Endian.BIG - + try: # den letzten Teil des Strings extrahieren, in Großbuchstaben und in Endian-Konstante wandeln - wordOrder = Endian[(str(wordOrderStr).split('.')[-1]).upper()] + wordOrder = Endian[(str(wordOrderStr).split('.')[-1]).upper()] except Exception as e: self.logger.warning(f"Invalid byteOrder -> default(Endian.BIG) is used. Error:{e}") wordOrder = Endian.BIG - + regPara = {'regAddr': regAddr, 'slaveUnit': slaveUnit, 'dataType': dataType, 'factor': factor, 'byteOrder': byteOrder, 'wordOrder': wordOrder, 'item': item, 'value': value, 'objectType': objectType, @@ -235,10 +274,10 @@ def poll_device(self): It is called by the scheduler which is set within run() method. """ if self.suspended: - if self.suspend_log_poll is None or self.suspend_log_poll is False: # debug - Nachricht nur 1x ausgeben + if self.suspend_log_poll is None or self.suspend_log_poll is False: # debug - Nachricht nur 1x ausgeben self.logger.info(f"poll suspended") self.suspend_log_poll = True - self.error_count = 0 + self.error_count = 0 return else: self.suspend_log_poll = False @@ -248,7 +287,7 @@ def poll_device(self): if self._Mclient.connect(): self.logger.debug(f"connected to {str(self._Mclient)}") self.connected = True - self.error_count = 0 + self.error_count = 0 else: self.error_count += 1 # Logs an error message based on error count @@ -315,8 +354,15 @@ def update_item(self, item, caller=None, source=None, dest=None): slaveUnit = self._slaveUnit dataDirection = 'read' + # check for suspend item + if item is self._suspend_item: + if caller != self.get_shortname(): + self.logger.debug(f'Suspend item changed to {item()}') + self.set_suspend(item(), by=f'suspend item {item.property.path}') + return + if self.suspended: - if self.suspend_log_update is None or self.suspend_log_update is False: # debug - Nachricht nur 1x ausgeben + if self.suspend_log_update is None or self.suspend_log_update is False: # debug - Nachricht nur 1x ausgeben self.logger.info('Plugin is suspended, data will not be written') self.suspend_log_update = True return @@ -348,7 +394,7 @@ def update_item(self, item, caller=None, source=None, dest=None): else: return - reg = str(objectType) # Dict-key: HoldingRegister.528.1 *** objectType.regAddr.slaveUnit *** + reg = str(objectType) # Dict-key: HoldingRegister.528.1 *** objectType.regAddr.slaveUnit *** reg += '.' reg += str(regAddr) reg += '.' @@ -361,7 +407,7 @@ def update_item(self, item, caller=None, source=None, dest=None): if self._Mclient.connect(): self.logger.debug(f"connected to {str(self._Mclient)}") self.connected = True - self.error_count = 0 + self.error_count = 0 else: self.error_count += 1 # Logs an error message based on error count @@ -390,16 +436,16 @@ def __write_Registers(self, regPara, value): bo = regPara['byteOrder'] wo = regPara['wordOrder'] dataTypeStr = regPara['dataType'] - dataType = ''.join(filter(str.isalpha, dataTypeStr)) # vom dataType die Ziffen entfernen z.B. uint16 = uint - registerCount = 0 # Anzahl der zu schreibenden Register (Words) + dataType = ''.join(filter(str.isalpha, dataTypeStr)) # vom dataType die Ziffen entfernen z.B. uint16 = uint + registerCount = 0 # Anzahl der zu schreibenden Register (Words) try: - bits = int(''.join(filter(str.isdigit, dataTypeStr))) # bit-Zahl aus aus dataType z.B. uint16 = 16 + bits = int(''.join(filter(str.isdigit, dataTypeStr))) # bit-Zahl aus aus dataType z.B. uint16 = 16 except: bits = 16 if dataType.lower() == 'string': - registerCount = int(bits / 2) # bei string: bits = bytes !! string16 -> 16Byte - 8 registerCount + registerCount = int(bits / 2) # bei string: bits = bytes !! string16 -> 16Byte - 8 registerCount else: registerCount = int(bits / 16) @@ -439,11 +485,11 @@ def __write_Registers(self, regPara, value): builder.add_string(value) elif dataType.lower() == 'bit': if objectType == 'Coil' or objectType == 'DiscreteInput': - if not isinstance(value, bool): # test is boolean + if not isinstance(value, bool): # test is boolean self.logger.error(f"Value is not boolean: {value}") return else: - if set(value).issubset({'0', '1'}) and bool(value): # test is bit-string '00110101' + if set(value).issubset({'0', '1'}) and bool(value): # test is bit-string '00110101' builder.add_bits(value) else: self.logger.error(f"Value is not a bitstring: {value}") @@ -500,7 +546,7 @@ def __read_Registers(self, regPara): bits = 16 if dataType.lower() == 'string': - registerCount = int(bits / 2) # bei string: bits = bytes !! string16 -> 16Byte - 8 registerCount + registerCount = int(bits / 2) # bei string: bits = bytes !! string16 -> 16Byte - 8 registerCount else: registerCount = int(bits / 16) From 45486206130e7b6e2ca87e0f7be81736edbf3d5a Mon Sep 17 00:00:00 2001 From: msinn Date: Sat, 25 May 2024 12:39:00 +0200 Subject: [PATCH 04/23] hue3: Improved error handling and added logging, if hue device has a connectivity issue --- hue3/__init__.py | 33 ++++++++++++++++++++++++--------- hue3/plugin.yaml | 4 ++-- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/hue3/__init__.py b/hue3/__init__.py index 2a81505a9..dfc873332 100755 --- a/hue3/__init__.py +++ b/hue3/__init__.py @@ -59,7 +59,7 @@ class HueApiV2(SmartPlugin): the update functions for the items """ - PLUGIN_VERSION = '3.0.0' # (must match the version specified in plugin.yaml) + PLUGIN_VERSION = '3.0.1' # (must match the version specified in plugin.yaml) hue_sensor_state_values = ['daylight', 'temperature', 'presence', 'lightlevel', 'status'] @@ -605,20 +605,32 @@ def update_light_from_item(self, config_data, item): hue_transition_time = int(float(config_data['transition_time']) * 1000) if config_data['function'] == 'on': - if value: - self.run_asyncio_coro(self.v2bridge.lights.turn_on(config_data['id'], hue_transition_time)) - else: - self.run_asyncio_coro(self.v2bridge.lights.turn_off(config_data['id'], hue_transition_time)) + try: + if value: + self.run_asyncio_coro(self.v2bridge.lights.turn_on(config_data['id'], hue_transition_time), return_exeption=True) + else: + self.run_asyncio_coro(self.v2bridge.lights.turn_off(config_data['id'], hue_transition_time), return_exeption=True) + except Exception as ex: + self.logger.error(f"update_light_from_item: id={config_data['id']}, {config_data['function']}, {value=} - Exception {ex}") elif config_data['function'] == 'bri': if float(value) <= 100: - self.run_asyncio_coro(self.v2bridge.lights.set_brightness(config_data['id'], float(value), hue_transition_time)) + try: + self.run_asyncio_coro(self.v2bridge.lights.set_brightness(config_data['id'], float(value), hue_transition_time), return_exeption=True) + except Exception as ex: + self.logger.error(f"update_light_from_item: id={config_data['id']}, {config_data['function']}, {value=} - Exception {ex}") else: self.logger.error(f"{item.property.path}: Can't set brightness of light {config_data['id']} to {value} - out of range") elif config_data['function'] == 'xy' and isinstance(value, list) and len(value) == 2: - self.run_asyncio_coro(self.v2bridge.lights.set_color(config_data['id'], value[0], value[1], hue_transition_time)) + try: + self.run_asyncio_coro(self.v2bridge.lights.set_color(config_data['id'], value[0], value[1], hue_transition_time), return_exeption=True) + except Exception as ex: + self.logger.error(f"update_light_from_item: id={config_data['id']}, {config_data['function']}, {value=} - Exception {ex}") elif config_data['function'] == 'ct': if float(value) >= 153 and float(value) <= 500: - self.run_asyncio_coro(self.v2bridge.lights.set_color_temperature(config_data['id'], value, hue_transition_time)) + try: + self.run_asyncio_coro(self.v2bridge.lights.set_color_temperature(config_data['id'], value, hue_transition_time), return_exeption=True) + except Exception as ex: + self.logger.error(f"update_light_from_item: id={config_data['id']}, {config_data['function']}, {value=} - Exception {ex}") else: self.logger.error(f"{item.property.path}: Can't set color temperature of light {config_data['id']} to {value} - out of range") elif config_data['function'] == 'dict': @@ -636,7 +648,10 @@ def update_light_from_item(self, config_data, item): transition_time = hue_transition_time else: transition_time = int(float(transition_time)*1000) - self.run_asyncio_coro(self.v2bridge.lights.set_state(config_data['id'], on, bri, xy, ct, transition_time=transition_time)) + try: + self.run_asyncio_coro(self.v2bridge.lights.set_state(config_data['id'], on, bri, xy, ct, transition_time=transition_time), return_exeption=True) + except Exception as ex: + self.logger.error(f"update_light_from_item: id={config_data['id']}, {config_data['function']}, {on=}, {bri=}, {xy=}, {ct=} - Exception {ex}") elif config_data['function'] == 'bri_inc': if float(value) >= -100 and float(value) <= 100: if float(value) < 0: diff --git a/hue3/plugin.yaml b/hue3/plugin.yaml index 3e59e2f96..b53433cb8 100755 --- a/hue3/plugin.yaml +++ b/hue3/plugin.yaml @@ -12,8 +12,8 @@ plugin: # documentation: https://github.com/smarthomeNG/smarthome/wiki/CLI-Plugin # url of documentation (wiki) page support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1586861-support-thread-für-das-hue2-plugin - version: 3.0.0 # Plugin version (must match the version specified in __init__.py) - sh_minversion: 1.10.0 # minimum shNG version to use this plugin + version: 3.0.1 # Plugin version (must match the version specified in __init__.py) + sh_minversion: '1.10.0' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) # py_minversion: 3.6 # minimum Python version to use for this plugin # py_maxversion: # maximum Python version to use for this plugin (leave empty if latest) From 3ba5ef496d0b3edbb72f7443d568dcbdb45333b6 Mon Sep 17 00:00:00 2001 From: aschwith Date: Sat, 25 May 2024 21:35:29 +0200 Subject: [PATCH 05/23] sonos: bumbed to version 1.8.7; fix for collections import for python >= 3.10 --- sonos/__init__.py | 2 +- sonos/plugin.yaml | 2 +- sonos/utils.py | 6 ++++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/sonos/__init__.py b/sonos/__init__.py index 6c6267886..7c46bd864 100755 --- a/sonos/__init__.py +++ b/sonos/__init__.py @@ -2998,7 +2998,7 @@ class Sonos(SmartPlugin): """ Main class of the Plugin. Does all plugin specific stuff """ - PLUGIN_VERSION = "1.8.6" + PLUGIN_VERSION = "1.8.7" def __init__(self, sh): """Initializes the plugin.""" diff --git a/sonos/plugin.yaml b/sonos/plugin.yaml index 55379cba9..9b2079687 100755 --- a/sonos/plugin.yaml +++ b/sonos/plugin.yaml @@ -12,7 +12,7 @@ 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.6 # Plugin version + version: 1.8.7 # Plugin version sh_minversion: 1.5.1 # minimum shNG version to use this plugin py_minversion: 3.8 # minimum Python version to use for this plugin multi_instance: False # plugin supports multi instance diff --git a/sonos/utils.py b/sonos/utils.py index 3876564bd..77d3fba1e 100755 --- a/sonos/utils.py +++ b/sonos/utils.py @@ -2,8 +2,10 @@ import os import socket import re -from collections import Set - +try: + from collections.abc import Set +except ImportError: + from collections import Set def is_valid_port(port): valid_port = re.compile( From 979c751103cebf04c46b3f81885065189fb32e32 Mon Sep 17 00:00:00 2001 From: aschwith Date: Mon, 27 May 2024 17:59:05 +0200 Subject: [PATCH 06/23] sonos: removed debug logs with warning level --- sonos/__init__.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sonos/__init__.py b/sonos/__init__.py index 7c46bd864..918abc9ff 100755 --- a/sonos/__init__.py +++ b/sonos/__init__.py @@ -2318,7 +2318,7 @@ def _play_tunein(self, station_name: str, music_service: str = 'TuneIn', start: # if a patch is applied. # ------------------------------------------------------------------------------------------------------------ # - self.logger.warning(f"DEBUG: _play_tunein start") + self.logger.debug(f"_play_tunein start") if not self._check_property(): return False, "Property check failed" @@ -2359,7 +2359,7 @@ def _play_tunein(self, station_name: str, music_service: str = 'TuneIn', start: return False, "response should contain either the key 'searchResult' or 'getMetadataResult'" for result_type in ('mediaCollection', 'mediaMetadata'): - self.logger.warning(f"DEBUG loop: result is {result_type}") + self.logger.debug(f"loop: result is {result_type}") # Upper case the first letter (used for the class_key) result_type_proper = result_type[0].upper() + result_type[1:] @@ -2373,7 +2373,7 @@ def _play_tunein(self, station_name: str, music_service: str = 'TuneIn', start: # formed by concatenating the result type with the item type. Turns # into e.g: MediaMetadataTrack - self.logger.warning(f"DEBUG loop 2: raw_item is {raw_item}") + self.logger.debug(f"loop 2: raw_item is {raw_item}") class_key = result_type_proper + raw_item['itemType'].title() cls = get_class(class_key) @@ -2383,7 +2383,7 @@ def _play_tunein(self, station_name: str, music_service: str = 'TuneIn', start: #cls.from_music_service(MusicService(service_name='TuneIn', token_store=JsonFileTokenStore()), raw_item)) if not items: - self.logger.warning(f"DEBUG _play radio: No matching items found") + self.logger.warning(f"_play radio: No matching items found") exit(0) item_id = items[0].metadata['id'] @@ -2396,10 +2396,10 @@ def _play_tunein(self, station_name: str, music_service: str = 'TuneIn', start: self.soco.avTransport.SetAVTransportURI([('InstanceID', 0), ('CurrentURI', uri), ('CurrentURIMetaData', meta)]) if start: - self.logger.warning(f"DEBUG _play radio: Starting play") + self.logger.debug(f"_play radio: Starting play") self.soco.play() - self.logger.warning(f"DEBUG _play radio: finished function") + self.logger.debug(f"_play radio: finished function") return True, "" def _play_radio(self, station_name: str, music_service: str = 'TuneIn', start: bool = True) -> tuple: From 923acacb64693e4e55bd1ca4eca338090a85b797 Mon Sep 17 00:00:00 2001 From: Onkel Andy Date: Sat, 1 Jun 2024 22:32:40 +0200 Subject: [PATCH 07/23] lms plugin: fix commands for querying data --- lms/__init__.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/lms/__init__.py b/lms/__init__.py index 43e14ca5b..df32aad30 100755 --- a/lms/__init__.py +++ b/lms/__init__.py @@ -94,7 +94,7 @@ def trigger_read(command): if not custom: return - if command == 'player.info.playlists.names': + if command == f'player.info.playlists.names{CUSTOM_SEP}{custom}': self.logger.debug(f"Got command playlist names {command} data {data} value {value} custom {custom} by {by}") trigger_read('player.playlist.id') trigger_read('player.playlist.name') @@ -102,7 +102,7 @@ def trigger_read(command): if command == 'playlist.rename': trigger_read('info.playlists.names') # set alarm - if command == 'player.control.alarms': + if command == f'player.control.alarms{CUSTOM_SEP}{custom}': # 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: @@ -118,7 +118,7 @@ def trigger_read(command): self.logger.error(f"Error setting alarm: {e}") # set album art URL - if command == 'player.info.album': + if command == f'player.info.album{CUSTOM_SEP}{custom}': self.logger.debug(f"Got command album {command} data {data} value {value} custom {custom} by {by}") host = self._parameters['web_host'] port = self._parameters['web_port'] @@ -130,17 +130,17 @@ def trigger_read(command): self._dispatch_callback('player.info.albumarturl' + CUSTOM_SEP + custom, url, by) # set playlist ID - if command == 'player.playlist.load': + if command == f'player.playlist.load{CUSTOM_SEP}{custom}': self.logger.debug(f"Got command load {command} data {data} value {value} custom {custom} by {by}") trigger_read('player.playlist.id') trigger_read('player.control.playmode') - if command == 'player.playlist.id': + if command == f'player.playlist.id{CUSTOM_SEP}{custom}': self.logger.debug(f"Got command id {command} data {data} value {value} custom {custom} by {by}") trigger_read('player.playlist.name') # update on new song - if command == 'player.info.title': + if command == f'player.info.title{CUSTOM_SEP}{custom}': # trigger_read('player.control.playmode') # trigger_read('player.playlist.index') trigger_read('player.info.duration') @@ -150,7 +150,7 @@ def trigger_read(command): trigger_read('player.info.path') # update on new song - if command == 'player.control.playpause' and value: + if command == f'player.control.playpause{CUSTOM_SEP}{custom}' and value: trigger_read('player.control.playmode') trigger_read('player.info.duration') trigger_read('player.info.album') @@ -159,7 +159,7 @@ def trigger_read(command): trigger_read('player.info.path') # update on new song - if command == 'player.playlist.index': + if command == f'player.playlist.index{CUSTOM_SEP}{custom}': self.logger.debug(f"Got command index {command} data {data} value {value} custom {custom} by {by}") trigger_read('player.control.playmode') trigger_read('player.info.duration') @@ -170,12 +170,12 @@ def trigger_read(command): trigger_read('player.info.title') # update current time info - if command in ['player.control.forward', 'player.control.rewind']: + if command in [f'player.control.forward{CUSTOM_SEP}{custom}', f'player.control.rewind{CUSTOM_SEP}{custom}']: self.logger.debug(f"Got command forward/rewind {command} data {data} value {value} custom {custom} by {by}") trigger_read('player.control.time') # update play and stop items based on playmode - if command == 'player.control.playmode': + if command == f'player.control.playmode{CUSTOM_SEP}{custom}': self.logger.debug(f"Got command playmode {command} data {data} value {value} custom {custom} by {by}") mode = data.split("mode")[-1].strip() mode = mode.split("playlist")[-1].strip() @@ -186,7 +186,7 @@ def trigger_read(command): trigger_read('player.control.time') # update play and stop items based on playmode - if command == 'player.control.stop' or (command == 'player.control.playpause' and not value): + if command == f'player.control.stop{CUSTOM_SEP}{custom}' or (command == f'player.control.playpause{CUSTOM_SEP}{custom}' and not value): self.logger.debug(f"Got command stop or pause {command} data {data} value {value} custom {custom} by {by}") trigger_read('player.control.playmode') From 5717003ab548bba8d655336fb5f2f3907730730b Mon Sep 17 00:00:00 2001 From: Onkel Andy Date: Sat, 1 Jun 2024 22:33:42 +0200 Subject: [PATCH 08/23] lms plugin: fix reply pattern for some commands --- lms/commands.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lms/commands.py b/lms/commands.py index 6d17d55fd..2b29e4308 100755 --- a/lms/commands.py +++ b/lms/commands.py @@ -56,9 +56,9 @@ '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']}, + '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}}, @@ -68,7 +68,7 @@ '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]+)']}, + '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}}, From fea3f53b6499719b3e08ecb7439862a47b632f38 Mon Sep 17 00:00:00 2001 From: Onkel Andy Date: Sat, 1 Jun 2024 22:34:00 +0200 Subject: [PATCH 09/23] lms plugin: fix and update structs --- lms/plugin.yaml | 1279 +++++++++++++++++++++++------------------------ 1 file changed, 639 insertions(+), 640 deletions(-) diff --git a/lms/plugin.yaml b/lms/plugin.yaml index 0f76d7b2c..00398532b 100755 --- a/lms/plugin.yaml +++ b/lms/plugin.yaml @@ -149,7 +149,6 @@ parameters: de: Item-Pfad für das Standby-Item en: item path for standby switch item - item_attributes: sqb_command: @@ -226,126 +225,126 @@ item_structs: read: type: bool enforce_updates: true - sqb_read_group_trigger: server + sqb_read_group_trigger@instance: server listenmode: type: bool - sqb_command: server.listenmode - sqb_read: true - sqb_write: true - sqb_custom1: '' + sqb_command@instance: server.listenmode + sqb_read@instance: true + sqb_write@instance: true + sqb_custom1@instance: '' playercount: type: num - sqb_command: server.playercount - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: server.playercount + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - server - sqb_read_initial: true - sqb_custom1: '' + sqb_read_initial@instance: true + sqb_custom1@instance: '' favoritescount: type: num - sqb_command: server.favoritescount - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: server.favoritescount + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - server - sqb_read_initial: true - sqb_custom1: '' + sqb_read_initial@instance: true + sqb_custom1@instance: '' database: read: type: bool enforce_updates: true - sqb_read_group_trigger: database + sqb_read_group_trigger@instance: database rescan: read: type: bool enforce_updates: true - sqb_read_group_trigger: database.rescan + sqb_read_group_trigger@instance: database.rescan start: type: str - sqb_command: database.rescan.start - sqb_read: false - sqb_write: true - sqb_custom1: '' + 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: type: bool - sqb_command: database.rescan.running - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: database.rescan.running + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - database - database.rescan - sqb_read_initial: true - sqb_read_cycle: '120' - sqb_custom1: '' + sqb_read_initial@instance: true + sqb_read_cycle@instance: '120' + sqb_custom1@instance: '' progress: type: str - sqb_command: database.rescan.progress - sqb_read: true - sqb_write: false - sqb_custom1: '' + sqb_command@instance: database.rescan.progress + sqb_read@instance: true + sqb_write@instance: false + sqb_custom1@instance: '' runningtime: type: str - sqb_command: database.rescan.runningtime - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: database.rescan.runningtime + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - database - database.rescan - sqb_custom1: '' + sqb_custom1@instance: '' fail: type: str - sqb_command: database.rescan.fail - sqb_read: true - sqb_write: false - sqb_custom1: '' + sqb_command@instance: database.rescan.fail + sqb_read@instance: true + sqb_write@instance: false + sqb_custom1@instance: '' abortscan: type: bool - sqb_command: database.rescan.abortscan - sqb_read: true - sqb_write: true - sqb_custom1: '' + sqb_command@instance: database.rescan.abortscan + sqb_read@instance: true + sqb_write@instance: true + sqb_custom1@instance: '' wipecache: type: bool - sqb_command: database.rescan.wipecache - sqb_read: true - sqb_write: true - sqb_custom1: '' + sqb_command@instance: database.rescan.wipecache + sqb_read@instance: true + sqb_write@instance: true + sqb_custom1@instance: '' totalgenres: type: num - sqb_command: database.totalgenres - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: database.totalgenres + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - database - sqb_read_initial: true - sqb_custom1: '' + sqb_read_initial@instance: true + sqb_custom1@instance: '' totalduration: type: num - sqb_command: database.totalduration - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: database.totalduration + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - database - sqb_read_initial: true - sqb_custom1: '' + sqb_read_initial@instance: true + sqb_custom1@instance: '' duration_format: type: str @@ -354,107 +353,107 @@ item_structs: totalartists: type: num - sqb_command: database.totalartists - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: database.totalartists + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - database - sqb_read_initial: true - sqb_custom1: '' + sqb_read_initial@instance: true + sqb_custom1@instance: '' totalalbums: type: num - sqb_command: database.totalalbums - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: database.totalalbums + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - database - sqb_read_initial: true - sqb_custom1: '' + sqb_read_initial@instance: true + sqb_custom1@instance: '' totalsongs: type: num - sqb_command: database.totalsongs - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: database.totalsongs + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - database - sqb_read_initial: true - sqb_custom1: '' + sqb_read_initial@instance: true + sqb_custom1@instance: '' player: read: type: bool enforce_updates: true - sqb_read_group_trigger: player + sqb_read_group_trigger@instance: player control: read: type: bool enforce_updates: true - sqb_read_group_trigger: player.control + sqb_read_group_trigger@instance: player.control power: type: bool - sqb_command: player.control.power - sqb_read: true - sqb_write: true - sqb_read_group: + sqb_command@instance: player.control.power + sqb_read@instance: true + sqb_write@instance: true + sqb_read_group@instance: - player - player.control enforce_updates: true playmode: type: str - sqb_command: player.control.playmode - sqb_read: true - sqb_write: true - sqb_read_group: + sqb_command@instance: player.control.playmode + sqb_read@instance: true + sqb_write@instance: true + sqb_read_group@instance: - player - player.control enforce_updates: true playpause: type: bool - sqb_command: player.control.playpause - sqb_read: true - sqb_write: true + sqb_command@instance: player.control.playpause + sqb_read@instance: true + sqb_write@instance: true enforce_updates: true stop: type: bool - sqb_command: player.control.stop - sqb_read: true - sqb_write: true + sqb_command@instance: player.control.stop + sqb_read@instance: true + sqb_write@instance: true enforce_updates: true mute: type: bool - sqb_command: player.control.mute - sqb_read: true - sqb_write: true - sqb_read_group: + sqb_command@instance: player.control.mute + sqb_read@instance: true + sqb_write@instance: true + sqb_read_group@instance: - player - player.control enforce_updates: true - sqb_read_initial: true + sqb_read_initial@instance: true volume: type: num - sqb_command: player.control.volume - sqb_read: true - sqb_write: true - sqb_read_group: + sqb_command@instance: player.control.volume + sqb_read@instance: true + sqb_write@instance: true + sqb_read_group@instance: - player - player.control volume_fading: type: num - sqb_command: player.control.volume_fading - sqb_read: false - sqb_write: true + sqb_command@instance: player.control.volume_fading + sqb_read@instance: false + sqb_write@instance: true goal: type: num @@ -463,56 +462,56 @@ item_structs: volume_low: type: num - sqb_command: player.control.volume_low - sqb_read: false - sqb_write: true + sqb_command@instance: player.control.volume_low + sqb_read@instance: false + sqb_write@instance: true cache: true enforce_updates: true initial_value: 60 volume_high: type: num - sqb_command: player.control.volume_high - sqb_read: false - sqb_write: true + sqb_command@instance: player.control.volume_high + sqb_read@instance: false + sqb_write@instance: true cache: true enforce_updates: true initial_value: 80 volumeup: type: num - sqb_command: player.control.volumeup - sqb_read: false - sqb_write: true + sqb_command@instance: player.control.volumeup + sqb_read@instance: false + sqb_write@instance: true cache: true enforce_updates: true initial_value: 1 volumedown: type: num - sqb_command: player.control.volumedown - sqb_read: false - sqb_write: true + sqb_command@instance: player.control.volumedown + sqb_read@instance: false + sqb_write@instance: true cache: true enforce_updates: true initial_value: 1 set_alarm: type: str - sqb_command: player.control.set_alarm - sqb_read: true - sqb_write: true + sqb_command@instance: player.control.set_alarm + sqb_read@instance: true + sqb_write@instance: true alarms: type: dict - sqb_command: player.control.alarms - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: player.control.alarms + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - player - player.control - player.control.alarms - sqb_read_initial: true + sqb_read_initial@instance: true query: type: bool @@ -521,51 +520,51 @@ item_structs: sync: type: str - sqb_command: player.control.sync - sqb_read: true - sqb_write: true - sqb_read_group: + sqb_command@instance: player.control.sync + sqb_read@instance: true + sqb_write@instance: true + sqb_read_group@instance: - player - player.control - sqb_read_initial: true + sqb_read_initial@instance: true unsync: type: bool - sqb_command: player.control.unsync - sqb_read: false - sqb_write: true + sqb_command@instance: player.control.unsync + sqb_read@instance: false + sqb_write@instance: true autotimer: 1s = 0 display: type: str - sqb_command: player.control.display - sqb_read: true - sqb_write: true - sqb_read_group: + sqb_command@instance: player.control.display + sqb_read@instance: true + sqb_write@instance: true + sqb_read_group@instance: - player - player.control - sqb_read_initial: true + sqb_read_initial@instance: true connect: type: str - sqb_command: player.control.connect - sqb_read: true - sqb_write: true + sqb_command@instance: player.control.connect + sqb_read@instance: true + sqb_write@instance: true remark: ip|www.mysqueezebox.com|www.test.mysqueezebox.com disconnect: type: str - sqb_command: player.control.disconnect - sqb_read: true - sqb_write: true + sqb_command@instance: player.control.disconnect + sqb_read@instance: true + sqb_write@instance: true remark: ip|www.mysqueezebox.com|www.test.mysqueezebox.com time: type: num - sqb_command: player.control.time - sqb_read: true - sqb_write: true - sqb_read_group: + sqb_command@instance: player.control.time + sqb_read@instance: true + sqb_write@instance: true + sqb_read_group@instance: - player - player.control - player.control.time_poll @@ -580,235 +579,235 @@ item_structs: forward: type: num - sqb_command: player.control.forward - sqb_read: true - sqb_write: true + sqb_command@instance: player.control.forward + sqb_read@instance: true + sqb_write@instance: true enforce_updates: true initial_value: 10 rewind: type: num - sqb_command: player.control.rewind - sqb_read: true - sqb_write: true + sqb_command@instance: player.control.rewind + sqb_read@instance: true + sqb_write@instance: true enforce_updates: true initial_value: 10 playsong: type: str - sqb_command: player.control.playsong - sqb_read: false - sqb_write: true + sqb_command@instance: player.control.playsong + sqb_read@instance: false + sqb_write@instance: true remark: song URL, playlist or directory sleep: type: num - sqb_command: player.control.sleep - sqb_read: true - sqb_write: true - sqb_read_group: + sqb_command@instance: player.control.sleep + sqb_read@instance: true + sqb_write@instance: true + sqb_read_group@instance: - player - player.control - sqb_read_initial: true + sqb_read_initial@instance: true playlist: read: type: bool enforce_updates: true - sqb_read_group_trigger: player.playlist + sqb_read_group_trigger@instance: player.playlist rename: type: str - sqb_command: player.playlist.rename - sqb_read: false - sqb_write: false + sqb_command@instance: player.playlist.rename + sqb_read@instance: false + sqb_write@instance: false repeat: type: str - sqb_command: player.playlist.repeat - sqb_read: true - sqb_write: true - sqb_read_group: + sqb_command@instance: player.playlist.repeat + sqb_read@instance: true + sqb_write@instance: true + sqb_read_group@instance: - player - player.playlist remark: 0 = Off, 1 = Song, 2 = Playlist lookup: type: list - sqb_lookup: REPEAT#list + sqb_lookup@instance: REPEAT#list shuffle: type: str - sqb_command: player.playlist.shuffle - sqb_read: true - sqb_write: true - sqb_read_group: + sqb_command@instance: player.playlist.shuffle + sqb_read@instance: true + sqb_write@instance: true + sqb_read_group@instance: - player - player.playlist remark: 0 = Off, 1 = Song, 2 = Album lookup: type: list - sqb_lookup: SHUFFLE#list + sqb_lookup@instance: SHUFFLE#list index: type: str - sqb_command: player.playlist.index - sqb_read: true - sqb_write: true - sqb_read_group: + sqb_command@instance: player.playlist.index + sqb_read@instance: true + sqb_write@instance: true + sqb_read_group@instance: - player - player.playlist - sqb_read_initial: true + sqb_read_initial@instance: true name: type: str - sqb_command: player.playlist.name - sqb_read: true - sqb_write: true - sqb_read_group: + sqb_command@instance: player.playlist.name + sqb_read@instance: true + sqb_write@instance: true + sqb_read_group@instance: - player - player.playlist - sqb_read_initial: true + sqb_read_initial@instance: true id: type: num - sqb_command: player.playlist.id - sqb_read: true - sqb_write: true - sqb_read_group: + sqb_command@instance: player.playlist.id + sqb_read@instance: true + sqb_write@instance: true + sqb_read_group@instance: - player - player.playlist save: type: str - sqb_command: player.playlist.save - sqb_read: true - sqb_write: true + sqb_command@instance: player.playlist.save + sqb_read@instance: true + sqb_write@instance: true enforce_updates: true load: type: str - sqb_command: player.playlist.load - sqb_read: true - sqb_write: true + sqb_command@instance: player.playlist.load + sqb_read@instance: true + sqb_write@instance: true enforce_updates: true loadalbum: type: str - sqb_command: player.playlist.loadalbum - sqb_read: true - sqb_write: true + sqb_command@instance: player.playlist.loadalbum + sqb_read@instance: true + sqb_write@instance: true enforce_updates: true loadtracks: type: str - sqb_command: player.playlist.loadtracks - sqb_read: true - sqb_write: true + sqb_command@instance: player.playlist.loadtracks + sqb_read@instance: true + sqb_write@instance: true enforce_updates: true add: type: str - sqb_command: player.playlist.add - sqb_read: true - sqb_write: true + sqb_command@instance: player.playlist.add + sqb_read@instance: true + sqb_write@instance: true enforce_updates: true addalbum: type: str - sqb_command: player.playlist.addalbum - sqb_read: true - sqb_write: true + sqb_command@instance: player.playlist.addalbum + sqb_read@instance: true + sqb_write@instance: true enforce_updates: true addtracks: type: str - sqb_command: player.playlist.addtracks - sqb_read: true - sqb_write: true + sqb_command@instance: player.playlist.addtracks + sqb_read@instance: true + sqb_write@instance: true enforce_updates: true insertalbum: type: str - sqb_command: player.playlist.insertalbum - sqb_read: true - sqb_write: true + sqb_command@instance: player.playlist.insertalbum + sqb_read@instance: true + sqb_write@instance: true enforce_updates: true inserttracks: type: str - sqb_command: player.playlist.inserttracks - sqb_read: true - sqb_write: true + sqb_command@instance: player.playlist.inserttracks + sqb_read@instance: true + sqb_write@instance: true enforce_updates: true tracks: type: num - sqb_command: player.playlist.tracks - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: player.playlist.tracks + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - player - player.playlist clear: type: bool - sqb_command: player.playlist.clear - sqb_read: true - sqb_write: true + sqb_command@instance: player.playlist.clear + sqb_read@instance: true + sqb_write@instance: true enforce_updates: true eval: True if value else None delete: type: str - sqb_command: player.playlist.delete - sqb_read: true - sqb_write: true + sqb_command@instance: player.playlist.delete + sqb_read@instance: true + sqb_write@instance: true enforce_updates: true deleteitem: type: str - sqb_command: player.playlist.deleteitem - sqb_read: true - sqb_write: true + sqb_command@instance: player.playlist.deleteitem + sqb_read@instance: true + sqb_write@instance: true enforce_updates: true deletealbum: type: str - sqb_command: player.playlist.deletealbum - sqb_read: true - sqb_write: true + sqb_command@instance: player.playlist.deletealbum + sqb_read@instance: true + sqb_write@instance: true enforce_updates: true preview: type: str - sqb_command: player.playlist.preview - sqb_read: true - sqb_write: true + sqb_command@instance: player.playlist.preview + sqb_read@instance: true + sqb_write@instance: true next: type: num - sqb_command: player.playlist.next - sqb_read: false - sqb_write: true + sqb_command@instance: player.playlist.next + sqb_read@instance: false + sqb_write@instance: true enforce_updates: true initial_value: 1 previous: type: num - sqb_command: player.playlist.previous - sqb_read: false - sqb_write: true + sqb_command@instance: player.playlist.previous + sqb_read@instance: false + sqb_write@instance: true enforce_updates: true initial_value: 1 customskip: type: str - sqb_command: player.playlist.customskip - sqb_read: false - sqb_write: true + sqb_command@instance: player.playlist.customskip + sqb_read@instance: false + sqb_write@instance: true cache: true info: @@ -816,160 +815,160 @@ item_structs: read: type: bool enforce_updates: true - sqb_read_group_trigger: player.info + sqb_read_group_trigger@instance: player.info playlists: read: type: bool enforce_updates: true - sqb_read_group_trigger: player.info.playlists + sqb_read_group_trigger@instance: player.info.playlists count: type: num - sqb_command: player.info.playlists.count - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: player.info.playlists.count + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - player - player.info - player.info.playlists - sqb_read_initial: true + sqb_read_initial@instance: true names: type: dict - sqb_command: player.info.playlists.names - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: player.info.playlists.names + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - player - player.info - player.info.playlists - sqb_read_initial: true + sqb_read_initial@instance: true status: type: str - sqb_command: player.info.status - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: player.info.status + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - player - player.info - sqb_read_initial: true + sqb_read_initial@instance: true connected: type: bool - sqb_command: player.info.connected - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: player.info.connected + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - player - player.info ip: type: str - sqb_command: player.info.ip - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: player.info.ip + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - player - player.info name: type: str - sqb_command: player.info.name - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: player.info.name + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - player - player.info syncgroups: type: num - sqb_command: player.info.syncgroups - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: player.info.syncgroups + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - player - player.info - sqb_read_initial: true + sqb_read_initial@instance: true signalstrength: type: num - sqb_command: player.info.signalstrength - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: player.info.signalstrength + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - player - player.info genre: type: str - sqb_command: player.info.genre - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: player.info.genre + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - player - player.info artist: type: str - sqb_command: player.info.artist - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: player.info.artist + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - player - player.info album: type: str - sqb_command: player.info.album - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: player.info.album + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - player - player.info - sqb_read_initial: true + sqb_read_initial@instance: true title: type: str - sqb_command: player.info.title - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: player.info.title + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - player - player.info - sqb_read_initial: true + sqb_read_initial@instance: true path: type: str - sqb_command: player.info.path - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: player.info.path + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - player - player.info duration: type: num - sqb_command: player.info.duration - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: player.info.duration + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - player - player.info trackstat: type: str - sqb_command: player.info.trackstat - sqb_read: true - sqb_write: false + sqb_command@instance: player.info.trackstat + sqb_read@instance: true + sqb_write@instance: false albumarturl: type: str - sqb_command: player.info.albumarturl - sqb_read: true - sqb_write: false + sqb_command@instance: player.info.albumarturl + sqb_read@instance: true + sqb_write@instance: false remark: This item gets automatically defined and overwritten based on (web_)host and web_port ALL: @@ -977,139 +976,139 @@ item_structs: read: type: bool enforce_updates: true - sqb_read_group_trigger: ALL + sqb_read_group_trigger@instance: ALL server: read: type: bool enforce_updates: true - sqb_read_group_trigger: ALL.server + sqb_read_group_trigger@instance: ALL.server listenmode: type: bool - sqb_command: server.listenmode - sqb_read: true - sqb_write: true - sqb_custom1: '' + sqb_command@instance: server.listenmode + sqb_read@instance: true + sqb_write@instance: true + sqb_custom1@instance: '' playercount: type: num - sqb_command: server.playercount - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: server.playercount + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - ALL - ALL.server - sqb_read_initial: true - sqb_custom1: '' + sqb_read_initial@instance: true + sqb_custom1@instance: '' favoritescount: type: num - sqb_command: server.favoritescount - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: server.favoritescount + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - ALL - ALL.server - sqb_read_initial: true - sqb_custom1: '' + sqb_read_initial@instance: true + sqb_custom1@instance: '' database: read: type: bool enforce_updates: true - sqb_read_group_trigger: ALL.database + sqb_read_group_trigger@instance: ALL.database rescan: read: type: bool enforce_updates: true - sqb_read_group_trigger: ALL.database.rescan + sqb_read_group_trigger@instance: ALL.database.rescan start: type: str - sqb_command: database.rescan.start - sqb_read: false - sqb_write: true - sqb_custom1: '' + 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: type: bool - sqb_command: database.rescan.running - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: database.rescan.running + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - ALL - ALL.database - ALL.database.rescan - sqb_read_initial: true - sqb_read_cycle: '120' - sqb_custom1: '' + sqb_read_initial@instance: true + sqb_read_cycle@instance: '120' + sqb_custom1@instance: '' progress: type: str - sqb_command: database.rescan.progress - sqb_read: true - sqb_write: false - sqb_custom1: '' + sqb_command@instance: database.rescan.progress + sqb_read@instance: true + sqb_write@instance: false + sqb_custom1@instance: '' runningtime: type: str - sqb_command: database.rescan.runningtime - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: database.rescan.runningtime + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - ALL - ALL.database - ALL.database.rescan - sqb_custom1: '' + sqb_custom1@instance: '' fail: type: str - sqb_command: database.rescan.fail - sqb_read: true - sqb_write: false - sqb_custom1: '' + sqb_command@instance: database.rescan.fail + sqb_read@instance: true + sqb_write@instance: false + sqb_custom1@instance: '' abortscan: type: bool - sqb_command: database.rescan.abortscan - sqb_read: true - sqb_write: true - sqb_custom1: '' + sqb_command@instance: database.rescan.abortscan + sqb_read@instance: true + sqb_write@instance: true + sqb_custom1@instance: '' wipecache: type: bool - sqb_command: database.rescan.wipecache - sqb_read: true - sqb_write: true - sqb_custom1: '' + sqb_command@instance: database.rescan.wipecache + sqb_read@instance: true + sqb_write@instance: true + sqb_custom1@instance: '' totalgenres: type: num - sqb_command: database.totalgenres - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: database.totalgenres + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - ALL - ALL.database - sqb_read_initial: true - sqb_custom1: '' + sqb_read_initial@instance: true + sqb_custom1@instance: '' totalduration: type: num - sqb_command: database.totalduration - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: database.totalduration + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - ALL - ALL.database - sqb_read_initial: true - sqb_custom1: '' + sqb_read_initial@instance: true + sqb_custom1@instance: '' duration_format: type: str @@ -1118,57 +1117,57 @@ item_structs: totalartists: type: num - sqb_command: database.totalartists - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: database.totalartists + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - ALL - ALL.database - sqb_read_initial: true - sqb_custom1: '' + sqb_read_initial@instance: true + sqb_custom1@instance: '' totalalbums: type: num - sqb_command: database.totalalbums - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: database.totalalbums + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - ALL - ALL.database - sqb_read_initial: true - sqb_custom1: '' + sqb_read_initial@instance: true + sqb_custom1@instance: '' totalsongs: type: num - sqb_command: database.totalsongs - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: database.totalsongs + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - ALL - ALL.database - sqb_read_initial: true - sqb_custom1: '' + sqb_read_initial@instance: true + sqb_custom1@instance: '' player: read: type: bool enforce_updates: true - sqb_read_group_trigger: ALL.player + sqb_read_group_trigger@instance: ALL.player control: read: type: bool enforce_updates: true - sqb_read_group_trigger: ALL.player.control + sqb_read_group_trigger@instance: ALL.player.control power: type: bool - sqb_command: player.control.power - sqb_read: true - sqb_write: true - sqb_read_group: + sqb_command@instance: player.control.power + sqb_read@instance: true + sqb_write@instance: true + sqb_read_group@instance: - ALL - ALL.player - ALL.player.control @@ -1176,10 +1175,10 @@ item_structs: playmode: type: str - sqb_command: player.control.playmode - sqb_read: true - sqb_write: true - sqb_read_group: + sqb_command@instance: player.control.playmode + sqb_read@instance: true + sqb_write@instance: true + sqb_read_group@instance: - ALL - ALL.player - ALL.player.control @@ -1187,45 +1186,45 @@ item_structs: playpause: type: bool - sqb_command: player.control.playpause - sqb_read: true - sqb_write: true + sqb_command@instance: player.control.playpause + sqb_read@instance: true + sqb_write@instance: true enforce_updates: true stop: type: bool - sqb_command: player.control.stop - sqb_read: true - sqb_write: true + sqb_command@instance: player.control.stop + sqb_read@instance: true + sqb_write@instance: true enforce_updates: true mute: type: bool - sqb_command: player.control.mute - sqb_read: true - sqb_write: true - sqb_read_group: + sqb_command@instance: player.control.mute + sqb_read@instance: true + sqb_write@instance: true + sqb_read_group@instance: - ALL - ALL.player - ALL.player.control enforce_updates: true - sqb_read_initial: true + sqb_read_initial@instance: true volume: type: num - sqb_command: player.control.volume - sqb_read: true - sqb_write: true - sqb_read_group: + sqb_command@instance: player.control.volume + sqb_read@instance: true + sqb_write@instance: true + sqb_read_group@instance: - ALL - ALL.player - ALL.player.control volume_fading: type: num - sqb_command: player.control.volume_fading - sqb_read: false - sqb_write: true + sqb_command@instance: player.control.volume_fading + sqb_read@instance: false + sqb_write@instance: true goal: type: num @@ -1234,57 +1233,57 @@ item_structs: volume_low: type: num - sqb_command: player.control.volume_low - sqb_read: false - sqb_write: true + sqb_command@instance: player.control.volume_low + sqb_read@instance: false + sqb_write@instance: true cache: true enforce_updates: true initial_value: 60 volume_high: type: num - sqb_command: player.control.volume_high - sqb_read: false - sqb_write: true + sqb_command@instance: player.control.volume_high + sqb_read@instance: false + sqb_write@instance: true cache: true enforce_updates: true initial_value: 80 volumeup: type: num - sqb_command: player.control.volumeup - sqb_read: false - sqb_write: true + sqb_command@instance: player.control.volumeup + sqb_read@instance: false + sqb_write@instance: true cache: true enforce_updates: true initial_value: 1 volumedown: type: num - sqb_command: player.control.volumedown - sqb_read: false - sqb_write: true + sqb_command@instance: player.control.volumedown + sqb_read@instance: false + sqb_write@instance: true cache: true enforce_updates: true initial_value: 1 set_alarm: type: str - sqb_command: player.control.set_alarm - sqb_read: true - sqb_write: true + sqb_command@instance: player.control.set_alarm + sqb_read@instance: true + sqb_write@instance: true alarms: type: dict - sqb_command: player.control.alarms - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: player.control.alarms + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - ALL - ALL.player - ALL.player.control - player.control.alarms - sqb_read_initial: true + sqb_read_initial@instance: true query: type: bool @@ -1293,53 +1292,53 @@ item_structs: sync: type: str - sqb_command: player.control.sync - sqb_read: true - sqb_write: true - sqb_read_group: + sqb_command@instance: player.control.sync + sqb_read@instance: true + sqb_write@instance: true + sqb_read_group@instance: - ALL - ALL.player - ALL.player.control - sqb_read_initial: true + sqb_read_initial@instance: true unsync: type: bool - sqb_command: player.control.unsync - sqb_read: false - sqb_write: true + sqb_command@instance: player.control.unsync + sqb_read@instance: false + sqb_write@instance: true autotimer: 1s = 0 display: type: str - sqb_command: player.control.display - sqb_read: true - sqb_write: true - sqb_read_group: + sqb_command@instance: player.control.display + sqb_read@instance: true + sqb_write@instance: true + sqb_read_group@instance: - ALL - ALL.player - ALL.player.control - sqb_read_initial: true + sqb_read_initial@instance: true connect: type: str - sqb_command: player.control.connect - sqb_read: true - sqb_write: true + sqb_command@instance: player.control.connect + sqb_read@instance: true + sqb_write@instance: true remark: ip|www.mysqueezebox.com|www.test.mysqueezebox.com disconnect: type: str - sqb_command: player.control.disconnect - sqb_read: true - sqb_write: true + sqb_command@instance: player.control.disconnect + sqb_read@instance: true + sqb_write@instance: true remark: ip|www.mysqueezebox.com|www.test.mysqueezebox.com time: type: num - sqb_command: player.control.time - sqb_read: true - sqb_write: true - sqb_read_group: + sqb_command@instance: player.control.time + sqb_read@instance: true + sqb_write@instance: true + sqb_read_group@instance: - ALL - ALL.player - ALL.player.control @@ -1355,57 +1354,57 @@ item_structs: forward: type: num - sqb_command: player.control.forward - sqb_read: true - sqb_write: true + sqb_command@instance: player.control.forward + sqb_read@instance: true + sqb_write@instance: true enforce_updates: true initial_value: 10 rewind: type: num - sqb_command: player.control.rewind - sqb_read: true - sqb_write: true + sqb_command@instance: player.control.rewind + sqb_read@instance: true + sqb_write@instance: true enforce_updates: true initial_value: 10 playsong: type: str - sqb_command: player.control.playsong - sqb_read: false - sqb_write: true + sqb_command@instance: player.control.playsong + sqb_read@instance: false + sqb_write@instance: true remark: song URL, playlist or directory sleep: type: num - sqb_command: player.control.sleep - sqb_read: true - sqb_write: true - sqb_read_group: + sqb_command@instance: player.control.sleep + sqb_read@instance: true + sqb_write@instance: true + sqb_read_group@instance: - ALL - ALL.player - ALL.player.control - sqb_read_initial: true + sqb_read_initial@instance: true playlist: read: type: bool enforce_updates: true - sqb_read_group_trigger: ALL.player.playlist + sqb_read_group_trigger@instance: ALL.player.playlist rename: type: str - sqb_command: player.playlist.rename - sqb_read: false - sqb_write: false + sqb_command@instance: player.playlist.rename + sqb_read@instance: false + sqb_write@instance: false repeat: type: str - sqb_command: player.playlist.repeat - sqb_read: true - sqb_write: true - sqb_read_group: + sqb_command@instance: player.playlist.repeat + sqb_read@instance: true + sqb_write@instance: true + sqb_read_group@instance: - ALL - ALL.player - ALL.player.playlist @@ -1413,14 +1412,14 @@ item_structs: lookup: type: list - sqb_lookup: REPEAT#list + sqb_lookup@instance: REPEAT#list shuffle: type: str - sqb_command: player.playlist.shuffle - sqb_read: true - sqb_write: true - sqb_read_group: + sqb_command@instance: player.playlist.shuffle + sqb_read@instance: true + sqb_write@instance: true + sqb_read_group@instance: - ALL - ALL.player - ALL.player.playlist @@ -1428,169 +1427,169 @@ item_structs: lookup: type: list - sqb_lookup: SHUFFLE#list + sqb_lookup@instance: SHUFFLE#list index: type: str - sqb_command: player.playlist.index - sqb_read: true - sqb_write: true - sqb_read_group: + sqb_command@instance: player.playlist.index + sqb_read@instance: true + sqb_write@instance: true + sqb_read_group@instance: - ALL - ALL.player - ALL.player.playlist - sqb_read_initial: true + sqb_read_initial@instance: true name: type: str - sqb_command: player.playlist.name - sqb_read: true - sqb_write: true - sqb_read_group: + sqb_command@instance: player.playlist.name + sqb_read@instance: true + sqb_write@instance: true + sqb_read_group@instance: - ALL - ALL.player - ALL.player.playlist - sqb_read_initial: true + sqb_read_initial@instance: true id: type: num - sqb_command: player.playlist.id - sqb_read: true - sqb_write: true - sqb_read_group: + sqb_command@instance: player.playlist.id + sqb_read@instance: true + sqb_write@instance: true + sqb_read_group@instance: - ALL - ALL.player - ALL.player.playlist save: type: str - sqb_command: player.playlist.save - sqb_read: true - sqb_write: true + sqb_command@instance: player.playlist.save + sqb_read@instance: true + sqb_write@instance: true enforce_updates: true load: type: str - sqb_command: player.playlist.load - sqb_read: true - sqb_write: true + sqb_command@instance: player.playlist.load + sqb_read@instance: true + sqb_write@instance: true enforce_updates: true loadalbum: type: str - sqb_command: player.playlist.loadalbum - sqb_read: true - sqb_write: true + sqb_command@instance: player.playlist.loadalbum + sqb_read@instance: true + sqb_write@instance: true enforce_updates: true loadtracks: type: str - sqb_command: player.playlist.loadtracks - sqb_read: true - sqb_write: true + sqb_command@instance: player.playlist.loadtracks + sqb_read@instance: true + sqb_write@instance: true enforce_updates: true add: type: str - sqb_command: player.playlist.add - sqb_read: true - sqb_write: true + sqb_command@instance: player.playlist.add + sqb_read@instance: true + sqb_write@instance: true enforce_updates: true addalbum: type: str - sqb_command: player.playlist.addalbum - sqb_read: true - sqb_write: true + sqb_command@instance: player.playlist.addalbum + sqb_read@instance: true + sqb_write@instance: true enforce_updates: true addtracks: type: str - sqb_command: player.playlist.addtracks - sqb_read: true - sqb_write: true + sqb_command@instance: player.playlist.addtracks + sqb_read@instance: true + sqb_write@instance: true enforce_updates: true insertalbum: type: str - sqb_command: player.playlist.insertalbum - sqb_read: true - sqb_write: true + sqb_command@instance: player.playlist.insertalbum + sqb_read@instance: true + sqb_write@instance: true enforce_updates: true inserttracks: type: str - sqb_command: player.playlist.inserttracks - sqb_read: true - sqb_write: true + sqb_command@instance: player.playlist.inserttracks + sqb_read@instance: true + sqb_write@instance: true enforce_updates: true tracks: type: num - sqb_command: player.playlist.tracks - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: player.playlist.tracks + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - ALL - ALL.player - ALL.player.playlist clear: type: bool - sqb_command: player.playlist.clear - sqb_read: true - sqb_write: true + sqb_command@instance: player.playlist.clear + sqb_read@instance: true + sqb_write@instance: true enforce_updates: true eval: True if value else None delete: type: str - sqb_command: player.playlist.delete - sqb_read: true - sqb_write: true + sqb_command@instance: player.playlist.delete + sqb_read@instance: true + sqb_write@instance: true enforce_updates: true deleteitem: type: str - sqb_command: player.playlist.deleteitem - sqb_read: true - sqb_write: true + sqb_command@instance: player.playlist.deleteitem + sqb_read@instance: true + sqb_write@instance: true enforce_updates: true deletealbum: type: str - sqb_command: player.playlist.deletealbum - sqb_read: true - sqb_write: true + sqb_command@instance: player.playlist.deletealbum + sqb_read@instance: true + sqb_write@instance: true enforce_updates: true preview: type: str - sqb_command: player.playlist.preview - sqb_read: true - sqb_write: true + sqb_command@instance: player.playlist.preview + sqb_read@instance: true + sqb_write@instance: true next: type: num - sqb_command: player.playlist.next - sqb_read: false - sqb_write: true + sqb_command@instance: player.playlist.next + sqb_read@instance: false + sqb_write@instance: true enforce_updates: true initial_value: 1 previous: type: num - sqb_command: player.playlist.previous - sqb_read: false - sqb_write: true + sqb_command@instance: player.playlist.previous + sqb_read@instance: false + sqb_write@instance: true enforce_updates: true initial_value: 1 customskip: type: str - sqb_command: player.playlist.customskip - sqb_read: false - sqb_write: true + sqb_command@instance: player.playlist.customskip + sqb_read@instance: false + sqb_write@instance: true cache: true info: @@ -1598,174 +1597,174 @@ item_structs: read: type: bool enforce_updates: true - sqb_read_group_trigger: ALL.player.info + sqb_read_group_trigger@instance: ALL.player.info playlists: read: type: bool enforce_updates: true - sqb_read_group_trigger: ALL.player.info.playlists + sqb_read_group_trigger@instance: ALL.player.info.playlists count: type: num - sqb_command: player.info.playlists.count - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: player.info.playlists.count + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - ALL - ALL.player - ALL.player.info - ALL.player.info.playlists - sqb_read_initial: true + sqb_read_initial@instance: true names: type: dict - sqb_command: player.info.playlists.names - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: player.info.playlists.names + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - ALL - ALL.player - ALL.player.info - ALL.player.info.playlists - sqb_read_initial: true + sqb_read_initial@instance: true status: type: str - sqb_command: player.info.status - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: player.info.status + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - ALL - ALL.player - ALL.player.info - sqb_read_initial: true + sqb_read_initial@instance: true connected: type: bool - sqb_command: player.info.connected - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: player.info.connected + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - ALL - ALL.player - ALL.player.info ip: type: str - sqb_command: player.info.ip - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: player.info.ip + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - ALL - ALL.player - ALL.player.info name: type: str - sqb_command: player.info.name - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: player.info.name + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - ALL - ALL.player - ALL.player.info syncgroups: type: num - sqb_command: player.info.syncgroups - sqb_read: true - sqb_write: false - sqb_read_group: + 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: true + sqb_read_initial@instance: true signalstrength: type: num - sqb_command: player.info.signalstrength - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: player.info.signalstrength + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - ALL - ALL.player - ALL.player.info genre: type: str - sqb_command: player.info.genre - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: player.info.genre + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - ALL - ALL.player - ALL.player.info artist: type: str - sqb_command: player.info.artist - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: player.info.artist + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - ALL - ALL.player - ALL.player.info album: type: str - sqb_command: player.info.album - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: player.info.album + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - ALL - ALL.player - ALL.player.info - sqb_read_initial: true + sqb_read_initial@instance: true title: type: str - sqb_command: player.info.title - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: player.info.title + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - ALL - ALL.player - ALL.player.info - sqb_read_initial: true + sqb_read_initial@instance: true path: type: str - sqb_command: player.info.path - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: player.info.path + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - ALL - ALL.player - ALL.player.info duration: type: num - sqb_command: player.info.duration - sqb_read: true - sqb_write: false - sqb_read_group: + sqb_command@instance: player.info.duration + sqb_read@instance: true + sqb_write@instance: false + sqb_read_group@instance: - ALL - ALL.player - ALL.player.info trackstat: type: str - sqb_command: player.info.trackstat - sqb_read: true - sqb_write: false + sqb_command@instance: player.info.trackstat + sqb_read@instance: true + sqb_write@instance: false albumarturl: type: str - sqb_command: player.info.albumarturl - sqb_read: true - sqb_write: false + sqb_command@instance: player.info.albumarturl + sqb_read@instance: true + sqb_write@instance: false remark: This item gets automatically defined and overwritten based on (web_)host and web_port plugin_functions: NONE logic_parameters: NONE From f9b31b8dfac7df1c0f364fdf6d7a48d723008f0a Mon Sep 17 00:00:00 2001 From: ivande Date: Sun, 2 Jun 2024 06:44:14 +0200 Subject: [PATCH 10/23] [modbus_tcp plugin]: fixed issue with writing without modBusObjectType --- modbus_tcp/__init__.py | 4 ++-- modbus_tcp/user_doc.rst | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/modbus_tcp/__init__.py b/modbus_tcp/__init__.py index 073e78697..96a796b38 100755 --- a/modbus_tcp/__init__.py +++ b/modbus_tcp/__init__.py @@ -391,8 +391,8 @@ def update_item(self, item, caller=None, source=None, dest=None): self._slaveUnitRegisterDependend = True if self.has_iattr(item.conf, AttrObjectType): objectType = self.get_iattr_value(item.conf, AttrObjectType) - else: - return + # else: + # self.logger.debug(f'update_item:{item} default modBusObjectTyp: {objectType}') reg = str(objectType) # Dict-key: HoldingRegister.528.1 *** objectType.regAddr.slaveUnit *** reg += '.' diff --git a/modbus_tcp/user_doc.rst b/modbus_tcp/user_doc.rst index 360e393cb..48ed946e6 100755 --- a/modbus_tcp/user_doc.rst +++ b/modbus_tcp/user_doc.rst @@ -164,7 +164,8 @@ siehe auch example.yaml Changelog --------- -V1.0.12 bei wiederholten Verbindungsproblemen Ausgabe vom Logger reduziert +V1.0.12 Problem beim Schreiben ohne modBusObjectTyp behoben + bei wiederholten Verbindungsproblemen Ausgabe vom Logger reduziert Verbindungstop mit supend/resume steuerbar V1.0.11 Verbesserung Umwandlung Byte/Wordorder in Endian-Konstante From f488bdb9ff5cbeceb60be83213b1e62f33375f7d Mon Sep 17 00:00:00 2001 From: Onkel Andy Date: Sun, 2 Jun 2024 19:02:15 +0200 Subject: [PATCH 11/23] knx plugin: improve logging for sending and polling --- knx/__init__.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/knx/__init__.py b/knx/__init__.py index 1cdf3a172..d3abb5e75 100755 --- a/knx/__init__.py +++ b/knx/__init__.py @@ -88,7 +88,9 @@ def __init__(self, smarthome): self.date_ga = self.get_parameter_value('date_ga') self._send_time_do = self.get_parameter_value('send_time') self._bm_separatefile = False - self._bm_format = "BM': {1} set {2} to {3}" + self._bm_format = "BM: {1} set {2} to {3}" + self._bm_format_send = "BM: Sending value {3} for GA {2}" + self._bm_format_poll = "BM: Polling value for GA {2}" self._startup_polling = {} # following needed for statistics @@ -110,6 +112,8 @@ def __init__(self, smarthome): elif busmonitor.lower() == 'logger': self._bm_separatefile = True self._bm_format = "{0};{1};{2};{3}" + self._bm_format_send = "{0};{1};{2};{3}" + self._bm_format_poll = "{0};{1};{2}" self._busmonitor = logging.getLogger("knx_busmonitor").info self.logger.info(self.translate("Using busmonitor (L) = '{}'").format(busmonitor)) else: @@ -241,6 +245,8 @@ def _poll(self, **kwargs): item = 'unknown item' if 'ga' in kwargs: self.groupread(kwargs['ga']) + if self._log_own_packets is True: + self._busmonitor(self._bm_format_poll.format(self.get_instance_name(), 'POLL', kwargs["ga"])) else: self.logger.warning(self.translate('problem polling {}, no known ga').format(item)) @@ -662,10 +668,6 @@ def parse_item(self, item): randomwait = random.randrange(15) next = self.shtime.now() + timedelta(seconds=poll_interval + randomwait) self._startup_polling.update({item: {'ga': poll_ga, 'interval': poll_interval}}) - ''' - self._sh.scheduler.add(f'KNX poll {item}', self._poll, - value={ITEM: item, 'ga': poll_ga, 'interval': poll_interval}, next=next) - ''' else: self.logger.warning("Ignoring knx_poll for item {}: We need two parameters, one for the GA and one for the polling interval.".format(item)) pass @@ -737,14 +739,14 @@ def update_item(self, item, caller=None, source=None, dest=None): for ga in self.get_iattr_value(item.conf, KNX_SEND): _value = item() if self._log_own_packets is True: - self._busmonitor(self._bm_format.format(self.get_instance_name(), 'SEND', ga, _value)) + self._busmonitor(self._bm_format_send.format(self.get_instance_name(), 'SEND', ga, _value)) self.groupwrite(ga, _value, self.get_iattr_value(item.conf, KNX_DPT)) if self.has_iattr(item.conf, KNX_STATUS): for ga in self.get_iattr_value(item.conf, KNX_STATUS): # send status update if ga != dest: _value = item() if self._log_own_packets is True: - self._busmonitor(self._bm_format.format(self.get_instance_name(), 'STATUS', ga, _value)) + self._busmonitor(self._bm_format_send.format(self.get_instance_name(), 'STATUS', ga, _value)) self.groupwrite(ga, _value, self.get_iattr_value(item.conf, KNX_DPT)) From de29f4e2e9fafbc19da8cd2c36f5193f4e63baf4 Mon Sep 17 00:00:00 2001 From: ivande Date: Sun, 16 Jun 2024 22:44:59 +0200 Subject: [PATCH 12/23] [modbus_tcp plugin]: fixed startup issue with active suspend --- modbus_tcp/__init__.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/modbus_tcp/__init__.py b/modbus_tcp/__init__.py index 96a796b38..bc75596c2 100755 --- a/modbus_tcp/__init__.py +++ b/modbus_tcp/__init__.py @@ -113,7 +113,8 @@ def run(self): if self._cycle or self._crontab: self.error_count = 0 # Initialize error count - self._create_cyclic_scheduler() + if not self.suspended: + self._create_cyclic_scheduler() self.logger.debug(f"Plugin '{self.get_fullname()}': run method finished ") def _create_cyclic_scheduler(self): @@ -274,13 +275,7 @@ def poll_device(self): It is called by the scheduler which is set within run() method. """ if self.suspended: - if self.suspend_log_poll is None or self.suspend_log_poll is False: # debug - Nachricht nur 1x ausgeben - self.logger.info(f"poll suspended") - self.suspend_log_poll = True - self.error_count = 0 return - else: - self.suspend_log_poll = False with self.lock: try: From b45f6c298dbfde87f1dad3dcbdbc054ce827b18d Mon Sep 17 00:00:00 2001 From: Onkel Andy Date: Tue, 18 Jun 2024 21:37:19 +0200 Subject: [PATCH 13/23] knx plugin: only log poll when plugin is alive --- knx/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/knx/__init__.py b/knx/__init__.py index d3abb5e75..5eb53667c 100755 --- a/knx/__init__.py +++ b/knx/__init__.py @@ -245,7 +245,7 @@ def _poll(self, **kwargs): item = 'unknown item' if 'ga' in kwargs: self.groupread(kwargs['ga']) - if self._log_own_packets is True: + if self._log_own_packets is True and self.alive: self._busmonitor(self._bm_format_poll.format(self.get_instance_name(), 'POLL', kwargs["ga"])) else: self.logger.warning(self.translate('problem polling {}, no known ga').format(item)) From eb406f88b88633461a0c3d41c685a960d2c8fbdc Mon Sep 17 00:00:00 2001 From: bmxp Date: Sat, 22 Jun 2024 09:29:57 +0200 Subject: [PATCH 14/23] knx- and uzsu plugin: add super().__init__() to init --- knx/__init__.py | 5 +++++ uzsu/__init__.py | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/knx/__init__.py b/knx/__init__.py index 5eb53667c..6fbb2f17d 100755 --- a/knx/__init__.py +++ b/knx/__init__.py @@ -60,6 +60,11 @@ class KNX(SmartPlugin): PROVIDER_KNXMC = 'IP Router' def __init__(self, smarthome): + """Initializes the plugin.""" + + # call init code of parent class (SmartPlugin) + super().__init__() + self.provider = self.get_parameter_value('provider') self.host = self.get_parameter_value('host') self.port = self.get_parameter_value('port') diff --git a/uzsu/__init__.py b/uzsu/__init__.py index 1125c32df..c6483b9a4 100755 --- a/uzsu/__init__.py +++ b/uzsu/__init__.py @@ -108,6 +108,10 @@ def __init__(self, smarthome): Initializes the plugin. The parameters describe for this method are pulled from the entry in plugin.conf. :param smarthome: The instance of the smarthome object, save it for later references """ + + # call init code of parent class (SmartPlugin) + super().__init__() + self.itemsApi = Items.get_instance() self._timezone = Shtime.get_instance().tzinfo() self._remove_duplicates = self.get_parameter_value('remove_duplicates') From d7174db383d96abe4a7298d1047d58cd4efcf0d4 Mon Sep 17 00:00:00 2001 From: Morg42 <43153739+Morg42@users.noreply.github.com> Date: Thu, 27 Jun 2024 19:59:44 +0200 Subject: [PATCH 15/23] enocean: modify connect logics, include refactoring by hasenradball --- enocean/__init__.py | 1182 ++++++++--------- enocean/plugin.yaml | 21 +- enocean/protocol/__init__.py | 65 + enocean/protocol/constants.py | 164 +++ enocean/{ => protocol}/eep_parser.py | 232 ++-- .../packet_data.py} | 194 ++- 6 files changed, 996 insertions(+), 862 deletions(-) create mode 100644 enocean/protocol/__init__.py create mode 100644 enocean/protocol/constants.py rename enocean/{ => protocol}/eep_parser.py (71%) rename enocean/{prepare_packet_data.py => protocol/packet_data.py} (80%) diff --git a/enocean/__init__.py b/enocean/__init__.py index dcbfb629d..a6c425914 100755 --- a/enocean/__init__.py +++ b/enocean/__init__.py @@ -21,172 +21,45 @@ ######################################################################### import serial -import os -import sys -import struct -import time +import logging import threading -from lib.item import Items #what for? -from . import eep_parser -from . import prepare_packet_data -from lib.model.smartplugin import * +from time import sleep + +from lib.model.smartplugin import SmartPlugin +from lib.item import Items + +from .protocol import CRC +from .protocol.eep_parser import EEP_Parser +from .protocol.packet_data import Packet_Data +from .protocol.constants import ( + PACKET, PACKET_TYPE, COMMON_COMMAND, SMART_ACK, EVENT, RETURN_CODE, RORG +) from .webif import WebInterface -FCSTAB = [ - 0x00, 0x07, 0x0e, 0x09, 0x1c, 0x1b, 0x12, 0x15, - 0x38, 0x3f, 0x36, 0x31, 0x24, 0x23, 0x2a, 0x2d, - 0x70, 0x77, 0x7e, 0x79, 0x6c, 0x6b, 0x62, 0x65, - 0x48, 0x4f, 0x46, 0x41, 0x54, 0x53, 0x5a, 0x5d, - 0xe0, 0xe7, 0xee, 0xe9, 0xfc, 0xfb, 0xf2, 0xf5, - 0xd8, 0xdf, 0xd6, 0xd1, 0xc4, 0xc3, 0xca, 0xcd, - 0x90, 0x97, 0x9e, 0x99, 0x8c, 0x8b, 0x82, 0x85, - 0xa8, 0xaf, 0xa6, 0xa1, 0xb4, 0xb3, 0xba, 0xbd, - 0xc7, 0xc0, 0xc9, 0xce, 0xdb, 0xdc, 0xd5, 0xd2, - 0xff, 0xf8, 0xf1, 0xf6, 0xe3, 0xe4, 0xed, 0xea, - 0xb7, 0xb0, 0xb9, 0xbe, 0xab, 0xac, 0xa5, 0xa2, - 0x8f, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9d, 0x9a, - 0x27, 0x20, 0x29, 0x2e, 0x3b, 0x3c, 0x35, 0x32, - 0x1f, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0d, 0x0a, - 0x57, 0x50, 0x59, 0x5e, 0x4b, 0x4c, 0x45, 0x42, - 0x6f, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7d, 0x7a, - 0x89, 0x8e, 0x87, 0x80, 0x95, 0x92, 0x9b, 0x9c, - 0xb1, 0xb6, 0xbf, 0xb8, 0xad, 0xaa, 0xa3, 0xa4, - 0xf9, 0xfe, 0xf7, 0xf0, 0xe5, 0xe2, 0xeb, 0xec, - 0xc1, 0xc6, 0xcf, 0xc8, 0xdd, 0xda, 0xd3, 0xd4, - 0x69, 0x6e, 0x67, 0x60, 0x75, 0x72, 0x7b, 0x7c, - 0x51, 0x56, 0x5f, 0x58, 0x4d, 0x4a, 0x43, 0x44, - 0x19, 0x1e, 0x17, 0x10, 0x05, 0x02, 0x0b, 0x0c, - 0x21, 0x26, 0x2f, 0x28, 0x3d, 0x3a, 0x33, 0x34, - 0x4e, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5c, 0x5b, - 0x76, 0x71, 0x78, 0x7f, 0x6A, 0x6d, 0x64, 0x63, - 0x3e, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2c, 0x2b, - 0x06, 0x01, 0x08, 0x0f, 0x1a, 0x1d, 0x14, 0x13, - 0xae, 0xa9, 0xa0, 0xa7, 0xb2, 0xb5, 0xbc, 0xbb, - 0x96, 0x91, 0x98, 0x9f, 0x8a, 0x8D, 0x84, 0x83, - 0xde, 0xd9, 0xd0, 0xd7, 0xc2, 0xc5, 0xcc, 0xcb, - 0xe6, 0xe1, 0xe8, 0xef, 0xfa, 0xfd, 0xf4, 0xf3 - ] - -################################ -### --- Packet Sync Byte --- ### -################################ -PACKET_SYNC_BYTE = 0x55 # PACKET SYNC BYTE - - -############################ -### --- Packet Types --- ### -############################ - -PACKET_TYPE_RADIO = 0x01 # RADIO ERP1 -PACKET_TYPE_RESPONSE = 0x02 # RESPONSE -PACKET_TYPE_RADIO_SUB_TEL = 0x03 # RADIO_SUB_TEL -PACKET_TYPE_EVENT = 0x04 # EVENT -PACKET_TYPE_COMMON_COMMAND = 0x05 # COMMON COMMAND -PACKET_TYPE_SMART_ACK_COMMAND = 0x06 # SMART ACK COMMAND -PACKET_REMOTE_MAN_COMMAND = 0x07 # REMOTE MANAGEMENT COMMAND -PACKET_TYPE_RADIO_MESSAGE = 0x09 # RADIO MESSAGE -PACKET_TYPE_RADIO_ERP2 = 0x0A # RADIO ERP2 -PACKET_TYPE_RADIO_802_15_4 = 0x10 # RADIO_802_15_4 -PACKET_TYPE_COMMAND_2_4 = 0x11 # COMMAND_2_4 - - -############################################ -### --- List of Common Command Codes --- ### -############################################ - -CO_WR_SLEEP = 0x01 # Order to enter in energy saving mode -CO_WR_RESET = 0x02 # Order to reset the device -CO_RD_VERSION = 0x03 # Read the device (SW) version /(HW) version, chip ID etc. -CO_RD_SYS_LOG = 0x04 # Read system log from device databank -CO_WR_SYS_LOG = 0x05 # Reset System log from device databank -CO_WR_BIST = 0x06 # Perform built in self test -CO_WR_IDBASE = 0x07 # Write ID range base number -CO_RD_IDBASE = 0x08 # Read ID range base number -CO_WR_REPEATER = 0x09 # Write Repeater Level off,1,2 -CO_RD_REPEATER = 0x0A # Read Repeater Level off,1,2 -CO_WR_FILTER_ADD = 0x0B # Add filter to filter list -CO_WR_FILTER_DEL = 0x0C # Delete filter from filter list -CO_WR_FILTER_DEL_ALL = 0x0D # Delete all filter -CO_WR_FILTER_ENABLE = 0x0E # Enable/Disable supplied filters -CO_RD_FILTER = 0x0F # Read supplied filters -CO_WR_WAIT_MATURITY = 0x10 # Waiting till end of maturity time before received radio telegrams will transmitted -CO_WR_SUBTEL = 0x11 # Enable/Disable transmitting additional subtelegram info -CO_WR_MEM = 0x12 # Write x bytes of the Flash, XRAM, RAM0 … -CO_RD_MEM = 0x13 # Read x bytes of the Flash, XRAM, RAM0 …. -CO_RD_MEM_ADDRESS = 0x14 # Feedback about the used address and length of the configarea and the Smart Ack Table -CO_RD_SECURITY = 0x15 # Read own security information (level, key) -CO_WR_SECURITY = 0x16 # Write own security information (level, key) -CO_WR_LEARNMODE = 0x17 # Function: Enables or disables learn mode of Controller. -CO_RD_LEARNMODE = 0x18 # Function: Reads the learn-mode state of Controller. -CO_WR_SECUREDEVICE_ADD = 0x19 # Add a secure device -CO_WR_SECUREDEVICE_DEL = 0x1A # Delete a secure device -CO_RD_SECUREDEVICE_BY_INDEX = 0x1B # Read secure device by index -CO_WR_MODE = 0x1C # Sets the gateway transceiver mode -CO_RD_NUMSECUREDEVICES = 0x1D # Read number of taught in secure devices -CO_RD_SECUREDEVICE_BY_ID = 0x1E # Read secure device by ID -CO_WR_SECUREDEVICE_ADD_PSK = 0x1F # Add Pre-shared key for inbound secure device -CO_WR_SECUREDEVICE_SENDTEACHIN = 0x20 # Send secure Teach-In message -CO_WR_TEMPORARY_RLC_WINDOW = 0x21 # Set the temporary rolling-code window for every taught-in devic -CO_RD_SECUREDEVICE_PSK = 0x22 # Read PSK -CO_RD_DUTYCYCLE_LIMIT = 0x23 # Read parameters of actual duty cycle limit -CO_SET_BAUDRATE = 0x24 # Modifies the baud rate of the EnOcean device -CO_GET_FREQUENCY_INFO = 0x25 # Reads Frequency and protocol of the Device -CO_GET_STEPCODE = 0x27 # Reads Hardware Step code and Revision of the Device - - -################################### -### --- List of Event Codes --- ### -################################### - -SA_RECLAIM_NOT_SUCCESSFUL = 0x01 # Informs the backbone of a Smart Ack Client to not successful reclaim. -SA_CONFIRM_LEARN = 0x02 # Used for SMACK to confirm/discard learn in/out -SA_LEARN_ACK = 0x03 # Inform backbone about result of learn request -CO_READY = 0x04 # Inform backbone about the readiness for operation -CO_EVENT_SECUREDEVICES = 0x05 # Informs about a secure device -CO_DUTYCYCLE_LIMIT = 0x06 # Informs about duty cycle limit -CO_TRANSMIT_FAILED = 0x07 # Informs that the device was not able to send a telegram. - - -########################################### -### --- Smart Acknowledge Defines: --- ### -########################################### - -SA_WR_LEARNMODE = 0x01 # Set/Reset Smart Ack learn mode -SA_RD_LEARNMODE = 0x02 # Get Smart Ack learn mode state -SA_WR_LEARNCONFIRM = 0x03 # Used for Smart Ack to add or delete a mailbox of a client -SA_WR_CLIENTLEARNRQ = 0x04 # Send Smart Ack Learn request (Client) -SA_WR_RESET = 0x05 # Send reset command to a Smart Ack client -SA_RD_LEARNEDCLIENTS = 0x06 # Get Smart Ack learned sensors / mailboxes -SA_WR_RECLAIMS = 0x07 # Set number of reclaim attempts -SA_WR_POSTMASTER = 0x08 # Activate/Deactivate Post master functionality - -SENT_RADIO_PACKET = 0xFF -SENT_ENCAPSULATED_RADIO_PACKET = 0xA6 class EnOcean(SmartPlugin): ALLOW_MULTIINSTANCE = False - PLUGIN_VERSION = "1.4.0" + PLUGIN_VERSION = "1.4.2" - def __init__(self, sh): - """ - Initializes the plugin. - - """ + """ Initializes the plugin. """ # Call init code of parent class (SmartPlugin) super().__init__() - self._sh = sh self.port = self.get_parameter_value("serialport") + self._log_unknown_msg = self.get_parameter_value("log_unknown_messages") + self._connect_retries = self.get_parameter_value("retry") + self._retry_cycle = self.get_parameter_value("retry_cycle") tx_id = self.get_parameter_value("tx_id") - if (len(tx_id) < 8): + if len(tx_id) < 8: self.tx_id = 0 self.logger.warning('No valid enocean stick ID configured. Transmitting is not supported') else: self.tx_id = int(tx_id, 16) self.logger.info(f"Stick TX ID configured via plugin.conf to: {tx_id}") - self._log_unknown_msg = self.get_parameter_value("log_unknown_messages") +# + self._items = [] self._tcm = None self._cmd_lock = threading.Lock() self._response_lock = threading.Condition() @@ -196,421 +69,168 @@ def __init__(self, sh): self.UTE_listen = False self.unknown_sender_id = 'None' self._block_ext_out_msg = False - # call init of eep_parser - self.eep_parser = eep_parser.EEP_Parser() - # call init of prepare_packet_data - self.prepare_packet_data = prepare_packet_data.Prepare_Packet_Data(self) - - self.init_webinterface(WebInterface) - - - def eval_telegram(self, sender_id, data, opt): - logger_debug = self.logger.isEnabledFor(logging.DEBUG) - if logger_debug: - self.logger.debug("Call function << eval_telegram >>") - for item in self._items: - # validate id for item id: - if item.conf['enocean_id'] == sender_id: - #print ("validated {0} for {1}".format(sender_id,item)) - #print ("try to get value for: {0} and {1}".format(item.conf['enocean_rorg'][0],item.conf['enocean_rorg'][1])) - rorg = item.conf['enocean_rorg'] - eval_value = item.conf['enocean_value'] - if rorg in RADIO_PAYLOAD_VALUE: # check if RORG exists - pl = eval(RADIO_PAYLOAD_VALUE[rorg]['payload_idx']) - #could be nicer - for entity in RADIO_PAYLOAD_VALUE: - if (rorg == entity) and (eval_value in RADIO_PAYLOAD_VALUE[rorg]['entities']): - value_dict = RADIO_PAYLOAD_VALUE[rorg]['entities'] - value = eval(RADIO_PAYLOAD_VALUE[rorg]['entities'][eval_value]) - if logger_debug: - self.logger.debug(f"Resulting value: {value} for {item}") - if value: # not sure about this - item(value, self.get_shortname(), 'RADIO') - - def _process_packet_type_event(self, data, optional): - logger_debug = self.logger.isEnabledFor(logging.DEBUG) - if logger_debug: - self.logger.debug("call function << _process_packet_type_event >>") - event_code = data[0] - if(event_code == SA_RECLAIM_NOT_SUCCESSFUL): - self.logger.error("SA reclaim was not successful") - elif(event_code == SA_CONFIRM_LEARN): - self.logger.info("Requesting how to handle confirm/discard learn in/out") - elif(event_code == SA_LEARN_ACK): - self.logger.info("SA lern acknowledged") - elif(event_code == CO_READY): - self.logger.info("Controller is ready for operation") - elif(event_code == CO_TRANSMIT_FAILED): - self.logger.error("Telegram transmission failed") - elif(event_code == CO_DUTYCYCLE_LIMIT): - self.logger.warning("Duty cycle limit reached") - elif(event_code == CO_EVENT_SECUREDEVICES): - self.logger.info("Secure device event packet received") - else: - self.logger.warning("Unknown event packet received") - - def _rocker_sequence(self, item, sender_id, sequence): - logger_debug = self.logger.isEnabledFor(logging.DEBUG) - if logger_debug: - self.logger.debug("Call function << _rocker_sequence >>") - try: - for step in sequence: - event, relation, delay = step.split() - #self.logger.debug("waiting for {} {} {}".format(event, relation, delay)) - if item._enocean_rs_events[event.upper()].wait(float(delay)) != (relation.upper() == "WITHIN"): - if logger_debug: - self.logger.debug(f"NOT {step} - aborting sequence!") - return - else: - if logger_debug: - self.logger.debug(f"{step}") - item._enocean_rs_events[event.upper()].clear() - continue - value = True - if 'enocean_rocker_action' in item.conf: - if item.conf['enocean_rocker_action'].upper() == "UNSET": - value = False - elif item.conf['enocean_rocker_action'].upper() == "TOGGLE": - value = not item() - item(value, self.get_shortname(), "{:08X}".format(sender_id)) - except Exception as e: - self.logger.error(f'Error handling enocean_rocker_sequence \"{sequence}\" - {e}'.format(sequence, e)) - - def _process_packet_type_radio(self, data, optional): - logger_debug = self.logger.isEnabledFor(logging.DEBUG) - if logger_debug: - self.logger.debug("Call function << _process_packet_type_radio >>") - #self.logger.warning("Processing radio message with data = [{}] / optional = [{}]".format(', '.join(['0x%02x' % b for b in data]), ', '.join(['0x%02x' % b for b in optional]))) - - choice = data[0] - payload = data[1:-5] - sender_id = int.from_bytes(data[-5:-1], byteorder='big', signed=False) - status = data[-1] - repeater_cnt = status & 0x0F - self.logger.info("Radio message: choice = {:02x} / payload = [{}] / sender_id = {:08X} / status = {} / repeat = {}".format(choice, ', '.join(['0x%02x' % b for b in payload]), sender_id, status, repeater_cnt)) - - if (len(optional) == 7): - subtelnum = optional[0] - dest_id = int.from_bytes(optional[1:5], byteorder='big', signed=False) - dBm = -optional[5] - SecurityLevel = optional[6] - if logger_debug: - self.logger.debug("Radio message with additional info: subtelnum = {} / dest_id = {:08X} / signal = {}dBm / SecurityLevel = {}".format(subtelnum, dest_id, dBm, SecurityLevel)) - if (choice == 0xD4) and (self.UTE_listen == True): - self.logger.info("Call send_UTE_response") - self._send_UTE_response(data, optional) - if sender_id in self._rx_items: - if logger_debug: - self.logger.debug("Sender ID found in item list") - # iterate over all eep known for this id and get list of associated items - for eep,items in self._rx_items[sender_id].items(): - # check if choice matches first byte in eep (this seems to be the only way to find right eep for this particular packet) - if eep.startswith("{:02X}".format(choice)): - # call parser for particular eep - returns dictionary with key-value pairs - results = self.eep_parser.Parse(eep, payload, status) - if logger_debug: - self.logger.debug(f"Radio message results = {results}") - if 'DEBUG' in results: - self.logger.warning("DEBUG Info: processing radio message with data = [{}] / optional = [{}]".format(', '.join(['0x%02x' % b for b in data]), ', '.join(['0x%02x' % b for b in optional]))) - self.logger.warning(f"Radio message results = {results}") - self.logger.warning("Radio message: choice = {:02x} / payload = [{}] / sender_id = {:08X} / status = {} / repeat = {}".format(choice, ', '.join(['0x%02x' % b for b in payload]), sender_id, status, repeater_cnt)) + self.log_for_debug = self.logger.isEnabledFor(logging.DEBUG) - for item in items: - rx_key = item.conf['enocean_rx_key'].upper() - if rx_key in results: - if 'enocean_rocker_sequence' in item.conf: - try: - if hasattr(item, '_enocean_rs_thread') and item._enocean_rs_thread.is_alive(): - if results[rx_key]: - if logger_debug: - self.logger.debug("Sending pressed event") - item._enocean_rs_events["PRESSED"].set() - else: - if logger_debug: - self.logger.debug("Sending released event") - item._enocean_rs_events["RELEASED"].set() - elif results[rx_key]: - item._enocean_rs_events = {'PRESSED': threading.Event(), 'RELEASED': threading.Event()} - item._enocean_rs_thread = threading.Thread(target=self._rocker_sequence, name="enocean-rs", args=(item, sender_id, item.conf['enocean_rocker_sequence'].split(','), )) - #self.logger.info("starting enocean_rocker_sequence thread") - item._enocean_rs_thread.daemon = True - item._enocean_rs_thread.start() - except Exception as e: - self.logger.error(f"Error handling enocean_rocker_sequence: {e}") - else: - item(results[rx_key], self.get_shortname(), "{:08X}".format(sender_id)) - elif (sender_id <= self.tx_id + 127) and (sender_id >= self.tx_id): - if logger_debug: - self.logger.debug("Received repeated enocean stick message") - else: - self.unknown_sender_id = "{:08X}".format(sender_id) - if self._log_unknown_msg: - self.logger.info("Unknown ID = {:08X}".format(sender_id)) - self.logger.warning("Unknown device sent radio message: choice = {:02x} / payload = [{}] / sender_id = {:08X} / status = {} / repeat = {}".format(choice, ', '.join(['0x%02x' % b for b in payload]), sender_id, status, repeater_cnt)) - - - def _process_packet_type_smart_ack_command(self, data, optional): - self.logger.warning("Smart acknowledge command 0x06 received but not supported at the moment") + # init eep_parser + self.eep_parser = EEP_Parser(self.logger) + # init prepare_packet_data + self.prepare_packet_data = Packet_Data(self) - def _process_packet_type_response(self, data, optional): - logger_debug = self.logger.isEnabledFor(logging.DEBUG) - if logger_debug: - self.logger.debug("Call function << _process_packet_type_response >>") - RETURN_CODES = ['OK', 'ERROR', 'NOT SUPPORTED', 'WRONG PARAM', 'OPERATION DENIED'] - if (self._last_cmd_code == SENT_RADIO_PACKET) and (len(data) == 1): - if logger_debug: - self.logger.debug(f"Sending command returned code = {RETURN_CODES[data[0]]}") - elif (self._last_packet_type == PACKET_TYPE_COMMON_COMMAND) and (self._last_cmd_code == CO_WR_RESET) and (len(data) == 1): - self.logger.info(f"Reset returned code = {RETURN_CODES[data[0]]}") - elif (self._last_packet_type == PACKET_TYPE_COMMON_COMMAND) and (self._last_cmd_code == CO_WR_LEARNMODE) and (len(data) == 1): - self.logger.info(f"Write LearnMode returned code = {RETURN_CODES[data[0]]}") - elif (self._last_packet_type == PACKET_TYPE_COMMON_COMMAND) and (self._last_cmd_code == CO_RD_VERSION): - if (data[0] == 0) and (len(data) == 33): - self.logger.info("Chip ID = 0x{} / Chip Version = 0x{}".format(''.join(['%02x' % b for b in data[9:13]]), ''.join(['%02x' % b for b in data[13:17]]))) - self.logger.info("APP version = {} / API version = {} / App description = {}".format('.'.join(['%d' % b for b in data[1:5]]), '.'.join(['%d' % b for b in data[5:9]]), ''.join(['%c' % b for b in data[17:33]]))) - elif (data[0] == 0) and (len(data) == 0): - self.logger.error("Reading version: No answer") - else: - self.logger.error(f"Reading version returned code = {RETURN_CODES[data[0]]}, length = {len(data)}") - elif (self._last_packet_type == PACKET_TYPE_COMMON_COMMAND) and (self._last_cmd_code == CO_RD_IDBASE): - if (data[0] == 0) and (len(data) == 5): - self.logger.info("Base ID = 0x{}".format(''.join(['%02x' % b for b in data[1:5]]))) - if (self.tx_id == 0): - self.tx_id = int.from_bytes(data[1:5], byteorder='big', signed=False) - self.logger.info("Transmit ID set set automatically by reading chips BaseID") - if (len(optional) == 1): - self.logger.info(f"Remaining write cycles for Base ID = {optional[0]}") - elif (data[0] == 0) and (len(data) == 0): - self.logger.error("Reading Base ID: No answer") - else: - self.logger.error(f"Reading Base ID returned code = {RETURN_CODES[data[0]]} and {len(data)} bytes") - elif (self._last_packet_type == PACKET_TYPE_COMMON_COMMAND) and (self._last_cmd_code == CO_WR_BIST): - if (data[0] == 0) and (len(data) == 2): - if (data[1] == 0): - self.logger.info("Built in self test result: All OK") - else: - self.logger.info(f"Built in self test result: Problem, code = {data[1]}") - elif (data[0] == 0) and (len(data) == 0): - self.logger.error("Doing built in self test: No answer") - else: - self.logger.error(f"Doing built in self test returned code = {RETURN_CODES[data[0]]}") - elif (self._last_packet_type == PACKET_TYPE_COMMON_COMMAND) and (self._last_cmd_code == CO_RD_LEARNMODE): - if (data[0] == 0) and (len(data) == 2): - self.logger.info("Reading LearnMode = 0x{}".format(''.join(['%02x' % b for b in data[1]]))) - if (len(optional) == 1): - self.logger.info("Learn channel = {}".format(optional[0])) - elif (data[0] == 0) and (len(data) == 0): - self.logger.error("Reading LearnMode: No answer") - elif (self._last_packet_type == PACKET_TYPE_COMMON_COMMAND) and (self._last_cmd_code == CO_RD_NUMSECUREDEVICES): - if (data[0] == 0) and (len(data) == 2): - self.logger.info("Number of taught in devices = 0x{}".format(''.join(['%02x' % b for b in data[1]]))) - elif (data[0] == 0) and (len(data) == 0): - self.logger.error("Reading NUMSECUREDEVICES: No answer") - elif (data[0] == 2) and (len(data) == 1): - self.logger.error("Reading NUMSECUREDEVICES: Command not supported") - else: - self.logger.error("Reading NUMSECUREDEVICES: Unknown error") - elif (self._last_packet_type == PACKET_TYPE_SMART_ACK_COMMAND) and (self._last_cmd_code == SA_WR_LEARNMODE): - self.logger.info(f"Setting SmartAck mode returned code = {RETURN_CODES[data[0]]}") - elif (self._last_packet_type == PACKET_TYPE_SMART_ACK_COMMAND) and (self._last_cmd_code == SA_RD_LEARNEDCLIENTS): - if (data[0] == 0): - self.logger.info(f"Number of smart acknowledge mailboxes = {int((len(data)-1)/9)}") - else: - self.logger.error(f"Requesting SmartAck mailboxes returned code = {RETURN_CODES[data[0]]}") - else: - self.logger.error("Processing unexpected response with return code = {} / data = [{}] / optional = [{}]".format(RETURN_CODES[data[0]], ', '.join(['0x%02x' % b for b in data]), ', '.join(['0x%02x' % b for b in optional]))) - self._response_lock.acquire() - self._response_lock.notify() - self._response_lock.release() + # init crc parser + self.crc = CRC() - def _startup(self): - self.logger.debug("Call function << _startup >>") - # request one time information - self.logger.info("Resetting device") - self._send_common_command(CO_WR_RESET) - self.logger.info("Requesting id-base") - self._send_common_command(CO_RD_IDBASE) - self.logger.info("Requesting version information") - self._send_common_command(CO_RD_VERSION) - self.logger.debug("Ending connect-thread") + self.init_webinterface(WebInterface) def run(self): - logger_debug = self.logger.isEnabledFor(logging.DEBUG) - if logger_debug: - self.logger.debug("Call function << run >>") + if self.log_for_debug: + self.logger.debug("Run method called") + self.alive = True self.UTE_listen = False - - # open serial or serial2TCP device: - try: - self._tcm = serial.serial_for_url(self.port, 57600, timeout=1.5) - except Exception as e: - self._tcm = None - self._init_complete = False - self.logger.error(f"Exception occurred during serial open: {e}") - return - else: - self.logger.info(f"Serial port successfully opened at port {self.port}") - - t = threading.Thread(target=self._startup, name="enocean-startup") - # if you need to create child threads, do not make them daemon = True! - # They will not shutdown properly. (It's a python bug) - t.daemon = False - t.start() msg = [] + while self.alive: + + # just try connecting anytime the serial object is not initialized + connect_count = 0 + while self._tcm is None and self.alive: + if connect_count >= self._connect_retries: + self.alive = False + break + if not self.connect(): + connect_count += 1 + self.logger.info(f'connecting failed {connect_count} times. Retrying after 5 seconds...') + sleep(self._retry_cycle) + + # main loop, read from device + readin = None try: readin = self._tcm.read(1000) except Exception as e: - self.logger.error(f"Exception during tcm read occurred: {e}") - break - else: - if readin: - msg += readin - if logger_debug: - self.logger.debug("Data received") - # check if header is complete (6bytes including sync) - # 0x55 (SYNC) + 4bytes (HEADER) + 1byte(HEADER-CRC) - while (len(msg) >= 6): - #check header for CRC - if (msg[0] == PACKET_SYNC_BYTE) and (self._calc_crc8(msg[1:5]) == msg[5]): - # header bytes: sync; length of data (2); optional length; packet type; crc - data_length = (msg[1] << 8) + msg[2] - opt_length = msg[3] - packet_type = msg[4] - msg_length = data_length + opt_length + 7 - if logger_debug: - self.logger.debug("Received header with data_length = {} / opt_length = 0x{:02x} / type = {}".format(data_length, opt_length, packet_type)) - - # break if msg is not yet complete: - if (len(msg) < msg_length): - break - - # msg complete - if (self._calc_crc8(msg[6:msg_length - 1]) == msg[msg_length - 1]): - if logger_debug: - self.logger.debug("Accepted package with type = 0x{:02x} / len = {} / data = [{}]!".format(packet_type, msg_length, ', '.join(['0x%02x' % b for b in msg]))) - data = msg[6:msg_length - (opt_length + 1)] - optional = msg[(6 + data_length):msg_length - 1] - if (packet_type == PACKET_TYPE_RADIO): - self._process_packet_type_radio(data, optional) - elif (packet_type == PACKET_TYPE_SMART_ACK_COMMAND): - self._process_packet_type_smart_ack_command(data, optional) - elif (packet_type == PACKET_TYPE_RESPONSE): - self._process_packet_type_response(data, optional) - elif (packet_type == PACKET_TYPE_EVENT): - self._process_packet_type_event(data, optional) - else: - self.logger.error("Received packet with unknown type = 0x{:02x} - len = {} / data = [{}]".format(packet_type, msg_length, ', '.join(['0x%02x' % b for b in msg]))) + if self.alive: + self.logger.error(f"Exception during tcm read occurred: {e}") + # reset serial device + try: + self._tcm.close() + except Exception: + pass + self._tcm = None + continue + + if readin: + msg += readin + if self.log_for_debug: + self.logger.debug(f"Data received: {readin}") + # check if header is complete (6bytes including sync) + # 0x55 (SYNC) + 4bytes (HEADER) + 1byte(HEADER-CRC) + while len(msg) >= 6: + # check header for CRC + if msg[0] == PACKET.SYNC_BYTE and msg[5] == self.crc(msg[1:5]): + # header bytes: sync; length of data (2); optional length; packet type; crc + data_length = (msg[1] << 8) + msg[2] + opt_length = msg[3] + packet_type = msg[4] + msg_length = data_length + opt_length + 7 + if self.log_for_debug: + self.logger.debug(f"Received header with data_length = {data_length} / opt_length = 0x{opt_length:02x} / type = {packet_type}") + + # break if msg is not yet complete: + if len(msg) < msg_length: + break + + # msg complete + if self.crc(msg[6:msg_length - 1]) == msg[msg_length - 1]: + if self.log_for_debug: + self.logger.debug("Accepted package with type = 0x{:02x} / len = {} / data = [{}]!".format(packet_type, msg_length, ', '.join(['0x%02x' % b for b in msg]))) + data = msg[6:msg_length - (opt_length + 1)] + optional = msg[data_length + 6:msg_length - 1] + if packet_type == PACKET_TYPE.RADIO: + self._process_packet_type_radio(data, optional) + elif packet_type == PACKET_TYPE.SMART_ACK_COMMAND: + self._process_packet_type_smart_ack_command(data, optional) + elif packet_type == PACKET_TYPE.RESPONSE: + self._process_packet_type_response(data, optional) + elif packet_type == PACKET_TYPE.EVENT: + self._process_packet_type_event(data, optional) else: - self.logger.error("Crc error - dumping packet with type = 0x{:02x} / len = {} / data = [{}]!".format(packet_type, msg_length, ', '.join(['0x%02x' % b for b in msg]))) - msg = msg[msg_length:] + self.logger.error("Received packet with unknown type = 0x{:02x} - len = {} / data = [{}]".format(packet_type, msg_length, ', '.join(['0x%02x' % b for b in msg]))) else: - #self.logger.warning("Consuming [0x{:02x}] from input buffer!".format(msg[0])) - msg.pop(0) - try: - self._tcm.close() - except Exception as e: - self.logger.error(f"Exception during tcm close occured: {e}") - else: - self.logger.info(f"Enocean serial device closed") - self.logger.info("Run method stopped") - - def stop(self): - self.logger.debug("Call function << stop >>") - self.alive = False - - def get_tx_id_as_hex(self): - hexstring = "{:08X}".format(self.tx_id) - return hexstring - - def get_serial_status_as_string(self): - if (self._tcm and self._tcm.is_open): - return "open" - else: - return "not connected" - - def get_log_unknown_msg(self): - return self._log_unknown_msg + self.logger.error("Crc error - dumping packet with type = 0x{:02x} / len = {} / data = [{}]!".format(packet_type, msg_length, ', '.join(['0x%02x' % b for b in msg]))) + msg = msg[msg_length:] + else: + # self.logger.warning("Consuming [0x{:02x}] from input buffer!".format(msg[0])) + msg.pop(0) - def toggle_log_unknown_msg(self): - self._log_unknown_msg = not self._log_unknown_msg - - def _send_UTE_response(self, data, optional): - self.logger.debug("Call function << _send_UTE_response >>") - choice = data[0] - payload = data[1:-5] - #sender_id = int.from_bytes(data[-5:-1], byteorder='big', signed=False) - #status = data[-1] - #repeater_cnt = status & 0x0F - SubTel = 0x03 - db = 0xFF - Secu = 0x0 + # self.alive is False or connect error caused loop exit + self.stop() - self._send_radio_packet(self.learn_id, choice, [0x91, payload[1], payload[2], payload[3], payload[4], payload[5], payload[6]],[SubTel, data[-5], data[-4], data[-3], data[-2], db, Secu] )#payload[0] = 0x91: EEP Teach-in response, Request accepted, teach-in successful, bidirectional - self.UTE_listen = False - self.logger.info("Sending UTE response and end listening") + def stop(self): + self.logger.debug("Stop method called") + self.alive = False + self.disconnect() def parse_item(self, item): - self.logger.debug("Call function << parse_item >>") + self.logger.debug("parse_item method called") if 'enocean_rx_key' in item.conf: # look for info from the most specific info to the broadest (key->eep->id) - one id might use multiple eep might define multiple keys eep_item = item found_eep = True - while (not 'enocean_rx_eep' in eep_item.conf): + while 'enocean_rx_eep' not in eep_item.conf: eep_item = eep_item.return_parent() - if (eep_item is self._sh): + if eep_item is Items.get_instance(): self.logger.error(f"Could not find enocean_rx_eep for item {item}") found_eep = False + break id_item = eep_item found_rx_id = True - while (not 'enocean_rx_id' in id_item.conf): + while 'enocean_rx_id' not in id_item.conf: id_item = id_item.return_parent() - if (id_item is self._sh): + if id_item is Items.get_instance(): self.logger.error(f"Could not find enocean_rx_id for item {item}") found_rx_id = False + break # Only proceed, if valid rx_id and eep could be found: if found_rx_id and found_eep: rx_key = item.conf['enocean_rx_key'].upper() rx_eep = eep_item.conf['enocean_rx_eep'].upper() - rx_id = int(id_item.conf['enocean_rx_id'],16) + rx_id = int(id_item.conf['enocean_rx_id'], 16) # check if there is a function to parse payload if self.eep_parser.CanParse(rx_eep): - + if (rx_key in ['A0', 'A1', 'B0', 'B1']): - self.logger.warning(f"Key \"{rx_key}\" does not match EEP - \"0\" (Zero, number) should be \"O\" (letter) (same for \"1\" and \"I\") - will be accepted for now") + self.logger.warning(f'Key "{rx_key}" does not match EEP - "0" (Zero, number) should be "O" (letter) (same for "1" and "I") - will be accepted for now') rx_key = rx_key.replace('0', 'O').replace("1", 'I') - if (not rx_id in self._rx_items): + if rx_id not in self._rx_items: self._rx_items[rx_id] = {rx_eep: [item]} - elif (not rx_eep in self._rx_items[rx_id]): + elif rx_eep not in self._rx_items[rx_id]: self._rx_items[rx_id][rx_eep] = [item] - elif (not item in self._rx_items[rx_id][rx_eep]): + elif item not in self._rx_items[rx_id][rx_eep]: self._rx_items[rx_id][rx_eep].append(item) - self.logger.info("Item {} listens to id {:08X} with eep {} key {}".format(item, rx_id, rx_eep, rx_key)) - #self.logger.info(f"self._rx_items = {self._rx_items}") - + self.logger.info(f"Item {item} listens to id {rx_id:08X} with eep {rx_eep} key {rx_key}") + # self.logger.info(f"self._rx_items = {self._rx_items}") + if 'enocean_tx_eep' in item.conf: self.logger.debug(f"TX eep found in item {item._name}") - - if not 'enocean_tx_id_offset' in item.conf: + + if 'enocean_tx_id_offset' not in item.conf: self.logger.error(f"TX eep found for item {item._name} but no tx id offset specified.") return tx_offset = item.conf['enocean_tx_id_offset'] - if not (tx_offset in self._used_tx_offsets): + if tx_offset not in self._used_tx_offsets: self._used_tx_offsets.append(tx_offset) self._used_tx_offsets.sort() self.logger.debug(f"Debug offset list: {self._used_tx_offsets}") for x in range(1, 127): - if not x in self._used_tx_offsets: + if x not in self._used_tx_offsets: self._unused_tx_offset = x self.logger.debug(f"Next free offset set to {self._unused_tx_offset}") break @@ -620,59 +240,101 @@ def parse_item(self, item): # register item for event handling via smarthomeNG core. Needed for sending control actions: return self.update_item - - def update_item(self, item, caller=None, source=None, dest=None): - logger_debug = self.logger.isEnabledFor(logging.DEBUG) - if logger_debug: - self.logger.debug("Call function << update_item >>") + if self.log_for_debug: + self.logger.debug("update_item method called") - #self.logger.warning(f"Debug: update item: caller: {caller}, shortname: {self.get_shortname()}, item: {item.id()}") + # self.logger.warning(f"Debug: update item: caller: {caller}, shortname: {self.get_shortname()}, item: {item.id()}") if caller != self.get_shortname(): - if logger_debug: - self.logger.debug(f'Item << {item} >> updated externally.') + if self.log_for_debug: + self.logger.debug(f'Item {item} updated externally.') if self._block_ext_out_msg: - self.logger.warning('Sending manually blocked by user. Aborting') - return None + self.logger.warning('Transmitting manually blocked by user. Aborting') + return if 'enocean_tx_eep' in item.conf: if isinstance(item.conf['enocean_tx_eep'], str): tx_eep = item.conf['enocean_tx_eep'] - if logger_debug: + if self.log_for_debug: self.logger.debug(f'item << {item} >> has tx_eep') # check if Data can be Prepared - if not self.prepare_packet_data.CanDataPrepare(tx_eep): + if not self.prepare_packet_data.CanPrepareData(tx_eep): self.logger.error(f'enocean-update_item: method missing for prepare telegram data for {tx_eep}') else: # call method prepare_packet_data(item, tx_eep) id_offset, rorg, payload, optional = self.prepare_packet_data.PrepareData(item, tx_eep) self._send_radio_packet(id_offset, rorg, payload, optional) else: - self.logger.error(f'tx_eep {tx_eep} is not a string value') + self.logger.error('tx_eep is not a string value') else: - if logger_debug: - self.logger.debug(f'Item << {item} >>has no tx_eep value') + if self.log_for_debug: + self.logger.debug(f'Item {item} has no tx_eep value') - def read_num_securedivices(self): - self.logger.debug("Call function << read_num_securedivices >>") - self._send_common_command(CO_RD_NUMSECUREDEVICES) - self.logger.info("Read number of secured devices") + def connect(self, startup=True): + """ open serial or serial2TCP device """ + self.logger.debug(f'trying to connect to device at {self.port}') + try: + self._tcm = serial.serial_for_url(self.port, 57600, timeout=1.5) + except Exception as e: + self._tcm = None + self.logger.error(f"Exception occurred during serial open: {e}") + return False + else: + self.logger.info(f"Serial port successfully opened at port {self.port}") + +# why startup in separate thread? time to startup? collision with receiving? + if startup: + t = threading.Thread(target=self._startup, name="enocean-startup") + t.daemon = False + t.start() + return True + + def disconnect(self): + """ close serial or serial2TCP device """ + try: + self._tcm.close() + except Exception: + pass + self.logger.info("Enocean serial device closed") + + def _startup(self): + """ send startup sequence to device """ + self.logger.debug("_startup method called") + + # request one time information + self.logger.info("Resetting device") + self._send_common_command(COMMON_COMMAND.WR_RESET) + self.logger.info("Requesting id-base") + self._send_common_command(COMMON_COMMAND.RD_IDBASE) + self.logger.info("Requesting version information") + self._send_common_command(COMMON_COMMAND.RD_VERSION) + self.logger.debug("Ending startup-thread") + +# +# public EnOcean interface methods +# + + def read_num_securedevices(self): + """ read number of secure devices """ + self.logger.debug("read_num_securedevices method called") + self._send_common_command(COMMON_COMMAND.RD_NUMSECUREDEVICES) + self.logger.info("Read number of secured devices") - # Request all taught in smart acknowledge devices that have a mailbox def get_smart_ack_devices(self): - self.logger.debug("Call function << get_smart_ack_devices >>") - self._send_smart_ack_command(SA_RD_LEARNEDCLIENTS) + """ request all smart acknowledge devices """ + self.logger.debug("get_smart_ack_devices method called") + self._send_smart_ack_command(SMART_ACK.RD_LEARNEDCLIENTS) self.logger.info("Requesting all available smart acknowledge mailboxes") - def reset_stick(self): - self.logger.debug("Call function << reset_stick >>") + """ reset EnOcean transmitter """ + self.logger.debug("reset_stick method called") self.logger.info("Resetting device") - self._send_common_command(CO_WR_RESET) + self._send_common_command(COMMON_COMMAND.WR_RESET) def block_external_out_messages(self, block=True): - self.logger.debug("Call function << block_external_out_messages >>") + self.logger.debug("block_external_out_messages method called") if block: self.logger.info("Blocking of external out messages activated") self._block_ext_out_msg = True @@ -683,213 +345,435 @@ def block_external_out_messages(self, block=True): self.logger.error("Invalid argument. Must be True/False") def toggle_block_external_out_messages(self): - self.logger.debug("Call function << toggle block_external_out_messages >>") - if self._block_ext_out_msg == False: + self.logger.debug("toggle block_external_out_messages method called") + if not self._block_ext_out_msg: self.logger.info("Blocking of external out messages activated") self._block_ext_out_msg = True else: self.logger.info("Blocking of external out messages deactivated") self._block_ext_out_msg = False - def toggle_UTE_mode(self,id_offset=0): + def toggle_UTE_mode(self, id_offset=0): self.logger.debug("Toggle UTE mode") - if self.UTE_listen == True: + if self.UTE_listen: self.logger.info("UTE mode deactivated") self.UTE_listen = False - elif (id_offset is not None) and not (id_offset == 0): + elif id_offset: self.start_UTE_learnmode(id_offset) - self.logger.info("UTE mode activated for ID offset") + self.logger.info(f"UTE mode activated for ID offset {id_offset}") def send_bit(self): self.logger.info("Trigger Built-In Self Test telegram") - self._send_common_command(CO_WR_BIST) + self._send_common_command(COMMON_COMMAND.WR_BIST) def version(self): self.logger.info("Request stick version") - self._send_common_command(CO_RD_VERSION) + self._send_common_command(COMMON_COMMAND.RD_VERSION) - def _send_packet(self, packet_type, data=[], optional=[]): - #self.logger.debug("Call function << _send_packet >>") - length_optional = len(optional) - if length_optional > 255: - self.logger.error(f"Optional too long ({length_optional} bytes, 255 allowed)") - return None - length_data = len(data) - if length_data > 65535: - self.logger.error(f"Data too long ({length_data} bytes, 65535 allowed)") - return None +# +# Utility methods +# - packet = bytearray([PACKET_SYNC_BYTE]) - packet += length_data.to_bytes(2, byteorder='big') + bytes([length_optional, packet_type]) - packet += bytes([self._calc_crc8(packet[1:5])]) - packet += bytes(data + optional) - packet += bytes([self._calc_crc8(packet[6:])]) - self.logger.info("Sending packet with len = {} / data = [{}]!".format(len(packet), ', '.join(['0x%02x' % b for b in packet]))) - - # Send out serial data: - if not (self._tcm and self._tcm.is_open): - self.logger.debug("Trying serial reinit") - try: - self._tcm = serial.serial_for_url(self.port, 57600, timeout=1.5) - except Exception as e: - self._tcm = None - self.logger.error(f"Exception occurred during serial reinit: {e}") - else: - self.logger.debug("Serial reinit successful") - if self._tcm: - try: - self._tcm.write(packet) - except Exception as e: - self.logger.error(f"Exception during tcm write occurred: {e}") - self.logger.debug("Trying serial reinit after failed write") - try: - self._tcm = serial.serial_for_url(self.port, 57600, timeout=1.5) - except Exception as e: - self._tcm = None - self.logger.error(f"Exception occurred during serial reinit after failed write: {e}") - else: - self.logger.debug("Serial reinit successful after failed write") - try: - self._tcm.write(packet) - except Exception as e: - self.logger.error(f"Exception occurred during tcm write after successful serial reinit: {e}") - - def _send_smart_ack_command(self, _code, data=[]): - #self.logger.debug("Call function << _send_smart_ack_command >>") + def get_tx_id_as_hex(self): + hexstring = "{:08X}".format(self.tx_id) + return hexstring + + def is_connected(self): + return self._tcm and self._tcm.is_open + + def get_serial_status_as_string(self): + return "open" if self.is_connected() else "not connected" + + def get_log_unknown_msg(self): + return self._log_unknown_msg + + def toggle_log_unknown_msg(self): + self._log_unknown_msg = not self._log_unknown_msg + +# +# (private) packet / protocol methods +# + + def _send_smart_ack_command(self, code, data=[]): + # self.logger.debug("_send_smart_ack_command method called") self._cmd_lock.acquire() - self._last_cmd_code = _code - self._last_packet_type = PACKET_TYPE_SMART_ACK_COMMAND - self._send_packet(PACKET_TYPE_SMART_ACK_COMMAND, [_code] + data) + self._last_cmd_code = code + self._last_packet_type = PACKET_TYPE.SMART_ACK_COMMAND + self._send_packet(PACKET_TYPE.SMART_ACK_COMMAND, [code] + data) self._response_lock.acquire() # wait 5sec for response self._response_lock.wait(5) self._response_lock.release() self._cmd_lock.release() - def _send_common_command(self, _code, data=[], optional=[]): - #self.logger.debug("Call function << _send_common_command >>") + def _send_common_command(self, code, data=[], optional=[]): + # self.logger.debug("_send_common_command method called") self._cmd_lock.acquire() - self._last_cmd_code = _code - self._last_packet_type = PACKET_TYPE_COMMON_COMMAND - self._send_packet(PACKET_TYPE_COMMON_COMMAND, [_code] + data, optional) + self._last_cmd_code = code + self._last_packet_type = PACKET_TYPE.COMMON_COMMAND + self._send_packet(PACKET_TYPE.COMMON_COMMAND, [code] + data, optional) self._response_lock.acquire() # wait 5sec for response self._response_lock.wait(5) self._response_lock.release() self._cmd_lock.release() - def _send_radio_packet(self, id_offset, _code, data=[], optional=[]): - #self.logger.debug("Call function << _send_radio_packet >>") + def _send_radio_packet(self, id_offset, code, data=[], optional=[]): + # self.logger.debug("_send_radio_packet method called") if (id_offset < 0) or (id_offset > 127): self.logger.error(f"Invalid base ID offset range. (Is {id_offset}, must be [0 127])") return self._cmd_lock.acquire() - self._last_cmd_code = SENT_RADIO_PACKET - self._send_packet(PACKET_TYPE_RADIO, [_code] + data + list((self.tx_id + id_offset).to_bytes(4, byteorder='big')) + [0x00], optional) + self._last_cmd_code = PACKET.SENT_RADIO + self._send_packet(PACKET_TYPE.RADIO, [code] + data + list((self.tx_id + id_offset).to_bytes(4, byteorder='big')) + [0x00], optional) self._response_lock.acquire() # wait 1sec for response self._response_lock.wait(1) self._response_lock.release() self._cmd_lock.release() - - + def _send_UTE_response(self, data, optional): + self.logger.debug("_send_UTE_response method called") + choice = data[0] + payload = data[1:-5] + # sender_id = int.from_bytes(data[-5:-1], byteorder='big', signed=False) + # status = data[-1] + # repeater_cnt = status & 0x0F + db = 0xFF + Secu = 0x0 + # payload[0] = 0x91: EEP Teach-in response, Request accepted, teach-in successful, bidirectional + self._send_radio_packet(self.learn_id, choice, [0x91, payload[1], payload[2], payload[3], payload[4], payload[5], payload[6]], [PACKET_TYPE.RADIO_SUB_TEL, data[-5], data[-4], data[-3], data[-2], db, Secu] ) + self.UTE_listen = False + self.logger.info("Sent UTE response and ended listening") + + def _rocker_sequence(self, item, sender_id, sequence): + if self.log_for_debug: + self.logger.debug("_rocker_sequence method called") + try: + for step in sequence: + event, relation, delay = step.split() + # self.logger.debug("waiting for {} {} {}".format(event, relation, delay)) + if item._enocean_rs_events[event.upper()].wait(float(delay)) != (relation.upper() == "WITHIN"): + if self.log_for_debug: + self.logger.debug(f"NOT {step} - aborting sequence!") + return + else: + if self.log_for_debug: + self.logger.debug(f"{step}") + item._enocean_rs_events[event.upper()].clear() + continue + value = True + if 'enocean_rocker_action' in item.conf: + if item.conf['enocean_rocker_action'].upper() == "UNSET": + value = False + elif item.conf['enocean_rocker_action'].upper() == "TOGGLE": + value = not item() + item(value, self.get_shortname(), "{:08X}".format(sender_id)) + except Exception as e: + self.logger.error(f'Error handling enocean_rocker_sequence \"{sequence}\" - {e}') + + def _send_packet(self, packet_type, data=[], optional=[]): + # self.logger.debug("_send_packet method called") + length_optional = len(optional) + if length_optional > 255: + self.logger.error(f"Optional too long ({length_optional} bytes, 255 allowed)") + return + length_data = len(data) + if length_data > 65535: + self.logger.error(f"Data too long ({length_data} bytes, 65535 allowed)") + return + + packet = bytearray([PACKET.SYNC_BYTE]) + packet += length_data.to_bytes(2, byteorder='big') + bytes([length_optional, packet_type]) + packet += bytes([self.crc(packet[1:5])]) + packet += bytes(data + optional) + packet += bytes([self.crc(packet[6:])]) + self.logger.info("Sending packet with len = {} / data = [{}]!".format(len(packet), ', '.join(['0x%02x' % b for b in packet]))) + + # check connection, reconnect + if not self.is_connected(): + self.logger.debug("Trying serial reinit") + if not self.connect(startup=False): + self.logger.error('Connection failed, not sending.') + return + try: + self._tcm.write(packet) + return + except Exception as e: + self.logger.error(f"Exception during tcm write occurred: {e}") + self.logger.debug("Trying serial reinit after failed write") + + if not self.connect(startup=False): + self.logger.error('Connection failed again, not sending. Giving up.') + return + + try: + self._tcm.write(packet) + except Exception as e: + self.logger.error(f"Writing failed twice, giving up: {e}") + + def _process_packet_type_event(self, data, optional): + if self.log_for_debug: + self.logger.debug("_process_packet_type_event method called") + event_code = data[0] + if event_code == EVENT.RECLAIM_NOT_SUCCESSFUL: + self.logger.error("SA reclaim was not successful") + elif event_code == EVENT.CONFIRM_LEARN: + self.logger.info("Requesting how to handle confirm/discard learn in/out") + elif event_code == EVENT.LEARN_ACK: + self.logger.info("SA lern acknowledged") + elif event_code == EVENT.READY: + self.logger.info("Controller is ready for operation") + elif event_code == EVENT.TRANSMIT_FAILED: + self.logger.error("Telegram transmission failed") + elif event_code == EVENT.DUTYCYCLE_LIMIT: + self.logger.warning("Duty cycle limit reached") + elif event_code == EVENT.EVENT_SECUREDEVICES: + self.logger.info("Secure device event packet received") + else: + self.logger.warning("Unknown event packet received") + + def _process_packet_type_radio(self, data, optional): + if self.log_for_debug: + self.logger.debug("_process_packet_type_radio method called") + # self.logger.warning("Processing radio message with data = [{}] / optional = [{}]".format(', '.join(['0x%02x' % b for b in data]), ', '.join(['0x%02x' % b for b in optional]))) + + choice = data[0] + payload = data[1:-5] + sender_id = int.from_bytes(data[-5:-1], byteorder='big', signed=False) + status = data[-1] + repeater_cnt = status & 0x0F + self.logger.info("Radio message: choice = {:02x} / payload = [{}] / sender_id = {:08X} / status = {} / repeat = {}".format(choice, ', '.join(['0x%02x' % b for b in payload]), sender_id, status, repeater_cnt)) + + if len(optional) == 7: + subtelnum = optional[0] + dest_id = int.from_bytes(optional[1:5], byteorder='big', signed=False) + dBm = -optional[5] + SecurityLevel = optional[6] + if self.log_for_debug: + self.logger.debug(f"Radio message with additional info: subtelnum = {subtelnum} / dest_id = {dest_id:08X} / signal = {dBm} dBm / SecurityLevel = {SecurityLevel}") + if choice == 0xD4 and self.UTE_listen: + self.logger.info("Call send_UTE_response") + self._send_UTE_response(data, optional) + + if sender_id in self._rx_items: + if self.log_for_debug: + self.logger.debug("Sender ID found in item list") + # iterate over all eep known for this id and get list of associated items + for eep, items in self._rx_items[sender_id].items(): + # check if choice matches first byte in eep (this seems to be the only way to find right eep for this particular packet) + if eep.startswith("{:02X}".format(choice)): + # call parser for particular eep - returns dictionary with key-value pairs + results = self.eep_parser(eep, payload, status) + if self.log_for_debug: + self.logger.debug(f"Radio message results = {results}") + if 'DEBUG' in results: + self.logger.warning("DEBUG Info: processing radio message with data = [{}] / optional = [{}]".format(', '.join(['0x%02x' % b for b in data]), ', '.join(['0x%02x' % b for b in optional]))) + self.logger.warning(f"Radio message results = {results}") + self.logger.warning("Radio message: choice = {:02x} / payload = [{}] / sender_id = {:08X} / status = {} / repeat = {}".format(choice, ', '.join(['0x%02x' % b for b in payload]), sender_id, status, repeater_cnt)) + + for item in items: + rx_key = item.conf['enocean_rx_key'].upper() + if rx_key in results: + if 'enocean_rocker_sequence' in item.conf: + try: + if hasattr(item, '_enocean_rs_thread') and item._enocean_rs_thread.is_alive(): + if results[rx_key]: + if self.log_for_debug: + self.logger.debug("Sending pressed event") + item._enocean_rs_events["PRESSED"].set() + else: + if self.log_for_debug: + self.logger.debug("Sending released event") + item._enocean_rs_events["RELEASED"].set() + elif results[rx_key]: + item._enocean_rs_events = {'PRESSED': threading.Event(), 'RELEASED': threading.Event()} + item._enocean_rs_thread = threading.Thread(target=self._rocker_sequence, name="enocean-rs", args=(item, sender_id, item.conf['enocean_rocker_sequence'].split(','), )) + # self.logger.info("starting enocean_rocker_sequence thread") + item._enocean_rs_thread.start() + except Exception as e: + self.logger.error(f"Error handling enocean_rocker_sequence: {e}") + else: + item(results[rx_key], self.get_shortname(), f"{sender_id:08X}") + elif sender_id <= self.tx_id + 127 and sender_id >= self.tx_id: + if self.log_for_debug: + self.logger.debug("Received repeated enocean stick message") + else: + self.unknown_sender_id = f"{sender_id:08X}" + if self._log_unknown_msg: + self.logger.info(f"Unknown ID = {sender_id:08X}") + self.logger.warning("Unknown device sent radio message: choice = {:02x} / payload = [{}] / sender_id = {:08X} / status = {} / repeat = {}".format(choice, ', '.join(['0x%02x' % b for b in payload]), sender_id, status, repeater_cnt)) + + def _process_packet_type_smart_ack_command(self, data, optional): + self.logger.warning("Smart acknowledge command 0x06 received but not supported at the moment") + + def _process_packet_type_response(self, data, optional): + if self.log_for_debug: + self.logger.debug("_process_packet_type_response method called") + + # handle sent packet + if self._last_cmd_code == PACKET.SENT_RADIO and len(data) == 1: + + if self.log_for_debug: + self.logger.debug(f"Sending command returned code = {RETURN_CODE(data[0])}") + + # handle common commands + elif self._last_packet_type == PACKET_TYPE.COMMON_COMMAND: + + if self._last_cmd_code == COMMON_COMMAND.WR_RESET and len(data) == 1: + self.logger.info(f"Reset returned code = {RETURN_CODE(data[0])}") + + elif self._last_cmd_code == COMMON_COMMAND.WR_LEARNMODE and len(data) == 1: + self.logger.info(f"Write LearnMode returned code = {RETURN_CODE(data[0])}") + + elif self._last_cmd_code == COMMON_COMMAND.RD_VERSION: + if data[0] == 0 and len(data) == 33: + self.logger.info("Chip ID = 0x{} / Chip Version = 0x{}".format(''.join(['%02x' % b for b in data[9:13]]), ''.join(['%02x' % b for b in data[13:17]]))) + self.logger.info("APP version = {} / API version = {} / App description = {}".format('.'.join(['%d' % b for b in data[1:5]]), '.'.join(['%d' % b for b in data[5:9]]), ''.join(['%c' % b for b in data[17:33]]))) + elif data[0] == 0 and len(data) == 0: + self.logger.error("Reading version: No answer") + else: + self.logger.error(f"Reading version returned code = {RETURN_CODE(data[0])}, length = {len(data)}") + + elif self._last_cmd_code == COMMON_COMMAND.RD_IDBASE: + if data[0] == 0 and len(data) == 5: + self.logger.info("Base ID = 0x{}".format(''.join(['%02x' % b for b in data[1:5]]))) + if self.tx_id == 0: + self.tx_id = int.from_bytes(data[1:5], byteorder='big', signed=False) + self.logger.info("Transmit ID set set automatically by reading chips BaseID") + if len(optional) == 1: + self.logger.info(f"Remaining write cycles for Base ID = {optional[0]}") + elif data[0] == 0 and len(data) == 0: + self.logger.error("Reading Base ID: No answer") + else: + self.logger.error(f"Reading Base ID returned code = {RETURN_CODE(data[0])} and {len(data)} bytes") + + elif self._last_cmd_code == COMMON_COMMAND.WR_BIST: + if data[0] == 0 and len(data) == 2: + if data[1] == 0: + self.logger.info("Built in self test result: All OK") + else: + self.logger.info(f"Built in self test result: Problem, code = {data[1]}") + elif data[0] == 0 and len(data) == 0: + self.logger.error("Doing built in self test: No answer") + else: + self.logger.error(f"Doing built in self test returned code = {RETURN_CODE(data[0])}") + + elif self._last_cmd_code == COMMON_COMMAND.RD_LEARNMODE: + if data[0] == 0 and len(data) == 2: + self.logger.info("Reading LearnMode = 0x{}".format(''.join(['%02x' % b for b in data[1]]))) + if len(optional) == 1: + self.logger.info("Learn channel = {}".format(optional[0])) + elif data[0] == 0 and len(data) == 0: + self.logger.error("Reading LearnMode: No answer") + + elif self._last_cmd_code == COMMON_COMMAND.RD_NUMSECUREDEVICES: + if data[0] == 0 and len(data) == 2: + self.logger.info("Number of taught in devices = 0x{}".format(''.join(['%02x' % b for b in data[1]]))) + elif data[0] == 0 and len(data) == 0: + self.logger.error("Reading NUMSECUREDEVICES: No answer") + elif data[0] == 2 and len(data) == 1: + self.logger.error("Reading NUMSECUREDEVICES: Command not supported") + else: + self.logger.error("Reading NUMSECUREDEVICES: Unknown error") + elif self._last_packet_type == PACKET_TYPE.SMART_ACK_COMMAND: + + # handle SmartAck commands + if self._last_cmd_code == SMART_ACK.WR_LEARNMODE: + self.logger.info(f"Setting SmartAck mode returned code = {RETURN_CODE(data[0])}") + + elif self._last_cmd_code == SMART_ACK.RD_LEARNEDCLIENTS: + if data[0] == 0: + self.logger.info(f"Number of smart acknowledge mailboxes = {int((len(data)-1)/9)}") + else: + self.logger.error(f"Requesting SmartAck mailboxes returned code = {RETURN_CODE(data[0])}") + else: + self.logger.error("Processing unexpected response with return code = {} / data = [{}] / optional = [{}]".format(RETURN_CODE(data[0]), ', '.join(['0x%02x' % b for b in data]), ', '.join(['0x%02x' % b for b in optional]))) + + self._response_lock.acquire() + self._response_lock.notify() + self._response_lock.release() + +# +# Definitions of Learn Methods +# -#################################################### -### --- START - Definitions of Learn Methods --- ### -#################################################### def send_learn_protocol(self, id_offset=0, device=10): - self.logger.debug("Call function << send_learn_protocol >>") + self.logger.debug("send_learn_protocol method called") # define RORG - rorg = 0xA5 - + rorg = RORG.BS4 + # check offset range between 0 and 127 - if (id_offset < 0) or (id_offset > 127): + if not 0 <= id_offset <= 127: self.logger.error(f'ID offset with value = {id_offset} out of range (0-127). Aborting.') return False + # device range 10 - 19 --> Learn protocol for switch actuators - elif (device == 10): + if device == 10: + # Prepare Data for Eltako switch FSR61, Eltako FSVA-230V payload = [0xE0, 0x40, 0x0D, 0x80] self.logger.info('Sending learn telegram for switch command with [Device], [ID-Offset], [RORG], [payload] / [{}], [{:#04x}], [{:#04x}], [{}]'.format(device, id_offset, rorg, ', '.join('{:#04x}'.format(x) for x in payload))) + # device range 20 - 29 --> Learn protocol for dim actuators - elif (device == 20): + elif device == 20: + # Only for Eltako FSUD-230V payload = [0x02, 0x00, 0x00, 0x00] self.logger.info('Sending learn telegram for dim command with [Device], [ID-Offset], [RORG], [payload] / [{}], [{:#04x}], [{:#04x}], [{}]'.format(device, id_offset, rorg, ', '.join('{:#04x}'.format(x) for x in payload))) - elif (device == 21): + elif device == 21: + # For Eltako FHK61SSR dim device (EEP A5-38-08) payload = [0xE0, 0x40, 0x0D, 0x80] self.logger.info('Sending learn telegram for dim command with [Device], [ID-Offset], [RORG], [payload] / [{}], [{:#04x}], [{:#04x}], [{}]'.format(device, id_offset, rorg, ', '.join('{:#04x}'.format(x) for x in payload))) - elif (device == 22): + elif device == 22: + # For Eltako FRGBW71L RGB dim devices (EEP 07-3F-7F) payload = [0xFF, 0xF8, 0x0D, 0x87] self.logger.info('Sending learn telegram for rgbw dim command with [Device], [ID-Offset], [RORG], [payload] / [{}], [{:#04x}], [{:#04x}], [{}]'.format(device, id_offset, rorg, ', '.join('{:#04x}'.format(x) for x in payload))) + # device range 30 - 39 --> Learn protocol for radiator valves - elif (device == 30): + elif device == 30: + # Radiator Valve payload = [0x00, 0x00, 0x00, 0x00] self.logger.info('Sending learn telegram for radiator valve with [Device], [ID-Offset], [RORG], [payload] / [{}], [{:#04x}], [{:#04x}], [{}]'.format(device, id_offset, rorg, ', '.join('{:#04x}'.format(x) for x in payload))) + # device range 40 - 49 --> Learn protocol for other actuators - elif (device == 40): + elif device == 40: + # Eltako shutter actor FSB14, FSB61, FSB71 payload = [0xFF, 0xF8, 0x0D, 0x80] self.logger.info('Sending learn telegram for actuator with [Device], [ID-Offset], [RORG], [payload] / [{}], [{:#04x}], [{:#04x}], [{}]'.format(device, id_offset, rorg, ', '.join('{:#04x}'.format(x) for x in payload))) else: - self.logger.error(f'Sending learn telegram with invalid device! Device {device} actually not defined!') + self.logger.error(f'Sending learn telegram with invalid device! Device {device} currently not defined!') return False + # Send radio package self._send_radio_packet(id_offset, rorg, payload) return True - def start_UTE_learnmode(self, id_offset=0): - self.logger.debug("Call function << start_UTE_learnmode >>") + self.logger.debug("start_UTE_learnmode method called") self.UTE_listen = True self.learn_id = id_offset self.logger.info("Listening for UTE package ('D4')") - - + def enter_learn_mode(self, onoff=1): - self.logger.debug("Call function << enter_learn_mode >>") - if (onoff == 1): - self._send_common_command(CO_WR_LEARNMODE, [0x01, 0x00, 0x00, 0x00, 0x00],[0xFF]) + self.logger.debug("enter_learn_mode method called") + if onoff == 1: + self._send_common_command(COMMON_COMMAND.WR_LEARNMODE, [0x01, 0x00, 0x00, 0x00, 0x00], [0xFF]) self.logger.info("Entering learning mode") - return None else: - self._send_common_command(CO_WR_LEARNMODE, [0x00, 0x00, 0x00, 0x00, 0x00],[0xFF]) + self._send_common_command(COMMON_COMMAND.WR_LEARNMODE, [0x00, 0x00, 0x00, 0x00, 0x00], [0xFF]) self.logger.info("Leaving learning mode") - return None - # This function enables/disables the controller's smart acknowledge mode def set_smart_ack_learn_mode(self, onoff=1): - self.logger.debug("Call function << set_smart_ack_learn_mode >>") - if (onoff == 1): - self._send_smart_ack_command(SA_WR_LEARNMODE, [0x01, 0x00, 0x00, 0x00, 0x00, 0x00]) + self.logger.debug("set_smart_ack_learn_mode method called") + if onoff == 1: + self._send_smart_ack_command(SMART_ACK.WR_LEARNMODE, [0x01, 0x00, 0x00, 0x00, 0x00, 0x00]) self.logger.info("Enabling smart acknowledge learning mode") - return None else: - self._send_smart_ack_command(SA_WR_LEARNMODE, [0x00, 0x00, 0x00, 0x00, 0x00, 0x00]) + self._send_smart_ack_command(SMART_ACK.WR_LEARNMODE, [0x00, 0x00, 0x00, 0x00, 0x00, 0x00]) self.logger.info("Disabling smart acknowledge learning mode") - return None - -################################################## -### --- END - Definitions of Learn Methods --- ### -################################################## - - -################################# -### --- START - Calc CRC8 --- ### -################################# - def _calc_crc8(self, msg, crc=0): - #self.logger.debug("Call function << _calc_crc8 >>") - for i in msg: - crc = FCSTAB[crc ^ i] - return crc - -############################### -### --- END - Calc CRC8 --- ### -############################### - - diff --git a/enocean/plugin.yaml b/enocean/plugin.yaml index ebaf6538b..e7f2c4a44 100755 --- a/enocean/plugin.yaml +++ b/enocean/plugin.yaml @@ -16,11 +16,11 @@ plugin: # url of the support thread support: https://knx-user-forum.de/forum/supportforen/smarthome-py/26542-featurewunsch-enocean-plugin/page13 - version: 1.4.0 # Plugin version - sh_minversion: 1.3 # minimum shNG version to use this plugin + version: 1.4.2 # Plugin version + sh_minversion: 1.9 # minimum shNG version to use this plugin #sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) - multi_instance: False # plugin supports multi instance - restartable: unknown + multi_instance: false # plugin supports multi instance + restartable: true classname: EnOcean # class containing the plugin parameters: @@ -46,6 +46,19 @@ parameters: en: 'Log messages from unknown devices to logfile' default: 'False' + retry: + type: int + description: + de: 'Anzahl der Verbindungsversuche' + en: 'Number of connect retries' + default: 10 + + retry_cycle: + type: int + description: + de: 'Pause zwischen Verbindungsversuchen (in Sekunden)' + en: 'pause interval between connect retries (in seconds)' + default: 5 item_attributes: # Definition of item attributes defined by this plugin diff --git a/enocean/protocol/__init__.py b/enocean/protocol/__init__.py new file mode 100644 index 000000000..c54f0df93 --- /dev/null +++ b/enocean/protocol/__init__.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 +# vim: set encoding=utf-8 tabstop=4 softtabstop=4 shiftwidth=4 expandtab +######################################################################### +######################################################################### +# Enocean plugin for SmartHomeNG. https://github.com/smarthomeNG// +# +# This plugin is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This plugin is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this plugin. If not, see . +######################################################################### + +# this module contains EnOcean protocol routines + + +class CRC(): + """ provides CRC calculations """ + + CRC_TABLE = ( + 0x00, 0x07, 0x0e, 0x09, 0x1c, 0x1b, 0x12, 0x15, + 0x38, 0x3f, 0x36, 0x31, 0x24, 0x23, 0x2a, 0x2d, + 0x70, 0x77, 0x7e, 0x79, 0x6c, 0x6b, 0x62, 0x65, + 0x48, 0x4f, 0x46, 0x41, 0x54, 0x53, 0x5a, 0x5d, + 0xe0, 0xe7, 0xee, 0xe9, 0xfc, 0xfb, 0xf2, 0xf5, + 0xd8, 0xdf, 0xd6, 0xd1, 0xc4, 0xc3, 0xca, 0xcd, + 0x90, 0x97, 0x9e, 0x99, 0x8c, 0x8b, 0x82, 0x85, + 0xa8, 0xaf, 0xa6, 0xa1, 0xb4, 0xb3, 0xba, 0xbd, + 0xc7, 0xc0, 0xc9, 0xce, 0xdb, 0xdc, 0xd5, 0xd2, + 0xff, 0xf8, 0xf1, 0xf6, 0xe3, 0xe4, 0xed, 0xea, + 0xb7, 0xb0, 0xb9, 0xbe, 0xab, 0xac, 0xa5, 0xa2, + 0x8f, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9d, 0x9a, + 0x27, 0x20, 0x29, 0x2e, 0x3b, 0x3c, 0x35, 0x32, + 0x1f, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0d, 0x0a, + 0x57, 0x50, 0x59, 0x5e, 0x4b, 0x4c, 0x45, 0x42, + 0x6f, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7d, 0x7a, + 0x89, 0x8e, 0x87, 0x80, 0x95, 0x92, 0x9b, 0x9c, + 0xb1, 0xb6, 0xbf, 0xb8, 0xad, 0xaa, 0xa3, 0xa4, + 0xf9, 0xfe, 0xf7, 0xf0, 0xe5, 0xe2, 0xeb, 0xec, + 0xc1, 0xc6, 0xcf, 0xc8, 0xdd, 0xda, 0xd3, 0xd4, + 0x69, 0x6e, 0x67, 0x60, 0x75, 0x72, 0x7b, 0x7c, + 0x51, 0x56, 0x5f, 0x58, 0x4d, 0x4a, 0x43, 0x44, + 0x19, 0x1e, 0x17, 0x10, 0x05, 0x02, 0x0b, 0x0c, + 0x21, 0x26, 0x2f, 0x28, 0x3d, 0x3a, 0x33, 0x34, + 0x4e, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5c, 0x5b, + 0x76, 0x71, 0x78, 0x7f, 0x6A, 0x6d, 0x64, 0x63, + 0x3e, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2c, 0x2b, + 0x06, 0x01, 0x08, 0x0f, 0x1a, 0x1d, 0x14, 0x13, + 0xae, 0xa9, 0xa0, 0xa7, 0xb2, 0xb5, 0xbc, 0xbb, + 0x96, 0x91, 0x98, 0x9f, 0x8a, 0x8D, 0x84, 0x83, + 0xde, 0xd9, 0xd0, 0xd7, 0xc2, 0xc5, 0xcc, 0xcb, + 0xe6, 0xe1, 0xe8, 0xef, 0xfa, 0xfd, 0xf4, 0xf3 + ) + + def __call__(self, msg, crc=0): + for i in msg: + crc = self.CRC_TABLE[crc ^ i] + return crc diff --git a/enocean/protocol/constants.py b/enocean/protocol/constants.py new file mode 100644 index 000000000..6f74c9c65 --- /dev/null +++ b/enocean/protocol/constants.py @@ -0,0 +1,164 @@ +#!/usr/bin/env python3 +# vim: set encoding=utf-8 tabstop=4 softtabstop=4 shiftwidth=4 expandtab +######################################################################### +######################################################################### +# Enocean plugin for SmartHomeNG. https://github.com/smarthomeNG// +# +# This plugin is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This plugin is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this plugin. If not, see . +######################################################################### + +from enum import IntEnum + + +class PACKET(IntEnum): + """ generic packet identifiers """ + SYNC_BYTE = 0x55 + SENT_RADIO = 0xFF + SENT_ENCAPSULATED_RADIO = 0xA6 + + +class RORG(IntEnum): + """ encapsulates EEP types from EnOcean Equipment Profiles v2.61 """ + UNDEFINED = 0x00 + RPS = 0xF6 + BS1 = 0xD5 + BS4 = 0xA5 + VLD = 0xD2 + MSC = 0xD1 + ADT = 0xA6 + SM_LRN_REQ = 0xC6 + SM_LRN_ANS = 0xC7 + SM_REC = 0xA7 + SYS_EX = 0xC5 + SEC = 0x30 + SEC_ENCAPS = 0x31 + UTE = 0xD4 + + +class PACKET_TYPE(IntEnum): + """ encapsulates packet types """ + RESERVED = 0x00 + RADIO = 0x01 # RADIO ERP1 + RADIO_ERP1 = 0x01 # RADIO ERP1 => Kept for backwards compatibility reasons, for example custom packet. Generation shouldn't be affected... + RESPONSE = 0x02 # RESPONSE + RADIO_SUB_TEL = 0x03 # RADIO_SUB_TEL + EVENT = 0x04 # EVENT + COMMON_COMMAND = 0x05 # COMMON COMMAND + SMART_ACK_COMMAND = 0x06 # SMART ACK COMMAND + REMOTE_MAN_COMMAND = 0x07 # REMOTE MANAGEMENT COMMAND + RADIO_MESSAGE = 0x09 # RADIO MESSAGE + RADIO_ERP2 = 0x0A # RADIO ERP2 + RADIO_802_15_4 = 0x10 # RADIO_802_15_4_RAW_Packet + COMMAND_2_4 = 0x11 # COMMAND 2.4 GHz + + +class EVENT(IntEnum): + """ encapsulates Event Codes """ + RECLAIM_NOT_SUCCESSFUL = 0x01 # Informs the backbone of a Smart Ack Client to not successful reclaim. + CONFIRM_LEARN = 0x02 # Used for SMACK to confirm/discard learn in/out + LEARN_ACK = 0x03 # Inform backbone about result of learn request + READY = 0x04 # Inform backbone about the readiness for operation + EVENT_SECUREDEVICES = 0x05 # Informs about a secure device + DUTYCYCLE_LIMIT = 0x06 # Informs about duty cycle limit + TRANSMIT_FAILED = 0x07 # Informs that the device was not able to send a telegram. + TX_DONE = 0x08 # Informs the external host that the device has finished all transmissions. + LRN_MODE_DISABLED = 0x09 # Informs the external host that the learn mode has been disabled due to timeout. + + +class COMMON_COMMAND(IntEnum): + """ encapsulates Common Command Codes """ + WR_SLEEP = 0x01 # Enter in energy saving mode + WR_RESET = 0x02 # Reset the device + RD_VERSION = 0x03 # Read the device (SW) version /(HW) version, chip ID etc. + RD_SYS_LOG = 0x04 # Read system log from device databank + WR_SYS_LOG = 0x05 # Reset System log from device databank + WR_BIST = 0x06 # Perform built in self test + WR_IDBASE = 0x07 # Write ID range base number + RD_IDBASE = 0x08 # Read ID range base number + WR_REPEATER = 0x09 # Write Repeater Level off,1,2 + RD_REPEATER = 0x0A # Read Repeater Level off,1,2 + WR_FILTER_ADD = 0x0B # Add filter to filter list + WR_FILTER_DEL = 0x0C # Delete filter from filter list + WR_FILTER_DEL_ALL = 0x0D # Delete all filter + WR_FILTER_ENABLE = 0x0E # Enable/Disable supplied filters + RD_FILTER = 0x0F # Read supplied filters + WR_WAIT_MATURITY = 0x10 # Waiting till end of maturity time before received radio telegrams will transmitted + WR_SUBTEL = 0x11 # Enable/Disable transmitting additional subtelegram info + WR_MEM = 0x12 # Write x bytes of the Flash, XRAM, RAM0 … + RD_MEM = 0x13 # Read x bytes of the Flash, XRAM, RAM0 …. + RD_MEM_ADDRESS = 0x14 # Feedback about the used address and length of the configarea and the Smart Ack Table + RD_SECURITY = 0x15 # Read own security information (level, key) + WR_SECURITY = 0x16 # Write own security information (level, key) + WR_LEARNMODE = 0x17 # Function: Enables or disables learn mode of Controller. + RD_LEARNMODE = 0x18 # Function: Reads the learn-mode state of Controller. + WR_SECUREDEVICE_ADD = 0x19 # Add a secure device + WR_SECUREDEVICE_DEL = 0x1A # Delete a secure device + RD_SECUREDEVICE_BY_INDEX = 0x1B # Read secure device by index + WR_MODE = 0x1C # Sets the gateway transceiver mode + RD_NUMSECUREDEVICES = 0x1D # Read number of taught in secure devices + RD_SECUREDEVICE_BY_ID = 0x1E # Read secure device by ID + WR_SECUREDEVICE_ADD_PSK = 0x1F # Add Pre-shared key for inbound secure device + WR_SECUREDEVICE_SENDTEACHIN = 0x20 # Send secure Teach-In message + WR_TEMPORARY_RLC_WINDOW = 0x21 # Set the temporary rolling-code window for every taught-in devic + RD_SECUREDEVICE_PSK = 0x22 # Read PSK + RD_DUTYCYCLE_LIMIT = 0x23 # Read parameters of actual duty cycle limit + SET_BAUDRATE = 0x24 # Modifies the baud rate of the EnOcean device + GET_FREQUENCY_INFO = 0x25 # Reads Frequency and protocol of the Device + GET_STEPCODE = 0x27 # Reads Hardware Step code and Revision of the Device + WR_REMAN_CODE = 0x2E # Set the security code to unlock Remote Management functionality via radio + WR_STARTUP_DELAY = 0x2F # Set the startup delay (time from power up until start of operation) + WR_REMAN_REPEATING = 0x30 # Select if REMAN telegrams originating from this module can be repeated + RD_REMAN_REPEATING = 0x31 # Check if REMAN telegrams originating from this module can be repeated + SET_NOISETHRESHOLD = 0x32 # Set the RSSI noise threshold level for telegram reception + GET_NOISETHRESHOLD = 0x33 # Read the RSSI noise threshold level for telegram reception + WR_RLC_SAVE_PERIOD = 0x36 # Set the period in which outgoing RLCs are saved to the EEPROM + WR_RLC_LEGACY_MODE = 0x37 # Activate the legacy RLC security mode allowing roll-over and using the RLC acceptance window for 24bit explicit RLC + WR_SECUREDEVICEV2_ADD = 0x38 # Add secure device to secure link table + RD_SECUREDEVICEV2_BY_INDEX = 0x39 # Read secure device from secure link table using the table index + WR_RSSITEST_MODE = 0x3A # Control the state of the RSSI-Test mode + RD_RSSITEST_MODE = 0x3B # Read the state of the RSSI-Test mode + WR_SECUREDEVICE_MAINTENANCEKEY = 0x3C # Add the maintenance key information into the secure link table + RD_SECUREDEVICE_MAINTENANCEKEY = 0x3D # Read by index the maintenance key information from the secure link table + WR_TRANSPARENT_MODE = 0x3E # Control the state of the transparent mode + RD_TRANSPARENT_MODE = 0x3F # Read the state of the transparent mode + WR_TX_ONLY_MODE = 0x40 # Control the state of the TX only mode + RD_TX_ONLY_MODE = 0x41 # Read the state of the TX only mode + + +class SMART_ACK(IntEnum): + """ encapsulates Smart Acknowledge codes """ + WR_LEARNMODE = 0x01 # Set/Reset Smart Ack learn mode + RD_LEARNMODE = 0x02 # Get Smart Ack learn mode state + WR_LEARNCONFIRM = 0x03 # Used for Smart Ack to add or delete a mailbox of a client + WR_CLIENTLEARNRQ = 0x04 # Send Smart Ack Learn request (Client) + WR_RESET = 0x05 # Send reset command to a Smart Ack client + RD_LEARNEDCLIENTS = 0x06 # Get Smart Ack learned sensors / mailboxes + WR_RECLAIMS = 0x07 # Set number of reclaim attempts + WR_POSTMASTER = 0x08 # Activate/Deactivate Post master functionality + + +class RETURN_CODE(IntEnum): + """ encapsulates return codes """ + OK = 0x00 + ERROR = 0x01 + NOT_SUPPORTED = 0x02 + WRONG_PARAM = 0x03 + OPERATION_DENIED = 0x04 + + +class PARSE_RESULT(IntEnum): + """ encapsulates parsing return codes """ + OK = 0x00 + INCOMPLETE = 0x01 + CRC_MISMATCH = 0x03 diff --git a/enocean/eep_parser.py b/enocean/protocol/eep_parser.py similarity index 71% rename from enocean/eep_parser.py rename to enocean/protocol/eep_parser.py index e017f7014..d9d90def3 100755 --- a/enocean/eep_parser.py +++ b/enocean/protocol/eep_parser.py @@ -1,26 +1,58 @@ +#!/usr/bin/env python3 +# vim: set encoding=utf-8 tabstop=4 softtabstop=4 shiftwidth=4 expandtab +######################################################################### +# Copyright 2013-2014 Robert Budde robert@ing-budde.de +# Copyright 2014 Alexander Schwithal aschwith +######################################################################### +# Enocean plugin for SmartHomeNG. https://github.com/smarthomeNG// +# +# This plugin is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This plugin is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this plugin. If not, see . +######################################################################### + import logging + class EEP_Parser(): - def __init__(self): + def __init__(self, plg_logger=None): self.logger = logging.getLogger(__name__) - self.logger.info('Eep-parser instantiated') + self.logger.info('EEP-parser instantiated') + + # create plugin logger for errors + self.plg_logger = plg_logger + if not self.plg_logger: + self.plg_logger = self.logger def CanParse(self, eep): found = callable(getattr(self, "_parse_eep_" + eep, None)) - if (not found): + if not found: self.logger.error(f"eep-parser: missing parser for eep {eep} - there should be a _parse_eep_{eep}-function!") return found - def Parse(self, eep, payload, status): - #self.logger.debug('Parser called with eep = {} / payload = {} / status = {}'.format(eep, ', '.join(hex(x) for x in payload), hex(status))) - results = getattr(self, "_parse_eep_" + eep)(payload, status) - #self.logger.info('Parser returns {results}') + def __call__(self, eep, payload, status): + # self.logger.debug('Parser called with eep = {} / payload = {} / status = {}'.format(eep, ', '.join(hex(x) for x in payload), hex(status))) + try: + results = getattr(self, "_parse_eep_" + eep)(payload, status) + except Exception as e: + self.plg_logger.warning(f'EEP-Parser: error on parsing eep {eep}: {e}') + return + + # self.logger.info('Parser returns {results}') return results -##################################################### -### --- Definitions for RORG = A5 / ORG = 07 --- ### -##################################################### +# Definitions for RORG = A5 / ORG = 07 + def _parse_eep_A5_02_01(self, payload, status): return {'TMP': (0 - (payload[2] * 40 / 255))} @@ -112,23 +144,23 @@ def _parse_eep_A5_04_02(self, payload, status): # temperature in degree Celsius from -20.0 degC - 60degC result['TMP'] = -20.0 + (payload[2] / 250.0 * 80.0) return result - + def _parse_eep_A5_06_01(self, payload, status): # Brightness sensor, for example Eltako FAH60 self.logger.debug('Parsing A5_06_01: Brightness sensor') result = {} # Calculation of brightness in lux - if (payload[3] == 0x0F) and (payload[1] > 0x00) and (payload[1] <= 0xFF): + if payload[3] == 0x0F and payload[1] > 0x00 and payload[1] <= 0xFF: # If Data-Messege AND DataByte 2 is between: 0x00 = 300 lux and 0xFF = 30.000 lux result['BRI'] = round(((payload[1] / 255.0 * (30000 - 300)) + 300), 2) - elif (payload[3] == 0x0F) and (payload[1] == 0x00): + elif payload[3] == 0x0F and payload[1] == 0x00: # If Data-Messege AND DataByte 2: 0x00 then read DataByte 3 - result['BRI'] = (payload[0]) + result['BRI'] = payload[0] else: # No Data Message - result['BRI'] = (-1) + result['BRI'] = -1 # only trigger the logger info when 'BRI' > 0 - if (result['BRI'] > 0): + if result['BRI'] > 0: self.logger.info(f"Brightness: {result['BRI']}") return result @@ -136,7 +168,7 @@ def _parse_eep_A5_07_03(self, payload, status): # Occupancy sensor with supply voltage monitor, NodOne self.logger.debug("Parsing A5_07_03: Occupancy sensor") result = {} - is_data = ((payload[3] & 0x08) == 0x08) # learn or data telegeram: 1:data, 0:learn + is_data = (payload[3] & 0x08) == 0x08 # learn or data telegeram: 1:data, 0:learn if not is_data: self.logger.info("Occupancy sensor: Received learn telegram.") return result @@ -144,9 +176,9 @@ def _parse_eep_A5_07_03(self, payload, status): if payload[0] > 250: self.logger.error(f"Occupancy sensor issued error code: {payload[0]}") else: - result['SVC'] = (payload[0] / 255.0 * 5.0) # supply voltage in volts - result['ILL'] = (payload[1] << 2) + ((payload[2] & 0xC0) >> 6) # 10 bit illumination in lux - result['PIR'] = ((payload[3] & 0x80) == 0x80) # Movement flag, 1:motion detected + result['SVC'] = payload[0] / 255.0 * 5.0 # supply voltage in volts + result['ILL'] = payload[1] << 2 + (payload[2] & 0xC0) >> 6 # 10 bit illumination in lux + result['PIR'] = (payload[3] & 0x80) == 0x80 # Movement flag, 1:motion detected self.logger.debug(f"Occupancy: PIR:{result['PIR']} illumination: {result['ILL']}lx, voltage: {result['SVC']}V") return result @@ -154,9 +186,9 @@ def _parse_eep_A5_08_01(self, payload, status): # Brightness and movement sensor, for example eltako FBH65TFB self.logger.debug("Parsing A5_08_01: Movement sensor") result = {} - result['BRI'] = (payload[1] / 255.0 * 2048) # brightness in lux - result['MOV'] = not ((payload[3] & 0x02) == 0x02) # movement - #self.logger.debug(f"Movement: {result['MOV']}, brightness: {result['BRI']}") + result['BRI'] = payload[1] / 255.0 * 2048 # brightness in lux + result['MOV'] = not (payload[3] & 0x02) == 0x02 # movement + # self.logger.debug(f"Movement: {result['MOV']}, brightness: {result['BRI']}") return result def _parse_eep_A5_11_04(self, payload, status): @@ -168,14 +200,14 @@ def _parse_eep_A5_11_04(self, payload, status): # Data_byte0 = 0x08 = Dimmer aus, 0x09 = Dimmer an self.logger.debug("Processing A5_11_04: Dimmer Status on/off") results = {} - # if !( (payload[0] == 0x02) and (payload[2] == 0x00)): + # if !( (payload[0] == 0x02 and payload[2] == 0x00)): # self.logger.error("Error in processing A5_11_04: static byte missmatch") # return results results['D'] = payload[1] - if (payload[3] == 0x08): + if payload[3] == 0x08: # Dimmer is off results['STAT'] = 0 - elif (payload[3] == 0x09): + elif payload[3] == 0x09: # Dimmer is on results['STAT'] = 1 return results @@ -184,40 +216,39 @@ def _parse_eep_A5_12_01(self, payload, status): # Status command from switch actor with powermeter, for example Eltako FSVA-230 results = {} status_byte = payload[3] - is_data = (status_byte & 0x08) == 0x08 - if(is_data == False): + is_data = status_byte & 0x08 == 0x08 + if is_data is False: self.logger.debug("Processing A5_12_01: powermeter: is learn telegram. Aborting.") return results - is_power = (status_byte & 0x04) == 0x04 - div_enum = (status_byte & 0x03) + is_power = status_byte & 0x04 == 0x04 + div_enum = status_byte & 0x03 divisor = 1.0 - if(div_enum == 0): + if div_enum == 0: divisor = 1.0 - elif(div_enum == 1): + elif div_enum == 1: divisor = 10.0 - elif(div_enum == 2): + elif div_enum == 2: divisor = 100.0 - elif(div_enum == 3): + elif div_enum == 3: divisor = 1000.0 - else: + else: self.logger.warning(f"Processing A5_12_01: Unknown enum ({div_enum}) for divisor") self.logger.debug(f"Processing A5_12_01: divisor is {divisor}") - if(is_power): + if is_power: self.logger.debug("Processing A5_12_01: powermeter: Unit is Watts") else: self.logger.debug("Processing A5_12_01: powermeter: Unit is kWh") - value = (payload[0] << 16) + (payload[1] << 8) + payload[2] - value = value / divisor + value = (payload[0] << 16 + payload[1] << 8 + payload[2]) / divisor self.logger.debug(f"Processing A5_12_01: powermeter: {value} W") # It is confirmed by Eltako that with the use of multiple repeaters in an Eltako network, values can be corrupted in random cases. # Catching these random errors via plausibility check: if value > 2300: self.logger.warning(f"A5_12_01 plausibility error: power value {value} is greater than 2300W, which is not plausible. Skipping.") - #self.logger.warning(f"A5_12_01 exception: value {value}, divisor {divisor}, divenum {div_enum}, statusPayload {status_byte}, header status {status}") - #self.logger.warning(f"A5_12_01 exception: payloads 0-3: {payload[0]},{payload[1]},{payload[2]},{payload[3]}") + # self.logger.warning(f"A5_12_01 exception: value {value}, divisor {divisor}, divenum {div_enum}, statusPayload {status_byte}, header status {status}") + # self.logger.warning(f"A5_12_01 exception: payloads 0-3: {payload[0]},{payload[1]},{payload[2]},{payload[3]}") results['DEBUG'] = 1 return results @@ -229,24 +260,24 @@ def _parse_eep_A5_20_04(self, payload, status): self.logger.debug("Processing A5_20_04") results = {} status_byte = payload[3] - #1: temperature setpoint, 0: feed temperature - TS = ((status_byte & 1 << 6) == 1 << 6) - #1: failure, 0: normal - FL = ((status_byte & 1 << 7) == 1 << 7) - #1: locked, 0: unlocked - BLS= ((status_byte& 1 << 5) == 1 << 5) + # 1: temperature setpoint, 0: feed temperature + TS = status_byte & 1 << 6 == 1 << 6 + # 1: failure, 0: normal + FL = status_byte & 1 << 7 == 1 << 7 + # 1: locked, 0: unlocked + BLS = status_byte & 1 << 5 == 1 << 5 results['BLS'] = BLS # current valve position 0-100% results['CP'] = payload[0] # Current feet temperature or setpoint - if(TS == 1): - results['TS'] = 10 + (payload[1]/255*20) + if TS == 1: + results['TS'] = 10 + payload[1] / 255 * 20 else: - results['FT'] = 20 + (payload[1]/255*60) + results['FT'] = 20 + payload[1] / 255 * 60 # Current room temperature or failure code - if (FL == 0): - results['TMP'] = 10 + (payload[2]/255*20) - else: + if FL == 0: + results['TMP'] = 10 + payload[2] / 255 * 20 + else: results['FC'] = payload[2] results['STATUS'] = status_byte return results @@ -259,7 +290,7 @@ def _parse_eep_A5_30_01(self, payload, status): self.logger.warning("A5_30_03 is learn telegram") return results # Data_byte1 = 0x00 / 0xFF - results['ALARM'] = (payload[2] == 0x00) + results['ALARM'] = payload[2] == 0x00 # Battery linear: 0-120 (bat low), 121-255(bat high) results['BAT'] = payload[1] return results @@ -276,27 +307,26 @@ def _parse_eep_A5_30_03(self, payload, status): self.logger.error("EEP A5_30_03 not according to spec.") return results # Data_byte2 = Temperatur 0...40 °C (255...0) - results['TEMP'] = 40 - (payload[1]/255*40) + results['TEMP'] = 40 - payload[1] / 255 * 40 # Data_byte1 = 0x0F = Alarm, 0x1F = kein Alarm - results['ALARM'] = (payload[2] == 0x0F) + results['ALARM'] = payload[2] == 0x0F return results - def _parse_eep_A5_38_08(self, payload, status): results = {} - if (payload[1] == 2): # Dimming + if payload[1] == 2: # Dimming results['EDIM'] = payload[2] results['RMP'] = payload[3] - results['LRNB'] = ((payload[4] & 1 << 3) == 1 << 3) - results['EDIM_R'] = ((payload[4] & 1 << 2) == 1 << 2) - results['STR'] = ((payload[4] & 1 << 1) == 1 << 1) - results['SW'] = ((payload[4] & 1 << 0) == 1 << 0) + results['LRNB'] = payload[4] & 1 << 3 == 1 << 3 + results['EDIM_R'] = payload[4] & 1 << 2 == 1 << 2 + results['STR'] = payload[4] & 1 << 1 == 1 << 1 + results['SW'] = payload[4] & 1 << 0 == 1 << 0 return results def _parse_eep_A5_3F_7F(self, payload, status): self.logger.debug("Processing A5_3F_7F") results = {'DI_3': (payload[3] & 1 << 3) == 1 << 3, 'DI_2': (payload[3] & 1 << 2) == 1 << 2, 'DI_1': (payload[3] & 1 << 1) == 1 << 1, 'DI_0': (payload[3] & 1 << 0) == 1 << 0} - results['AD_0'] = (((payload[1] & 0x03) << 8) + payload[2]) * 1.8 / pow(2, 10) + results['AD_0'] = ((payload[1] & 0x03) << 8 + payload[2]) * 1.8 / pow(2, 10) results['AD_1'] = (payload[1] >> 2) * 1.8 / pow(2, 6) results['AD_2'] = payload[0] * 1.8 / pow(2, 8) return results @@ -316,26 +346,25 @@ def _parse_eep_A5_0G_03(self, payload, status): self.logger.debug(f"eep-parser input status = {status}") results = {} runtime_s = ((payload[0] << 8) + payload[1]) / 10 - if (payload[2] == 1): + if payload[2] == 1: self.logger.debug(f"Shutter moved {runtime_s} s 'upwards'") results['MOVE'] = runtime_s * -1 - elif (payload[2] == 2): + elif payload[2] == 2: self.logger.debug(f"Shutter moved {runtime_s} s 'downwards'") results['MOVE'] = runtime_s return results -##################################################### -### --- Definitions for RORG = D2 / ORG = D2 --- ### -##################################################### +# Definitions for RORG = D2 / ORG = D2 + def _parse_eep_D2_01_07(self, payload, status): # self.logger.debug("Processing D2_01_07: VLD Switch") results = {} # self.logger.info(f'D2 Switch Feedback 0:{payload[0]} 1:{payload[1]} 2:{payload[2]}') - if (payload[2] == 0x80): + if payload[2] == 0x80: # Switch is off results['STAT'] = 0 self.logger.debug('D2 Switch off') - elif (payload[2] == 0xe4): + elif payload[2] == 0xe4: # Switch is on results['STAT'] = 1 self.logger.debug('D2 Switch on') @@ -345,55 +374,50 @@ def _parse_eep_D2_01_12(self, payload, status): # self.logger.debug("Processing D2_01_12: VLD Switch") results = {} # self.logger.info(f'D2 Switch Feedback 0:{payload[0]} 1:{payload[1]} 2:{payload[2]}') - if (payload[1] == 0x60) and (payload[2] == 0x80): + if payload[1] == 0x60 and payload[2] == 0x80: # Switch is off results['STAT_A'] = 0 self.logger.debug('D2 Switch Channel A: off') - elif (payload[1] == 0x60) and (payload[2] == 0xe4): + elif payload[1] == 0x60 and payload[2] == 0xe4: # Switch is on results['STAT_A'] = 1 self.logger.debug('D2 Channel A: Switch on') - elif (payload[1] == 0x61) and (payload[2] == 0x80): + elif payload[1] == 0x61 and payload[2] == 0x80: # Switch is off results['STAT_B'] = 0 self.logger.debug('D2 SwitchChannel A: off') - elif (payload[1] == 0x61) and (payload[2] == 0xe4): + elif payload[1] == 0x61 and payload[2] == 0xe4: # Switch is on results['STAT_B'] = 1 self.logger.debug('D2 Switch Channel B: on') return results -#################################################### -### --- Definitions for RORG = D5 / ORG = 06 --- ### -#################################################### +# Definitions for RORG = D5 / ORG = 06 + def _parse_eep_D5_00_01(self, payload, status): # Window/Door Contact Sensor, for example Eltako FTK, FTKB self.logger.debug("Processing D5_00_01: Door contact") - return {'STATUS': (payload[0] & 0x01) == 0x01} + return {'STATUS': payload[0] & 0x01 == 0x01} +# Definitions for RORG = F6 / ORG = 05 -#################################################### -### --- Definitions for RORG = F6 / ORG = 05 --- ### -#################################################### def _parse_eep_F6_02_01(self, payload, status): self.logger.debug("Processing F6_02_01: Rocker Switch, 2 Rocker, Light and Blind Control - Application Style 1") results = {} R1 = (payload[0] & 0xE0) >> 5 - EB = (payload[0] & (1<<4) == (1<<4)) R2 = (payload[0] & 0x0E) >> 1 - SA = (payload[0] & (1<<0) == (1<<0)) - NU = (status & (1<<4) == (1<<4)) + SA = payload[0] & 1 == 1 + NU = status & (1 << 4) == (1 << 4) - if (NU): + if NU: results['AI'] = (R1 == 0) or (SA and (R2 == 0)) results['AO'] = (R1 == 1) or (SA and (R2 == 1)) results['BI'] = (R1 == 2) or (SA and (R2 == 2)) results['BO'] = (R1 == 3) or (SA and (R2 == 3)) - elif (not NU) and (payload[0] == 0x00): + elif not NU and payload[0] == 0x00: results = {'AI': False, 'AO': False, 'BI': False, 'BO': False} else: self.logger.error("Parser detected invalid state encoding - check your switch!") - pass return results def _parse_eep_F6_02_02(self, payload, status): @@ -408,34 +432,34 @@ def _parse_eep_F6_02_03(self, payload, status): self.logger.debug("Processing F6_02_03: Rocker Switch, 2 Rocker") results = {} # Button A1: Dimm light down - results['AI'] = (payload[0]) == 0x10 + results['AI'] = payload[0] == 0x10 # Button A0: Dimm light up - results['AO'] = (payload[0]) == 0x30 + results['AO'] = payload[0] == 0x30 # Button B1: Dimm light down - results['BI'] = (payload[0]) == 0x50 + results['BI'] = payload[0] == 0x50 # Button B0: Dimm light up - results['BO'] = (payload[0]) == 0x70 - if (payload[0] == 0x70): + results['BO'] = payload[0] == 0x70 + if payload[0] == 0x70: results['B'] = True - elif (payload[0] == 0x50): + elif payload[0] == 0x50: results['B'] = False - elif (payload[0] == 0x30): + elif payload[0] == 0x30: results['A'] = True - elif (payload[0] == 0x10): + elif payload[0] == 0x10: results['A'] = False - return results + return results def _parse_eep_F6_10_00(self, payload, status): self.logger.debug(f"Processing F6_10_00: Mechanical Handle sends payload {payload[0]}") results = {} # Eltako defines 0xF0 for closed status. Enocean spec defines masking of lower 4 bit: - if (payload[0] & 0b11110000) == 0b11110000: + if payload[0] & 0b11110000 == 0b11110000: results['STATUS'] = 0 # Eltako defines 0xE0 for window open (horizontal) up status. Enocean spec defines the following masking: - elif (payload[0] & 0b11010000) == 0b11000000: + elif payload[0] & 0b11010000 == 0b11000000: results['STATUS'] = 1 # Eltako defines 0xD0 for open/right up status. Enocean spec defines masking of lower 4 bit: - elif (payload[0] & 0b11110000) == 0b11010000: + elif payload[0] & 0b11110000 == 0b11010000: results['STATUS'] = 2 else: self.logger.error(f"Error in F6_10_00 handle status, payload: {payload[0]} unknown") @@ -453,19 +477,19 @@ def _parse_eep_F6_0G_03(self, payload, status): ''' self.logger.debug("Processing F6_0G_03: shutter actor") self.logger.debug("payload = [{}]".format(', '.join(['0x%02X' % b for b in payload]))) - self.logger.debug("status: {}".format(status)) + self.logger.debug(f"status: {status}") results = {} - if (payload[0] == 0x70): + if payload[0] == 0x70: results['POSITION'] = 0 results['B'] = 0 - elif (payload[0] == 0x50): + elif payload[0] == 0x50: results['POSITION'] = 255 results['B'] = 0 - elif (payload[0] == 0x01): + elif payload[0] == 0x01: results['STATUS'] = 'Start moving up' results['B'] = 1 - elif (payload[0] == 0x02): + elif payload[0] == 0x02: results['STATUS'] = 'Start moving down' results['B'] = 2 - self.logger.debug('parse_eep_F6_0G_03 returns: {}'.format(results)) + self.logger.debug(f'parse_eep_F6_0G_03 returns: {results}') return results diff --git a/enocean/prepare_packet_data.py b/enocean/protocol/packet_data.py similarity index 80% rename from enocean/prepare_packet_data.py rename to enocean/protocol/packet_data.py index 79f9f76e6..e4683bdb6 100755 --- a/enocean/prepare_packet_data.py +++ b/enocean/protocol/packet_data.py @@ -22,9 +22,10 @@ import logging from lib.utils import Utils +from .constants import RORG, PACKET_TYPE -class Prepare_Packet_Data(): +class Packet_Data(): def __init__(self, plugin_instance): """ @@ -35,13 +36,13 @@ def __init__(self, plugin_instance): # Get the plugin instance from encocean class self._plugin_instance = plugin_instance - def CanDataPrepare(self, tx_eep): + def CanPrepareData(self, tx_eep): """ This Method checks if there is an available Prepare Data Method for the tx_eep """ found = callable(getattr(self, '_prepare_data_for_tx_eep_' + tx_eep, None)) - if (not found): - self.logger.error(f"enocean-CanDataPrepare: missing tx_eep for pepare send data {tx_eep} - there should be a _prepare_data_for_tx_eep_{tx_eep}-function!") + if not found: + self.logger.error(f"enocean-CanPrepareData: missing tx_eep for pepare send data {tx_eep} - there should be a _prepare_data_for_tx_eep_{tx_eep}-function!") return found def PrepareData(self, item, tx_eep): @@ -54,40 +55,34 @@ def PrepareData(self, item, tx_eep): if self._plugin_instance.has_iattr(item.conf, 'enocean_tx_id_offset'): self.logger.debug("enocean-PrepareData: item has valid enocean_tx_id_offset") id_offset = int(self._plugin_instance.get_iattr_value(item.conf, 'enocean_tx_id_offset')) - if (id_offset < 0) or (id_offset > 127): + if id_offset < 0 or id_offset > 127: self.logger.error('enocean-PrepareData: ID offset out of range (0-127). Aborting.') - return None + return else: self.logger.info(f"enocean-PrepareData: {tx_eep} item has no attribute ''enocean_tx_id_offset''! Set to default = 0") id_offset = 0 - # start prepare data + # start prepare data rorg, payload, optional = getattr(self, '_prepare_data_for_tx_eep_' + tx_eep)(item, tx_eep) - #self.logger.info('enocean-PrepareData: {} returns [{:#04x}], [{}], [{}]'.format(tx_eep, rorg, ', '.join('{:#04x}'.format(x) for x in payload), ', '.join('{:#04x}'.format(x) for x in optional))) + # self.logger.info('enocean-PrepareData: {} returns [{:#04x}], [{}], [{}]'.format(tx_eep, rorg, ', '.join('{:#04x}'.format(x) for x in payload), ', '.join('{:#04x}'.format(x) for x in optional))) return id_offset, rorg, payload, optional +# Definitions for RORG = A5 / ORG = 07 -##################################################### -### --- Definitions for RORG = A5 / ORG = 07 --- ### -### --> Definition of 4BS Telegrams ### -##################################################### - - def _prepare_data_for_tx_eep_A5_20_04(self, item, tx_eep): """ ### --- Data for radiator valve command --- ### """ self.logger.debug(f'enocean-PrepareData: prepare data for tx_eep {tx_eep}') - rorg = 0xa5 temperature = item() # define default values: - MC = 1 # off - WUC = 3 # 120 seconds - BLC = 0 # unlocked - LRNB = 1 # data - DSO = 0 # 0 degree + MC = 1 # off + WUC = 3 # 120 seconds + BLC = 0 # unlocked + LRNB = 1 # data + DSO = 0 # 0 degree valve_position = 50 - for sibling in get_children(item.parent): + for sibling in item.return_parent().get_children(): if hasattr(sibling, 'MC'): MC = sibling() if hasattr(sibling, 'WUC'): @@ -100,24 +95,22 @@ def _prepare_data_for_tx_eep_A5_20_04(self, item, tx_eep): DSO = sibling() if hasattr(sibling, 'VALVE_POSITION'): valve_position = sibling() - TSP = int((temperature -10)*255/30) - status = 0 + (MC << 1) + (WUC << 2) + TSP = int((temperature - 10) * 255 / 30) + status = 0 + (MC << 1) + (WUC << 2) status2 = (BLC << 5) + (LRNB << 4) + (DSO << 2) - payload = [valve_position, TSP, status , status2] + payload = [valve_position, TSP, status, status2] optional = [] - return rorg, payload, optional - - - def _prepare_data_for_tx_eep_A5_38_08_01(self, item, tx_eep): + return RORG.BS4, payload, optional + + def _prepare_data_for_tx_eep_A5_38_08_01(self, item, tx_eep): """ ### --- Data for A5-38_08 command 1 --- ### Eltako Devices: - FSR14-2x, FSR14-4x, FSR14SSR, FSR71 + FSR14-2x, FSR14-4x, FSR14SSR, FSR71 FSR61, FSR61NP, FSR61G, FSR61LN, FLC61NP This method has the function to prepare the packet data in case of switching device on or off """ self.logger.debug(f'enocean-PrepareData: prepare data for tx_eep {tx_eep}') - rorg = 0xa5 block = 0 # check if item has attribute block_switch if self._plugin_instance.has_iattr(item.conf, 'block_switch'): @@ -133,10 +126,9 @@ def _prepare_data_for_tx_eep_A5_38_08_01(self, item, tx_eep): payload = [0x01, 0x00, 0x00, int(9 + block)] self.logger.debug(f'enocean-PrepareData: {tx_eep} prepare data to switch on') optional = [] - return rorg, payload, optional - - - def _prepare_data_for_tx_eep_A5_38_08_02(self, item, tx_eep): + return RORG.BS4, payload, optional + + def _prepare_data_for_tx_eep_A5_38_08_02(self, item, tx_eep): """ ### --- Data for A5-38_08 command 2 --- ### Eltako Devices: @@ -145,8 +137,7 @@ def _prepare_data_for_tx_eep_A5_38_08_02(self, item, tx_eep): This method has the function to prepare the packet data in case of switching the dimmer device on or off, but calculate also the correct data of dim_speed and dim_value for further solutions. """ - #self.logger.debug(f'enocean-PrepareData: prepare data for tx_eep {tx_eep}') - rorg = 0xa5 + # self.logger.debug(f'enocean-PrepareData: prepare data for tx_eep {tx_eep}') block = 0 # check if item has attribute block_dim_value if self._plugin_instance.has_iattr(item.level.conf, 'block_dim_value'): @@ -158,7 +149,7 @@ def _prepare_data_for_tx_eep_A5_38_08_02(self, item, tx_eep): dim_speed = self._plugin_instance.get_iattr_value(item.level.conf, 'dim_speed') # bound dim_speed values to [0 - 100] % dim_speed = max(0, min(100, int(dim_speed))) - #self.logger.debug(f'enocean-PrepareData: {tx_eep} use dim_speed = {dim_speed} %') + # self.logger.debug(f'enocean-PrepareData: {tx_eep} use dim_speed = {dim_speed} %') # calculate dimspeed from percent into integer # 0x01 --> fastest speed --> 100 % # 0xFF --> slowest speed --> 0 % @@ -166,29 +157,28 @@ def _prepare_data_for_tx_eep_A5_38_08_02(self, item, tx_eep): else: # use intern dim_speed of the dim device dim_speed = 0 - #self.logger.debug('enocean-PrepareData: no attribute dim_speed --> use intern dim speed') + # self.logger.debug('enocean-PrepareData: no attribute dim_speed --> use intern dim speed') if not item(): # if value is False --> Switch off dim_value = 0 payload = [0x02, int(dim_value), int(dim_speed), int(8 + block)] - #self.logger.debug('enocean-PrepareData: prepare data to switch off for command for A5_38_08_02') + # self.logger.debug('enocean-PrepareData: prepare data to switch off for command for A5_38_08_02') else: # check if reference dim value exists if 'ref_level' in item.level.conf: dim_value = int(item.level.conf['ref_level']) # check range of dim_value [0 - 100] % dim_value = max(0, min(100, int(dim_value))) - #self.logger.debug(f'enocean-PrepareData: {tx_eep} ref_level {dim_value} % found for A5_38_08_02') + # self.logger.debug(f'enocean-PrepareData: {tx_eep} ref_level {dim_value} % found for A5_38_08_02') else: # set dim_value on 100 % == 0x64 dim_value = 0x64 self.logger.debug(f'enocean-PrepareData: {tx_eep} no ref_level found! Setting to default 100 %') payload = [0x02, int(dim_value), int(dim_speed), int(9 + block)] optional = [] - return rorg, payload, optional - - - def _prepare_data_for_tx_eep_A5_38_08_03(self, item, tx_eep): + return RORG.BS4, payload, optional + + def _prepare_data_for_tx_eep_A5_38_08_03(self, item, tx_eep): """ ### --- Data for A5-38_08 command 3--- ### Eltako Devices: @@ -198,14 +188,13 @@ def _prepare_data_for_tx_eep_A5_38_08_03(self, item, tx_eep): In case of dim_value == 0 the dimmer is switched off. """ self.logger.debug(f'enocean-PrepareData: prepare data for tx_eep {tx_eep}') - rorg = 0xa5 block = 0 # check if item has attribute block_dim_value if self._plugin_instance.has_iattr(item.conf, 'block_dim_value'): block_value = self._plugin_instance.get_iattr_value(item.conf, 'block_dim_value') if Utils.to_bool(block_value): block = 4 - # check if item has attribite dim_speed + # check if item has attribite dim_speed if self._plugin_instance.has_iattr(item.conf, 'dim_speed'): dim_speed = self._plugin_instance.get_iattr_value(item.conf, 'dim_speed') # bound dim_speed values to [0 - 100] % @@ -214,7 +203,7 @@ def _prepare_data_for_tx_eep_A5_38_08_03(self, item, tx_eep): # calculate dimspeed from percent into hex # 0x01 --> fastest speed --> 100 % # 0xFF --> slowest speed --> 0 % - dim_speed = (255 - (254 * dim_speed/100)) + dim_speed = (255 - (254 * dim_speed / 100)) else: # use intern dim_speed of the dim device dim_speed = 0x00 @@ -232,10 +221,9 @@ def _prepare_data_for_tx_eep_A5_38_08_03(self, item, tx_eep): dim_value = dim_value payload = [0x02, int(dim_value), int(dim_speed), int(9 + block)] optional = [] - return rorg, payload, optional - - - def _prepare_data_for_tx_eep_A5_3F_7F(self, item, tx_eep): + return RORG.BS4, payload, optional + + def _prepare_data_for_tx_eep_A5_3F_7F(self, item, tx_eep): """ ### --- Data for A5-3F-7F - Universal Actuator Command --- ### Eltako Devices: @@ -244,14 +232,13 @@ def _prepare_data_for_tx_eep_A5_3F_7F(self, item, tx_eep): The Runtime is set in [0 - 255] s """ self.logger.debug(f'enocean-PrepareData: prepare data for tx_eep {tx_eep}') - rorg = 0xa5 block = 0 # check if item has attribute block_switch if self._plugin_instance.has_iattr(item.conf, 'block_switch'): block_value = self._plugin_instance.get_iattr_value(item.conf, 'block_switch') if Utils.to_bool(block_value): block = 4 - # check if item has attribite enocean_rtime + # check if item has attribite enocean_rtime if self._plugin_instance.has_iattr(item.conf, 'enocean_rtime'): rtime = self._plugin_instance.get_iattr_value(item.conf, 'enocean_rtime') # rtime [0 - 255] s @@ -263,25 +250,24 @@ def _prepare_data_for_tx_eep_A5_3F_7F(self, item, tx_eep): self.logger.debug(f'enocean-PrepareData: {tx_eep} actuator runtime not specified set to {rtime} s.') # check command (up, stop, or down) command = int(item()) - if(command == 0): + if command == 0: # Stopp moving command_hex_code = 0x00 - elif(command == 1): + elif command == 1: # moving up command_hex_code = 0x01 - elif(command == 2): + elif command == 2: # moving down command_hex_code = 0x02 else: self.logger.error(f'enocean-PrepareData: {tx_eep} sending actuator command failed: invalid command {command}') - return None + return # define payload payload = [0x00, rtime, command_hex_code, int(8 + block)] optional = [] - return rorg, payload, optional - - - def _prepare_data_for_tx_eep_07_3F_7F(self, item, tx_eep): + return RORG.BS4, payload, optional + + def _prepare_data_for_tx_eep_07_3F_7F(self, item, tx_eep): """ ### --- Data for 07-3F-7F Command --- ### Eltako Devices: @@ -294,8 +280,9 @@ def _prepare_data_for_tx_eep_07_3F_7F(self, item, tx_eep): Color: bit0 = red, bit1= green, bit2 = blue, bit3 = white """ self.logger.debug(f'enocean-PrepareData: prepare data for tx_eep {tx_eep}') + # NOTE: not an official RORG value! rorg = 0x07 - # check if item has attribite dim_speed + # check if item has attribite dim_speed if self._plugin_instance.has_iattr(item.conf, 'dim_speed'): dim_speed = self._plugin_instance.get_iattr_value(item.conf, 'dim_speed') dim_speed = max(0, min(100, int(dim_speed))) @@ -303,89 +290,87 @@ def _prepare_data_for_tx_eep_07_3F_7F(self, item, tx_eep): # calculate dimspeed from percent into hex # 0x01 --> fastest speed --> 100 % # 0xFF --> slowest speed --> 0 % - dim_speed = (255 - (254 * dim_speed/100)) + dim_speed = (255 - (254 * dim_speed / 100)) else: # use intern dim_speed of the dim device dim_speed = 0x00 self.logger.debug(f'enocean-PrepareData: {tx_eep} no attribute dim_speed --> use intern dim speed') + # check the color of the item if self._plugin_instance.has_iattr(item.conf, 'color'): color = self._plugin_instance.get_iattr_value(item.conf, 'color') - if (color == 'red'): + color_hex = '' + if color == 'red': color_hex = 0x01 - elif (color == 'green'): + elif color == 'green': color_hex = 0x02 - elif (color == 'blue'): + elif color == 'blue': color_hex = 0x04 - elif (color == 'white'): + elif color == 'white': color_hex = 0x08 else: self.logger.error(f'enocean-PrepareData: {item} has no attribute color --> please specify color!') - return None + return + # Aufdimmen: [dim_speed, color_hex, 0x30, 0x0F] # Abdimmen: [dim_speed, color_hex, 0x31, 0x0F] # Dimmstop: [dim_speed, color_hex, 0x32, 0x0F] # check command (up, stop, or down) command = int(item()) - if(command == 0): + if command == 0: # dim up command_hex_code = 0x30 - elif(command == 1): + elif command == 1: # dim down command_hex_code = 0x31 - elif(command == 2): + elif command == 2: # stop dim command_hex_code = 0x32 else: self.logger.error(f'enocean-PrepareData: {tx_eep} sending actuator command failed: invalid command {command}') - return None + return # define payload payload = [int(dim_speed), color_hex, command_hex_code, 0x0F] optional = [] return rorg, payload, optional - - -############################################################# -### --- Definitions for RORG = D2 --- ### -### --> Definition EnOcean Variable Length Telegram (VLD) ### -############################################################# - def _prepare_data_for_tx_eep_D2_01_07(self, item, tx_eep): +# Definitions for RORG = D2, EnOcean Variable Length Telegram (VLD) + + def _prepare_data_for_tx_eep_D2_01_07(self, item, tx_eep): """ ### --- Data for D2_01_07 (VLD) --- ### Prepare data for Devices with Varable Length Telegram. - There is currently no device information available. - Optional 'pulsewidth' - Attribute was removed, it can be realized with the smarthomeNG + There is currently no device information available. + Optional 'pulsewidth' - Attribute was removed, it can be realized with the smarthomeNG build in function autotimer! """ self.logger.debug(f'enocean-PrepareData: prepare data for tx_eep {tx_eep}') - rorg = 0xD2 - SubTel = 0x03 db = 0xFF Secu = 0x0 if self._plugin_instance.has_iattr(item.conf, 'enocean_rx_id'): rx_id = int(self._plugin_instance.get_iattr_value(item.conf, 'enocean_rx_id'), 16) - if (rx_id < 0) or (rx_id > 0xFFFFFFFF): + if rx_id < 0 or rx_id > 0xFFFFFFFF: self.logger.error(f'enocean-PrepareData: {tx_eep} rx-ID-Offset out of range (0-127). Aborting.') - return None + return self.logger.debug(f'enocean-PrepareData: {tx_eep} enocean_rx_id found.') else: rx_id = 0 self.logger.debug(f'enocean-PrepareData: {tx_eep} no enocean_rx_id found!') # Prepare Data Packet - if (item() == 0): + if item() == 0: payload = [0x01, 0x1E, 0x00] - optional = [SubTel, rx_id, db, Secu] - elif (item() == 1): + optional = [PACKET_TYPE.RADIO_SUB_TEL, rx_id, db, Secu] + elif item() == 1: payload = [0x01, 0x1E, 0x01] - optional = [SubTel, rx_id, db, Secu] + optional = [PACKET_TYPE.RADIO_SUB_TEL, rx_id, db, Secu] else: self.logger.error(f'enocean-PrepareData: {tx_eep} undefined Value. Error!') - return None + return # packet_data_prepared = (id_offset, 0xD2, payload, [0x03, 0xFF, 0xBA, 0xD0, 0x00, 0xFF, 0x0]) self.logger.info(f'enocean-PrepareData: {tx_eep} Packet Data Prepared for {tx_eep} (VLD)') - optional = [SubTel, rx_id, db, Secu] - return rorg, payload, optional + optional = [PACKET_TYPE.RADIO_SUB_TEL, rx_id, db, Secu] + + return RORG.VLD, payload, optional def _prepare_data_for_tx_eep_D2_01_12(self, item, tx_eep): """ @@ -396,24 +381,22 @@ def _prepare_data_for_tx_eep_D2_01_12(self, item, tx_eep): build in function autotimer! """ self.logger.debug(f'enocean-PrepareData: prepare data for tx_eep {tx_eep}') - rorg = 0xD2 - SubTel = 0x03 db = 0xFF Secu = 0x0 if self._plugin_instance.has_iattr(item.conf, 'enocean_rx_id'): rx_id = int(self._plugin_instance.get_iattr_value(item.conf, 'enocean_rx_id'), 16) - if (rx_id < 0) or (rx_id > 0xFFFFFFFF): + if rx_id < 0 or rx_id > 0xFFFFFFFF: self.logger.error(f'enocean-PrepareData: {tx_eep} rx-ID-Offset out of range (0-127). Aborting.') - return None + return self.logger.debug(f'enocean-PrepareData: {tx_eep} enocean_rx_id found.') else: rx_id = 0 self.logger.debug(f'enocean-PrepareData: {tx_eep} no enocean_rx_id found!') if self._plugin_instance.has_iattr(item.conf, 'enocean_channel'): schannel = self._plugin_instance.get_iattr_value(item.conf, 'enocean_channel') - if (schannel == "A"): + if schannel == "A": channel = 0x00 - elif (schannel == "B"): + elif schannel == "B": channel = 0x01 else: channel = 0x1E @@ -422,16 +405,17 @@ def _prepare_data_for_tx_eep_D2_01_12(self, item, tx_eep): channel = 0x1E self.logger.debug(f'enocean-PrepareData: {tx_eep} no enocean_channel found!') # Prepare Data Packet - if (item() == 0): + if item() == 0: payload = [0x01, channel, 0x00] - optional = [SubTel, rx_id, db, Secu] - elif (item() == 1): + optional = [PACKET_TYPE.RADIO_SUB_TEL, rx_id, db, Secu] + elif item() == 1: payload = [0x01, channel, 0x01] - optional = [SubTel, rx_id, db, Secu] + optional = [PACKET_TYPE.RADIO_SUB_TEL, rx_id, db, Secu] else: self.logger.error(f'enocean-PrepareData: {tx_eep} undefined Value. Error!') - return None + return # packet_data_prepared = (id_offset, 0xD2, payload, [0x03, 0xFF, 0xBA, 0xD0, 0x00, 0xFF, 0x0]) self.logger.info(f'enocean-PrepareData: {tx_eep} Packet Data Prepared for {tx_eep} (VLD)') - optional = [SubTel, rx_id, db, Secu] - return rorg, payload, optional + optional = [PACKET_TYPE.RADIO_SUB_TEL, rx_id, db, Secu] + + return RORG.VLD, payload, optional From 4735160133ec1a809641b7e21273740b92e3d1f3 Mon Sep 17 00:00:00 2001 From: Morg42 <43153739+Morg42@users.noreply.github.com> Date: Thu, 27 Jun 2024 20:04:15 +0200 Subject: [PATCH 16/23] enocean: added 0 connect retries for unlimited --- enocean/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/enocean/__init__.py b/enocean/__init__.py index a6c425914..2a0f9c241 100755 --- a/enocean/__init__.py +++ b/enocean/__init__.py @@ -95,7 +95,7 @@ def run(self): # just try connecting anytime the serial object is not initialized connect_count = 0 while self._tcm is None and self.alive: - if connect_count >= self._connect_retries: + if self._connect_retries > 0 and connect_count >= self._connect_retries: self.alive = False break if not self.connect(): From 848fd7a16de7d595a1b3410bb882408f9984c419 Mon Sep 17 00:00:00 2001 From: Morg42 <43153739+Morg42@users.noreply.github.com> Date: Fri, 28 Jun 2024 08:48:59 +0200 Subject: [PATCH 17/23] enocean: fix plugin.yaml --- enocean/plugin.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/enocean/plugin.yaml b/enocean/plugin.yaml index e7f2c4a44..e8eaaf319 100755 --- a/enocean/plugin.yaml +++ b/enocean/plugin.yaml @@ -49,8 +49,8 @@ parameters: retry: type: int description: - de: 'Anzahl der Verbindungsversuche' - en: 'Number of connect retries' + de: 'Anzahl der Verbindungsversuche (0 = kein Limit)' + en: 'Number of connect retries (0 = no limit)' default: 10 retry_cycle: From cc8c556a093510c37708b3b8d82991772303d40c Mon Sep 17 00:00:00 2001 From: Morg42 <43153739+Morg42@users.noreply.github.com> Date: Fri, 28 Jun 2024 09:00:03 +0200 Subject: [PATCH 18/23] plugins: move sh|py_min|max_version to str definition --- alexa/plugin.yaml | 2 +- alexa4p3/plugin.yaml | 2 +- alexarc4shng/plugin.yaml | 2 +- apcups/plugin.yaml | 2 +- appletv/plugin.yaml | 2 +- artnet/plugin.yaml | 2 +- asterisk/plugin.yaml | 2 +- avdevice/plugin.yaml | 2 +- avm/plugin.yaml | 2 +- avm_smarthome/plugin.yaml | 2 +- beolink/plugin.yaml | 2 +- blockly/plugin.yaml | 2 +- bose_soundtouch/plugin.yaml | 2 +- bsblan/plugin.yaml | 4 ++-- buderus/plugin.yaml | 2 +- byd_bat/plugin.yaml | 4 ++-- casambi/plugin.yaml | 2 +- cli/plugin.yaml | 2 +- co2meter/plugin.yaml | 2 +- comfoair/plugin.yaml | 2 +- darksky/plugin.yaml | 2 +- dashbutton/plugin.yaml | 2 +- database/plugin.yaml | 2 +- datalog/plugin.yaml | 2 +- db_addon/plugin.yaml | 4 ++-- deebot_ozmo/plugin.yaml | 2 +- denon/plugin.yaml | 4 ++-- dlms/plugin.yaml | 4 ++-- dmx/plugin.yaml | 2 +- drexelundweiss/plugin.yaml | 2 +- ebus/plugin.yaml | 2 +- enigma2/plugin.yaml | 2 +- enocean/plugin.yaml | 2 +- epson/plugin.yaml | 4 ++-- eta_pu/plugin.yaml | 2 +- executor/plugin.yaml | 4 ++-- garminconnect/plugin.yaml | 2 +- gpio/plugin.yaml | 2 +- harmony/plugin.yaml | 4 ++-- helios/plugin.yaml | 2 +- helios_tcp/plugin.yaml | 2 +- homeconnect/plugin.yaml | 2 +- homematic/plugin.yaml | 4 ++-- hue/plugin.yaml | 2 +- hue2/plugin.yaml | 2 +- husky/plugin.yaml | 2 +- husky2/plugin.yaml | 2 +- ical/plugin.yaml | 2 +- indego/plugin.yaml | 2 +- indego4shng/plugin.yaml | 2 +- influxdata/plugin.yaml | 2 +- influxdb/plugin.yaml | 2 +- influxdb2/plugin.yaml | 2 +- intercom_2n/plugin.yaml | 2 +- join/plugin.yaml | 2 +- jsonread/plugin.yaml | 4 ++-- jvcproj/plugin.yaml | 2 +- kathrein/plugin.yaml | 2 +- knx/plugin.yaml | 2 +- kodi/plugin.yaml | 4 +--- kostal/plugin.yaml | 2 +- kostalmodbus/plugin.yaml | 4 ++-- ksemmodbus/plugin.yaml | 4 ++-- leveljet/plugin.yaml | 2 +- lirc/plugin.yaml | 2 +- lms/plugin.yaml | 4 ++-- logo/plugin.yaml | 2 +- luxtronic2/plugin.yaml | 2 +- mailrcv/plugin.yaml | 2 +- mailsend/plugin.yaml | 2 +- memlog/plugin.yaml | 2 +- mieleathome/plugin.yaml | 2 +- miflora/plugin.yaml | 2 +- mikrotik/plugin.yaml | 2 +- milight/plugin.yaml | 2 +- mlgw/plugin.yaml | 2 +- modbus_tcp/plugin.yaml | 4 ++-- mpd/plugin.yaml | 2 +- mqtt/plugin.yaml | 2 +- mvg_live/plugin.yaml | 2 +- neato/plugin.yaml | 2 +- network/plugin.yaml | 2 +- nuki/plugin.yaml | 2 +- nut/plugin.yaml | 2 +- odlinfo/plugin.yaml | 2 +- onewire/plugin.yaml | 2 +- openweathermap/plugin.yaml | 2 +- operationlog/plugin.yaml | 2 +- oppo/plugin.yaml | 4 ++-- philips_tv/plugin.yaml | 2 +- pioneer/plugin.yaml | 4 ++-- piratewthr/plugin.yaml | 2 +- plex/plugin.yaml | 2 +- pluggit/plugin.yaml | 2 +- prowl/plugin.yaml | 2 +- pushbullet/plugin.yaml | 2 +- pushover/plugin.yaml | 2 +- raumfeld_ng/plugin.yaml | 2 +- rcs1000n/plugin.yaml | 2 +- rcswitch/plugin.yaml | 2 +- resol/plugin.yaml | 2 +- robonect/plugin.yaml | 2 +- roomba/plugin.yaml | 2 +- roomba_980/plugin.yaml | 2 +- roombapysh/plugin.yaml | 2 +- rpi1wire/plugin.yaml | 2 +- rpi_info/plugin.yaml | 2 +- rrd/plugin.yaml | 2 +- rtr/plugin.yaml | 2 +- rtr2/plugin.yaml | 4 ++-- russound/plugin.yaml | 2 +- shelly/plugin.yaml | 2 +- simulation/plugin.yaml | 2 +- slack/plugin.yaml | 2 +- sma/plugin.yaml | 2 +- sma_em/plugin.yaml | 2 +- sma_mb/plugin.yaml | 4 ++-- smarttv/plugin.yaml | 2 +- smartvisu/plugin.yaml | 4 ++-- sml/plugin.yaml | 2 +- sml2/plugin.yaml | 2 +- smlx/plugin.yaml | 2 +- snap7_logo/plugin.yaml | 2 +- snmp/plugin.yaml | 2 +- solarforecast/plugin.yaml | 2 +- solarlog/plugin.yaml | 2 +- sonos/plugin.yaml | 4 ++-- speech/plugin.yaml | 2 +- sqlite_visu2_8/plugin.yaml | 2 +- squeezebox/plugin.yaml | 2 +- stateengine/plugin.yaml | 2 +- systemair/plugin.yaml | 2 +- tankerkoenig/plugin.yaml | 2 +- tasmota/plugin.yaml | 2 +- telegram/plugin.yaml | 2 +- text_display/plugin.yaml | 4 ++-- thz/plugin.yaml | 2 +- timmy/plugin.yaml | 4 ++-- traffic/plugin.yaml | 2 +- trovis557x/plugin.yaml | 2 +- unifi/plugin.yaml | 2 +- uzsu/plugin.yaml | 2 +- vacations/plugin.yaml | 2 +- vicare/plugin.yaml | 2 +- viessmann/plugin.yaml | 4 ++-- visu_smartvisu/plugin.yaml | 2 +- visu_websocket/plugin.yaml | 2 +- volkszaehler/plugin.yaml | 2 +- waterkotte/plugin.yaml | 2 +- webpush/plugin.yaml | 2 +- webservices/plugin.yaml | 2 +- wettercom/plugin.yaml | 2 +- withings_health/plugin.yaml | 2 +- wol/plugin.yaml | 4 ++-- wunderground/plugin.yaml | 2 +- xiaomi_vac/plugin.yaml | 2 +- xmpp/plugin.yaml | 2 +- yamaha/plugin.yaml | 2 +- yamahayxc/plugin.yaml | 2 +- zigbee2mqtt/plugin.yaml | 4 ++-- zwave/plugin.yaml | 4 ++-- 161 files changed, 187 insertions(+), 189 deletions(-) diff --git a/alexa/plugin.yaml b/alexa/plugin.yaml index 14c71a225..92455d2d4 100755 --- a/alexa/plugin.yaml +++ b/alexa/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1021150-amazon-alexa-plugin version: 1.3.1 # Plugin version - sh_minversion: 1.3 # minimum shNG version to use this plugin + sh_minversion: '1.3' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: unknown diff --git a/alexa4p3/plugin.yaml b/alexa4p3/plugin.yaml index 6f83c5f54..46db797e9 100755 --- a/alexa4p3/plugin.yaml +++ b/alexa4p3/plugin.yaml @@ -11,7 +11,7 @@ plugin: #documentation: https://www.smarthomeng.de/user/plugins/alexa4p3/user_doc.html # url of documentation support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1021150-amazon-alexa-plugin version: 1.0.2 # Plugin version - sh_minversion: 1.5.2 # minimum shNG version to use this plugin + sh_minversion: '1.5.2' # minimum shNG version to use this plugin multi_instance: False # plugin supports multi instance classname: Alexa4P3 # class containing the plugin state: ready # State of the Plugin diff --git a/alexarc4shng/plugin.yaml b/alexarc4shng/plugin.yaml index ec99a371b..96acbc432 100755 --- a/alexarc4shng/plugin.yaml +++ b/alexarc4shng/plugin.yaml @@ -9,7 +9,7 @@ plugin: tester: henfri, juergen, psilo #documentation: https://www.smarthomeng.de/user/plugins/alexarc4shng/user_doc.html # url of documentation version: 1.0.4 # Plugin version - sh_minversion: 1.5.2 # minimum shNG version to use this plugin + sh_minversion: '1.5.2' # minimum shNG version to use this plugin multi_instance: False # plugin supports multi instance classname: AlexaRc4shNG # class containing the plugin keywords: Alexa Amazon Remote Control diff --git a/apcups/plugin.yaml b/apcups/plugin.yaml index e8ba61906..e103de9df 100755 --- a/apcups/plugin.yaml +++ b/apcups/plugin.yaml @@ -13,7 +13,7 @@ plugin: #support: https://knx-user-forum.de/forum/supportforen/smarthome-py version: 1.4.0 # Plugin version - sh_minversion: 1.9 # minimum shNG version to use this plugin + sh_minversion: '1.9' # minimum shNG version to use this plugin #sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: True # plugin supports multi instance restartable: unknown diff --git a/appletv/plugin.yaml b/appletv/plugin.yaml index 43053f3d0..2534857fa 100755 --- a/appletv/plugin.yaml +++ b/appletv/plugin.yaml @@ -14,7 +14,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1223483-plugin-apple-tv version: 1.6.2 # Plugin version - sh_minversion: 1.6 # minimum shNG version to use this plugin + sh_minversion: '1.6' # minimum shNG version to use this plugin multi_instance: True # plugin supports multi instance restartable: unknown classname: AppleTV # class containing the plugin diff --git a/artnet/plugin.yaml b/artnet/plugin.yaml index b4483b8e5..1233430d4 100755 --- a/artnet/plugin.yaml +++ b/artnet/plugin.yaml @@ -12,7 +12,7 @@ plugin: # documentation: https://github.com/smarthomeNG/plugins/blob/develop/mqtt/README.md # url of documentation (wiki) page version: 1.6.1 # Plugin version - sh_minversion: 1.5.1 # minimum shNG version to use this plugin + sh_minversion: '1.5.1' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: True restartable: True # Plugin can be restarted safely, however, the data is not re-applied from items to the DMX-Values diff --git a/asterisk/plugin.yaml b/asterisk/plugin.yaml index 7a8772e9f..6201931ac 100755 --- a/asterisk/plugin.yaml +++ b/asterisk/plugin.yaml @@ -12,7 +12,7 @@ plugin: documentation: https://www.smarthomeng.de/user/plugins/asterisk/README.html support: https://knx-user-forum.de/forum/supportforen/smarthome-py/ version: 1.4.2 # Plugin version - sh_minversion: 1.9.0 # minimum shNG version to use this plugin + sh_minversion: '1.9.0' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) # py_minversion: 3.6 # minimum Python version to use for this plugin # py_maxversion: # maximum Python version to use for this plugin (leave empty if latest) diff --git a/avdevice/plugin.yaml b/avdevice/plugin.yaml index 29e05e7a0..2fdbdac68 100755 --- a/avdevice/plugin.yaml +++ b/avdevice/plugin.yaml @@ -33,7 +33,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1097870-neues-plugin-av-device-f%C3%BCr-yamaha-pioneer-denon-etc version: 1.6.4 # Plugin version - sh_minversion: 1.6 # minimum shNG version to use this plugin + sh_minversion: '1.6' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: True # plugin supports multi instance restartable: unknown diff --git a/avm/plugin.yaml b/avm/plugin.yaml index a39366590..5ec564bf1 100644 --- a/avm/plugin.yaml +++ b/avm/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/934835-avm-plugin version: 2.2.2 # Plugin version (must match the version specified in __init__.py) - sh_minversion: 1.8 # minimum shNG version to use this plugin + sh_minversion: '1.8' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) # py_minversion: 3.6 # minimum Python version to use for this plugin # py_maxversion: # maximum Python version to use for this plugin (leave empty if latest) diff --git a/avm_smarthome/plugin.yaml b/avm_smarthome/plugin.yaml index b38df0934..031d4a15a 100755 --- a/avm_smarthome/plugin.yaml +++ b/avm_smarthome/plugin.yaml @@ -16,7 +16,7 @@ plugin: # support: https://knx-user-forum.de/forum/supportforen/smarthome-py version: 1.0.A # Plugin version (must match the version specified in __init__.py) - sh_minversion: 1.8 # minimum shNG version to use this plugin + sh_minversion: '1.8' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: True # plugin supports multi instance restartable: unknown diff --git a/beolink/plugin.yaml b/beolink/plugin.yaml index 1062c1b61..9ddb079b3 100755 --- a/beolink/plugin.yaml +++ b/beolink/plugin.yaml @@ -13,7 +13,7 @@ plugin: # support: https://knx-user-forum.de/forum/supportforen/smarthome-py version: 0.8.0 # Plugin version - sh_minversion: 1.9 # minimum shNG version to use this plugin + sh_minversion: '1.9' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: unknown diff --git a/blockly/plugin.yaml b/blockly/plugin.yaml index 20a44e759..3fc280e8b 100755 --- a/blockly/plugin.yaml +++ b/blockly/plugin.yaml @@ -14,7 +14,7 @@ plugin: # support: https://knx-user-forum.de/forum/supportforen/smarthome-py/959964-support-thread-für-das-backend-plugin version: 1.5.0 # Plugin version - sh_minversion: 1.4 # minimum shNG version to use this plugin + sh_minversion: '1.4' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: unknown diff --git a/bose_soundtouch/plugin.yaml b/bose_soundtouch/plugin.yaml index 4a6eb8e39..15b72db9b 100755 --- a/bose_soundtouch/plugin.yaml +++ b/bose_soundtouch/plugin.yaml @@ -12,7 +12,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py version: 1.0.1 # Plugin version - sh_minversion: 1.6 # minimum shNG version to use this plugin + sh_minversion: '1.6' # minimum shNG version to use this plugin multi_instance: False # plugin supports multi instance restartable: True classname: BoseSoundtouch # class containing the plugin diff --git a/bsblan/plugin.yaml b/bsblan/plugin.yaml index d77ef64cd..734e223b0 100755 --- a/bsblan/plugin.yaml +++ b/bsblan/plugin.yaml @@ -13,9 +13,9 @@ plugin: support: version: 1.0.2 # Plugin version - sh_minversion: 1.8 # minimum shNG version to use this plugin + sh_minversion: '1.8' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) - py_minversion: 3.7 # minimum Python version to use this plugin + py_minversion: '3.7' # minimum Python version to use this plugin # py_maxversion: # maximum Python version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: unknown diff --git a/buderus/plugin.yaml b/buderus/plugin.yaml index 1517e7334..a6b8ba41b 100755 --- a/buderus/plugin.yaml +++ b/buderus/plugin.yaml @@ -14,7 +14,7 @@ plugin: # support: https://knx-user-forum.de/forum/supportforen/smarthome-py version: 1.0.2 # Plugin version - sh_minversion: 1.1 # minimum shNG version to use this plugin + sh_minversion: '1.1' # minimum shNG version to use this plugin #sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) restartable: True multi_instance: False # plugin supports multi instance diff --git a/byd_bat/plugin.yaml b/byd_bat/plugin.yaml index e3af5ae26..fa8ff9056 100644 --- a/byd_bat/plugin.yaml +++ b/byd_bat/plugin.yaml @@ -13,9 +13,9 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1886748-support-thread-f%C3%BCr-das-byd-batterie-plugin version: 0.1.3 # Plugin version (must match the version specified in __init__.py) - sh_minversion: 1.9 # minimum shNG version to use this plugin + sh_minversion: '1.9' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) - py_minversion: 3.9 # minimum Python version to use for this plugin + py_minversion: '3.9' # minimum Python version to use for this plugin # py_maxversion: # maximum Python version to use for this plugin (leave empty if latest) multi_instance: false # plugin supports multi instance restartable: true diff --git a/casambi/plugin.yaml b/casambi/plugin.yaml index 6b5833bd2..c04e46f9d 100755 --- a/casambi/plugin.yaml +++ b/casambi/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1496182-supportthread-f%C3%BCr-casambi-plugin version: 1.7.6 # Plugin version (must match the version specified in __init__.py) - sh_minversion: 1.7 # minimum shNG version to use this plugin + sh_minversion: '1.7' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: unknown diff --git a/cli/plugin.yaml b/cli/plugin.yaml index d8287f409..a1dd0a502 100755 --- a/cli/plugin.yaml +++ b/cli/plugin.yaml @@ -13,7 +13,7 @@ plugin: documentation: '' version: 1.8.2 # Plugin version - sh_minversion: 1.9.0 # minimum shNG version to use this plugin + sh_minversion: '1.9.0' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: true # plugin supports multi instance restartable: true diff --git a/co2meter/plugin.yaml b/co2meter/plugin.yaml index 339681855..60e5dcf19 100755 --- a/co2meter/plugin.yaml +++ b/co2meter/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1165010-supportthread-f%C3%BCr-das-co2meter-plugin version: 1.3.2 # Plugin version - sh_minversion: 1.6 # minimum shNG version to use this plugin + sh_minversion: '1.6' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: unknown diff --git a/comfoair/plugin.yaml b/comfoair/plugin.yaml index 7438bad8e..9dc1871c9 100755 --- a/comfoair/plugin.yaml +++ b/comfoair/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/31291-neues-plugin-comfoair-kwl-wohnraumlüftung-zehnder-paul-wernig version: 1.3.1 # Plugin version - sh_minversion: 1.3 # minimum shNG version to use this plugin + sh_minversion: '1.3' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: false restartable: true diff --git a/darksky/plugin.yaml b/darksky/plugin.yaml index 5f08cb276..cdde13717 100755 --- a/darksky/plugin.yaml +++ b/darksky/plugin.yaml @@ -12,7 +12,7 @@ plugin: documentation: 'https://www.smarthomeng.de/user/plugins/darksky/user_doc.html' support: 'https://knx-user-forum.de/forum/supportforen/smarthome-py/1244744' version: 1.7.2 # Plugin version - sh_minversion: 1.7 # minimum shNG version to use this plugin + sh_minversion: '1.7' # minimum shNG version to use this plugin #sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: True # plugin supports multi instance restartable: True diff --git a/dashbutton/plugin.yaml b/dashbutton/plugin.yaml index a1950daf8..c64354658 100755 --- a/dashbutton/plugin.yaml +++ b/dashbutton/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1005266-plugin-amazon-dashbutton version: 1.3.1 # Plugin version - sh_minversion: 1.5 # minimum shNG version to use this plugin + sh_minversion: '1.5' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: unknown diff --git a/database/plugin.yaml b/database/plugin.yaml index 1d4b38924..3f4cbbafe 100755 --- a/database/plugin.yaml +++ b/database/plugin.yaml @@ -12,7 +12,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1021844-neues-database-plugin version: 1.6.12 # Plugin version - sh_minversion: 1.9.3.2 # minimum shNG version to use this plugin + sh_minversion: '1.9.3.2' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: True # plugin supports multi instance restartable: unknown diff --git a/datalog/plugin.yaml b/datalog/plugin.yaml index 07a546653..d13d47459 100755 --- a/datalog/plugin.yaml +++ b/datalog/plugin.yaml @@ -12,7 +12,7 @@ plugin: # Following entries are for Smart-Plugins: version: 1.5.2 # Plugin version - sh_minversion: 1.5 # minimum shNG version to use this plugin + sh_minversion: '1.5' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: True restartable: True diff --git a/db_addon/plugin.yaml b/db_addon/plugin.yaml index 411311667..ed258d805 100644 --- a/db_addon/plugin.yaml +++ b/db_addon/plugin.yaml @@ -12,9 +12,9 @@ plugin: # documentation: https://github.com/smarthomeNG/smarthome/wiki/CLI-Plugin # url of documentation (wiki) page support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1848494-support-thread-databaseaddon-plugin version: 1.2.8 # Plugin version (must match the version specified in __init__.py) - sh_minversion: 1.9.3.5 # minimum shNG version to use this plugin + sh_minversion: '1.9.3.5' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) - py_minversion: 3.8 # minimum Python version to use for this plugin + py_minversion: '3.8' # minimum Python version to use for this plugin # py_maxversion: # maximum Python version to use for this plugin (leave empty if latest) multi_instance: false # plugin supports multi instance restartable: unknown diff --git a/deebot_ozmo/plugin.yaml b/deebot_ozmo/plugin.yaml index d0f974705..c5128b617 100755 --- a/deebot_ozmo/plugin.yaml +++ b/deebot_ozmo/plugin.yaml @@ -14,7 +14,7 @@ plugin: # support: https://knx-user-forum.de/forum/supportforen/smarthome-py version: 1.7.2 # Plugin version (must match the version specified in __init__.py) - sh_minversion: 1.5 # minimum shNG version to use this plugin + sh_minversion: '1.5' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: True # plugin supports multi instance restartable: true diff --git a/denon/plugin.yaml b/denon/plugin.yaml index 3ab797598..e03f03321 100755 --- a/denon/plugin.yaml +++ b/denon/plugin.yaml @@ -7,8 +7,8 @@ plugin: state: develop keywords: iot device av denon sdp version: 1.0.1 - sh_minversion: 1.9.5 - py_minversion: 3.7 + sh_minversion: '1.9.5' + py_minversion: '3.7' multi_instance: false restartable: true classname: denon diff --git a/dlms/plugin.yaml b/dlms/plugin.yaml index 8c5daa5da..212e31f82 100755 --- a/dlms/plugin.yaml +++ b/dlms/plugin.yaml @@ -13,9 +13,9 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1002464-support-thread-für-dlms-plugin version: 1.9.5 # Plugin version - sh_minversion: 1.8 # minimum shNG version to use this plugin + sh_minversion: '1.8' # minimum shNG version to use this plugin #sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) - py_minversion: 3.6 # minimum Python version to use for this plugin, due to f-strings + py_minversion: '3.6' # minimum Python version to use for this plugin, due to f-strings # py_maxversion: # maximum Python version to use for this plugin (leave empty if latest) multi_instance: True # plugin supports multi instance restartable: unknown diff --git a/dmx/plugin.yaml b/dmx/plugin.yaml index ebda8affa..43f5ae44c 100755 --- a/dmx/plugin.yaml +++ b/dmx/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py version: 1.6.0 # Plugin version - sh_minversion: 1.4.0 # minimum shNG version to use this plugin + sh_minversion: '1.4.0' # minimum shNG version to use this plugin #sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: unknown diff --git a/drexelundweiss/plugin.yaml b/drexelundweiss/plugin.yaml index 33b46c17e..2471265d6 100755 --- a/drexelundweiss/plugin.yaml +++ b/drexelundweiss/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/34582-drexel-weiss-plugin version: 1.5.4 # Plugin version - sh_minversion: 1.5 # minimum shNG version to use this plugin + sh_minversion: '1.5' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: unknown diff --git a/ebus/plugin.yaml b/ebus/plugin.yaml index 9d25ddfe0..c8f14431b 100755 --- a/ebus/plugin.yaml +++ b/ebus/plugin.yaml @@ -21,7 +21,7 @@ plugin: # Following entries are for Smart-Plugins: version: 1.6.0 # Plugin version - sh_minversion: 1.5 # minimum shNG version to use this plugin + sh_minversion: '1.5' # minimum shNG version to use this plugin #sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False restartable: unknown diff --git a/enigma2/plugin.yaml b/enigma2/plugin.yaml index 840851349..f7b4e47a1 100755 --- a/enigma2/plugin.yaml +++ b/enigma2/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/943871-enigma2-plugin version: 1.4.13 # Plugin version - sh_minversion: 1.6 # minimum shNG version to use this plugin + sh_minversion: '1.6' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: True # plugin supports multi instance restartable: unknown diff --git a/enocean/plugin.yaml b/enocean/plugin.yaml index ebaf6538b..38c69d850 100755 --- a/enocean/plugin.yaml +++ b/enocean/plugin.yaml @@ -17,7 +17,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/26542-featurewunsch-enocean-plugin/page13 version: 1.4.0 # Plugin version - sh_minversion: 1.3 # minimum shNG version to use this plugin + sh_minversion: '1.3'1. # minimum shNG version to use this plugin #sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: unknown diff --git a/epson/plugin.yaml b/epson/plugin.yaml index 947df4d6c..a3050d1eb 100755 --- a/epson/plugin.yaml +++ b/epson/plugin.yaml @@ -7,8 +7,8 @@ plugin: state: develop keywords: iot device av epson sdp version: 1.0.0 - sh_minversion: 1.9.5 - py_minversion: 3.7 + sh_minversion: '1.9.5' + py_minversion: '3.7' multi_instance: false restartable: true classname: epson diff --git a/eta_pu/plugin.yaml b/eta_pu/plugin.yaml index fbfee24d0..47480901d 100755 --- a/eta_pu/plugin.yaml +++ b/eta_pu/plugin.yaml @@ -13,7 +13,7 @@ plugin: # support: https://knx-user-forum.de/forum/supportforen/smarthome-py version: 1.1.1 # Plugin version - sh_minversion: 1.1 # minimum shNG version to use this plugin + sh_minversion: '1.1' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: unknown diff --git a/executor/plugin.yaml b/executor/plugin.yaml index 351405348..a0d58c75f 100755 --- a/executor/plugin.yaml +++ b/executor/plugin.yaml @@ -13,9 +13,9 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1425152-support-thread-plugin-executor version: 1.2.1 # Plugin version - sh_minversion: 1.9 # minimum shNG version to use this plugin + sh_minversion: '1.9' # minimum shNG version to use this plugin #sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) - py_minversion: 3.8 # minimum Python version to use for this plugin, use f-strings for debug + py_minversion: '3.8' # minimum Python version to use for this plugin, use f-strings for debug #py_maxversion: # maximum Python version to use for this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: True diff --git a/garminconnect/plugin.yaml b/garminconnect/plugin.yaml index af39883f7..12bdb4efa 100755 --- a/garminconnect/plugin.yaml +++ b/garminconnect/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: 'https://knx-user-forum.de/forum/supportforen/smarthome-py/1451496-support-thread-f%C3%BCr-das-garminconnect-plugin' version: 1.2.0 # Plugin version - sh_minversion: 1.6 # minimum shNG version to use this plugin + sh_minversion: '1.6' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: unknown diff --git a/gpio/plugin.yaml b/gpio/plugin.yaml index a222fe667..4bdac9e92 100755 --- a/gpio/plugin.yaml +++ b/gpio/plugin.yaml @@ -31,7 +31,7 @@ plugin: keywords: iot gpio raspberrypi version: 1.5.4 # Plugin version - sh_minversion: 1.4 # minimum shNG version to use this plugin + sh_minversion: '1.4' # minimum shNG version to use this plugin multi_instance: false # plugin supports multi instance restartable: unknown classname: GPIO # class containing the plugin diff --git a/harmony/plugin.yaml b/harmony/plugin.yaml index 4eb2c879e..2bec0c2b7 100755 --- a/harmony/plugin.yaml +++ b/harmony/plugin.yaml @@ -14,10 +14,10 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1046500-harmony-hub-plugin version: 1.4.2 # Plugin version - sh_minversion: 1.4 # minimum shNG version to use this plugin + sh_minversion: '1.4' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) # py_minversion: 3.6 # minimum Python version to use for this plugin - py_maxversion: 3.9 # maximum Python version to use for this plugin (leave empty if latest) + py_maxversion: '3.9' # maximum Python version to use for this plugin (leave empty if latest) py_versioncomment: "Die aktuellste Version (1.3.3, released September 2017) des benötigten Packages 'sleekxmpp' ist nicht kompatibel mit Python 3.10" multi_instance: False # plugin supports multi instance restartable: unknown diff --git a/helios/plugin.yaml b/helios/plugin.yaml index 2a643b9ed..62c679d6b 100755 --- a/helios/plugin.yaml +++ b/helios/plugin.yaml @@ -13,7 +13,7 @@ plugin: documentation: https://github.com/Tom-Bom-badil/helios/wiki support: https://knx-user-forum.de/forum/supportforen/smarthome-py/40092-erweiterung-helios-vallox-plugin version: 1.4.3 - sh_minversion: 1.6 + sh_minversion: '1.6' multi_instance: false restartable: true classname: 'Helios' diff --git a/helios_tcp/plugin.yaml b/helios_tcp/plugin.yaml index b629f961b..1577ed500 100755 --- a/helios_tcp/plugin.yaml +++ b/helios_tcp/plugin.yaml @@ -9,7 +9,7 @@ plugin: tester: nobody version: 1.0.2 # Plugin version state: ready - sh_minversion: 1.6 # minimum shNG version to use this plugin + sh_minversion: '1.6' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) restartable: True multi_instance: False # plugin supports multi instance diff --git a/homeconnect/plugin.yaml b/homeconnect/plugin.yaml index 34ce7a2a4..3c660e517 100755 --- a/homeconnect/plugin.yaml +++ b/homeconnect/plugin.yaml @@ -12,7 +12,7 @@ plugin: documentation: '' support: '' version: 1.0.0 # Plugin version - sh_minversion: 1.7 # minimum shNG version to use this plugin + sh_minversion: '1.7' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: False diff --git a/homematic/plugin.yaml b/homematic/plugin.yaml index fd005eba2..c264d7846 100755 --- a/homematic/plugin.yaml +++ b/homematic/plugin.yaml @@ -15,9 +15,9 @@ plugin: # Following entries are for Smart-Plugins: version: 1.5.2 # Plugin version - sh_minversion: 1.7 # minimum shNG version to use this plugin + sh_minversion: '1.7' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) - py_minversion: 3.6 # minimum Python version to use for this plugin + py_minversion: '3.6' # minimum Python version to use for this plugin # py_maxversion: # maximum Python version to use for this plugin (leave empty if latest) multi_instance: True restartable: unknown diff --git a/hue/plugin.yaml b/hue/plugin.yaml index 3d31a16ca..2499c503f 100755 --- a/hue/plugin.yaml +++ b/hue/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/41379-philips-hue-plugin-neu-v1-0-released version: 1.4.5 # Plugin version - sh_minversion: 1.4 # minimum shNG version to use this plugin + sh_minversion: '1.4' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False classname: HUE # class containing the plugin diff --git a/hue2/plugin.yaml b/hue2/plugin.yaml index a414ded05..c90b6aa77 100755 --- a/hue2/plugin.yaml +++ b/hue2/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1586861-support-thread-für-das-hue2-plugin version: 2.3.1 # Plugin version (must match the version specified in __init__.py) - sh_minversion: 1.8.2 # minimum shNG version to use this plugin + sh_minversion: '1.8.2' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) # py_minversion: 3.6 # minimum Python version to use for this plugin # py_maxversion: # maximum Python version to use for this plugin (leave empty if latest) diff --git a/husky/plugin.yaml b/husky/plugin.yaml index 60be75282..fc9603086 100755 --- a/husky/plugin.yaml +++ b/husky/plugin.yaml @@ -12,7 +12,7 @@ plugin: documentation: https://www.smarthomeng.de/user/plugins/husky/user_doc.html support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1658386-support-thread-zum-husky-plugin version: 1.1.0 # Plugin version - sh_minversion: 1.6 # minimum shNG version to use this plugin + sh_minversion: '1.6' # minimum shNG version to use this plugin #sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: True diff --git a/husky2/plugin.yaml b/husky2/plugin.yaml index bed222c30..6a5bea82d 100755 --- a/husky2/plugin.yaml +++ b/husky2/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1764058-support-thread version: 2.1.1 # Plugin version (must match the version specified in __init__.py) - sh_minversion: 1.8 # minimum shNG version to use this plugin + sh_minversion: '1.8' # minimum shNG version to use this plugin multi_instance: false # plugin supports multi instance restartable: unknown classname: Husky2 # class containing the plugin diff --git a/ical/plugin.yaml b/ical/plugin.yaml index 9551db302..f609d6aa0 100755 --- a/ical/plugin.yaml +++ b/ical/plugin.yaml @@ -21,7 +21,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1352089-support-thread-zum-ical-plugin version: 1.6.4 # Plugin version - sh_minversion: 1.9.0 # minimum shNG version to use this plugin + sh_minversion: '1.9.0' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: false # plugin supports multi instance restartable: true diff --git a/indego/plugin.yaml b/indego/plugin.yaml index 7de76a39c..064a6647d 100755 --- a/indego/plugin.yaml +++ b/indego/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/96661 2-indego-connect version: 1.6.1 # Plugin version - sh_minversion: 1.4 # minimum shNG version to use this plugin + sh_minversion: '1.4' # minimum shNG version to use this plugin #sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: True diff --git a/indego4shng/plugin.yaml b/indego4shng/plugin.yaml index 24f4a6ed4..bf128c499 100755 --- a/indego4shng/plugin.yaml +++ b/indego4shng/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/966612-indego-connect version: 4.0.1 # Plugin version - sh_minversion: 1.6 # minimum shNG version to use this plugin + sh_minversion: '1.6' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: yes diff --git a/influxdata/plugin.yaml b/influxdata/plugin.yaml index f4387be67..7a08ec121 100755 --- a/influxdata/plugin.yaml +++ b/influxdata/plugin.yaml @@ -12,7 +12,7 @@ plugin: #documentation: https://github.com/smarthomeNG/smarthome/wiki/Installation-Influx-Grafana # url of documentation (wiki) page version: 1.0.0 # Plugin version - sh_minversion: 1.1 # minimum shNG version to use this plugin + sh_minversion: '1.1' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: unknown diff --git a/influxdb/plugin.yaml b/influxdb/plugin.yaml index d72cc624f..0850de07b 100755 --- a/influxdb/plugin.yaml +++ b/influxdb/plugin.yaml @@ -16,7 +16,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1498207-support-thread-f%C3%BCr-influxdb-plugin version: 1.0.3 # Plugin version - sh_minversion: 1.1 # minimum shNG version to use this plugin + sh_minversion: '1.1' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: unknown diff --git a/influxdb2/plugin.yaml b/influxdb2/plugin.yaml index 66435a9ba..37c1161fc 100755 --- a/influxdb2/plugin.yaml +++ b/influxdb2/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1498207-support-thread-für-influxdb-plugin version: 0.1.0 # Plugin version (must match the version specified in __init__.py) - sh_minversion: 1.9 # minimum shNG version to use this plugin + sh_minversion: '1.9' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) # py_minversion: 3.6 # minimum Python version to use for this plugin # py_maxversion: # maximum Python version to use for this plugin (leave empty if latest) diff --git a/intercom_2n/plugin.yaml b/intercom_2n/plugin.yaml index 996c54c90..0937f2009 100755 --- a/intercom_2n/plugin.yaml +++ b/intercom_2n/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1030539-plugin-2n-intercom version: 1.3.1 # Plugin version - sh_minversion: 1.6 # minimum shNG version to use this plugin + sh_minversion: '1.6' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: false # plugin supports multi instance restartable: false diff --git a/join/plugin.yaml b/join/plugin.yaml index 1345301b0..be2da0ff8 100755 --- a/join/plugin.yaml +++ b/join/plugin.yaml @@ -12,7 +12,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1113523-neues-plugin-join-tts-sms-phonecall-notification-uvm version: 1.4.4 # Plugin version - sh_minversion: 1.4 # minimum shNG version to use this plugin + sh_minversion: '1.4' # minimum shNG version to use this plugin #sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: unknown diff --git a/jsonread/plugin.yaml b/jsonread/plugin.yaml index b411fef1d..b2d5fa7ed 100755 --- a/jsonread/plugin.yaml +++ b/jsonread/plugin.yaml @@ -13,10 +13,10 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/not-yet version: 1.0.4 # Plugin version - sh_minversion: 1.4 # minimum shNG version to use this plugin + sh_minversion: '1.4' # minimum shNG version to use this plugin #sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) #py_minversion: 3.6 # minimum Python version to use for this plugin - py_maxversion: 3.11 # maximum Python version to use for this plugin (leave empty if latest) + py_maxversion: '3.11' # maximum Python version to use for this plugin (leave empty if latest) py_versioncomment: "Die aktuellste Version (2.6.0, released August 2022) des benötigten Packages 'pyjq' ist nicht kompatibel mit Python 3.12" restartable: True multi_instance: True # plugin supports multi instance diff --git a/jvcproj/plugin.yaml b/jvcproj/plugin.yaml index 9305f9815..8b0a67f84 100755 --- a/jvcproj/plugin.yaml +++ b/jvcproj/plugin.yaml @@ -11,7 +11,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1188479-plugin-steuerung-von-jvc-d-ila-projektoren version: 1.0.2 # Plugin version - sh_minversion: 1.6 # minimum shNG version to use this plugin + sh_minversion: '1.6' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: false # plugin supports multi instance restartable: true diff --git a/kathrein/plugin.yaml b/kathrein/plugin.yaml index 7ca56f1f8..d0acc66bb 100755 --- a/kathrein/plugin.yaml +++ b/kathrein/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py version: 1.6.1 # Plugin version - sh_minversion: 1.5 # minimum shNG version to use this plugin + sh_minversion: '1.5' # minimum shNG version to use this plugin #sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: True diff --git a/knx/plugin.yaml b/knx/plugin.yaml index 828f1a942..4696cf837 100755 --- a/knx/plugin.yaml +++ b/knx/plugin.yaml @@ -12,7 +12,7 @@ plugin: keywords: KNX knxd listen cache bus support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1552531-support-thread-zum-knx-plugin - sh_minversion: 1.9.0 # minimum shNG version to use this plugin + sh_minversion: '1.9.0' # minimum shNG version to use this plugin version: 1.8.5 # Plugin version # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: true # plugin supports multi instance diff --git a/kodi/plugin.yaml b/kodi/plugin.yaml index d294c287f..50b961527 100644 --- a/kodi/plugin.yaml +++ b/kodi/plugin.yaml @@ -7,9 +7,7 @@ plugin: state: develop keywords: iot device mediacenter kodi xmbc sdp version: 1.7.2 - sh_minversion: 1.9.5 - py_minversion: 3.7 - multi_instance: true + sh_minversion: '1.9.5'9. py_minversion: '3.7'3. multi_instance: true restartable: true classname: kodi diff --git a/kostal/plugin.yaml b/kostal/plugin.yaml index 4e0d5ea70..500b26517 100755 --- a/kostal/plugin.yaml +++ b/kostal/plugin.yaml @@ -28,7 +28,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1109697-kostal-plugin-piko-wechselrichter version: 1.3.3 # Plugin version - sh_minversion: 1.6 # minimum shNG version to use this plugin + sh_minversion: '1.6' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: true # plugin supports multi instance restartable: true diff --git a/kostalmodbus/plugin.yaml b/kostalmodbus/plugin.yaml index ed09b6568..8bc48da5a 100755 --- a/kostalmodbus/plugin.yaml +++ b/kostalmodbus/plugin.yaml @@ -13,9 +13,9 @@ plugin: support: version: 1.6.3 # Plugin version - sh_minversion: 1.8 # minimum shNG version to use this plugin + sh_minversion: '1.8' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) - py_minversion: 3.8 # minimum Python version to use this plugin + py_minversion: '3.8' # minimum Python version to use this plugin # py_maxversion: # maximum Python version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: unknown diff --git a/ksemmodbus/plugin.yaml b/ksemmodbus/plugin.yaml index 662d4d5dd..4c0509c54 100755 --- a/ksemmodbus/plugin.yaml +++ b/ksemmodbus/plugin.yaml @@ -12,9 +12,9 @@ plugin: support: version: 1.6.3 # Plugin version - sh_minversion: 1.8 # minimum shNG version to use this plugin + sh_minversion: '1.8' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) - py_minversion: 3.8 # minimum Python version to use this plugin + py_minversion: '3.8' # minimum Python version to use this plugin multi_instance: False # plugin supports multi instance restartable: unknown classname: Ksemmodbus # class containing the plugin diff --git a/leveljet/plugin.yaml b/leveljet/plugin.yaml index 923d6af49..4722050b9 100755 --- a/leveljet/plugin.yaml +++ b/leveljet/plugin.yaml @@ -10,7 +10,7 @@ plugin: state: ready # change to ready when done with development # support: https://knx-user-forum.de/forum/supportforen/smarthome-py version: 1.0.1 # Plugin version - sh_minversion: 1.3 # minimum shNG version to use this plugin + sh_minversion: '1.3' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False restartable: unknown diff --git a/lirc/plugin.yaml b/lirc/plugin.yaml index c5c74f0ba..22c12dc86 100755 --- a/lirc/plugin.yaml +++ b/lirc/plugin.yaml @@ -29,7 +29,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1149875-neues-plugin-lirc version: 1.5.1 # Plugin version - sh_minversion: 1.3 # minimum shNG version to use this plugin + sh_minversion: '1.3' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: True # plugin supports multi instance classname: LIRC # class containing the plugin diff --git a/lms/plugin.yaml b/lms/plugin.yaml index 00398532b..ae6004f07 100755 --- a/lms/plugin.yaml +++ b/lms/plugin.yaml @@ -7,8 +7,8 @@ plugin: state: develop keywords: iot device logitechmediaserver lms sdp av version: 1.5.2 - sh_minversion: 1.9.5 - py_minversion: 3.7 + sh_minversion: '1.9.5' + py_minversion: '3.7' multi_instance: false restartable: true classname: lms diff --git a/logo/plugin.yaml b/logo/plugin.yaml index ac5838a93..ff25c0685 100755 --- a/logo/plugin.yaml +++ b/logo/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/36372-plugin-siemens-logo-0ba7 version: 1.2.4 # Plugin version - sh_minversion: 1.2 # minimum shNG version to use this plugin + sh_minversion: '1.2' # minimum shNG version to use this plugin #sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: True # plugin supports multi instance restartable: unknown diff --git a/luxtronic2/plugin.yaml b/luxtronic2/plugin.yaml index e98af2d62..b88e8e04f 100755 --- a/luxtronic2/plugin.yaml +++ b/luxtronic2/plugin.yaml @@ -12,7 +12,7 @@ plugin: # support: https://knx-user-forum.de/forum/supportforen/smarthome-py version: 1.3.3 # Plugin version - sh_minversion: 1.3 # minimum shNG version to use this plugin + sh_minversion: '1.3' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: false restartable: true diff --git a/mailrcv/plugin.yaml b/mailrcv/plugin.yaml index c8616ca77..4147af54d 100755 --- a/mailrcv/plugin.yaml +++ b/mailrcv/plugin.yaml @@ -13,7 +13,7 @@ plugin: # support: https://knx-user-forum.de/forum/supportforen/smarthome-py version: 1.4.2 # Plugin version - sh_minversion: 1.4 # minimum shNG version to use this plugin + sh_minversion: '1.4' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: True # plugin supports multi instance restartable: unknown diff --git a/mailsend/plugin.yaml b/mailsend/plugin.yaml index c3c94a2a3..3745556af 100755 --- a/mailsend/plugin.yaml +++ b/mailsend/plugin.yaml @@ -13,7 +13,7 @@ plugin: # support: https://knx-user-forum.de/forum/supportforen/smarthome-py version: 1.4.2 # Plugin version - sh_minversion: 1.4 # minimum shNG version to use this plugin + sh_minversion: '1.4' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: True # plugin supports multi instance restartable: unknown diff --git a/memlog/plugin.yaml b/memlog/plugin.yaml index 248d5e444..7cc221531 100755 --- a/memlog/plugin.yaml +++ b/memlog/plugin.yaml @@ -11,7 +11,7 @@ plugin: keywords: memory log # keywords, where applicable version: 1.6.1 # Plugin version - sh_minversion: 1.4 # minimum shNG version to use this plugin + sh_minversion: '1.4' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: True restartable: True diff --git a/mieleathome/plugin.yaml b/mieleathome/plugin.yaml index 22c9077ea..7724d14cf 100755 --- a/mieleathome/plugin.yaml +++ b/mieleathome/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1512798-miele-home-mit-mqtt version: 1.0.0 # Plugin version (must match the version specified in __init__.py) - sh_minversion: 1.5 # minimum shNG version to use this plugin + sh_minversion: '1.5' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) # py_minversion: 3.6 # minimum Python version to use for this plugin # py_maxversion: # maximum Python version to use for this plugin (leave empty if latest) diff --git a/miflora/plugin.yaml b/miflora/plugin.yaml index 8fcaa589f..86a0bab46 100755 --- a/miflora/plugin.yaml +++ b/miflora/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1027133-plugin-miflora-mi-plant-flowers-tester-light-monitor version: 1.6.2 # Plugin version - sh_minversion: 1.6 # minimum shNG version to use this plugin + sh_minversion: '1.6' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: True # plugin supports multi instance restartable: unknown diff --git a/mikrotik/plugin.yaml b/mikrotik/plugin.yaml index 7a2ec1ab6..132d397d6 100755 --- a/mikrotik/plugin.yaml +++ b/mikrotik/plugin.yaml @@ -10,7 +10,7 @@ plugin: state: develop # change to ready when done with development keywords: infrastructure network version: 1.0.0 - sh_minversion: 1.9 + sh_minversion: '1.9' multi_instance: true restartable: true classname: MikrotikPlugin diff --git a/milight/plugin.yaml b/milight/plugin.yaml index 93db99043..1044aadee 100755 --- a/milight/plugin.yaml +++ b/milight/plugin.yaml @@ -14,7 +14,7 @@ plugin: # Following entries are for Smart-Plugins: version: 1.6.1 # Plugin version - sh_minversion: 1.5 # minimum shNG version to use this plugin + sh_minversion: '1.5' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: True # plugin supports multi instance restartable: True diff --git a/mlgw/plugin.yaml b/mlgw/plugin.yaml index 6ba93010b..ea068f0ed 100755 --- a/mlgw/plugin.yaml +++ b/mlgw/plugin.yaml @@ -12,7 +12,7 @@ plugin: # documentation: https://github.com/smarthomeNG/smarthome/wiki/CLI-Plugin # url of documentation (wiki) page version: 1.1.2 # Plugin version - sh_minversion: 1.1 # minimum shNG version to use this plugin + sh_minversion: '1.1' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: False diff --git a/modbus_tcp/plugin.yaml b/modbus_tcp/plugin.yaml index 51c6cc90d..e78b1e7cd 100755 --- a/modbus_tcp/plugin.yaml +++ b/modbus_tcp/plugin.yaml @@ -12,9 +12,9 @@ plugin: #documentation: http://smarthomeng.de/user/plugins/modbus_tcp/user_doc.html support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1154368-einbindung-von-modbus-tcp version: 1.0.12 # Plugin version - sh_minversion: 1.8 # minimum shNG version to use this plugin + sh_minversion: '1.8' # minimum shNG version to use this plugin #sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) - py_minversion: 3.6 + py_minversion: '3.6' # py_maxversion: # maximum Python version to use for this plugin (leave empty if latest) multi_instance: True # plugin supports multi instance restartable: unknown diff --git a/mpd/plugin.yaml b/mpd/plugin.yaml index 240860213..926ff770e 100755 --- a/mpd/plugin.yaml +++ b/mpd/plugin.yaml @@ -15,7 +15,7 @@ plugin: version: 1.6.1 # Plugin version sh_minversion: 1.8.2a # minimum shNG version to use this plugin #sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) - py_minversion: 3.6 # minimum Python version to use for this plugin + py_minversion: '3.6' # minimum Python version to use for this plugin multi_instance: true restartable: true classname: MPD # class containing the plugin diff --git a/mqtt/plugin.yaml b/mqtt/plugin.yaml index d038ff2b3..e6ae0be24 100755 --- a/mqtt/plugin.yaml +++ b/mqtt/plugin.yaml @@ -12,7 +12,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1089334-neues-mqtt-plugin version: 2.0.5 # Plugin version - sh_minversion: 1.7 # minimum shNG version to use this plugin + sh_minversion: '1.7' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: True # since the plugin connects to the mqtt module, multi instance makes no sense restartable: unknown diff --git a/mvg_live/plugin.yaml b/mvg_live/plugin.yaml index 764df8bed..65b810d52 100755 --- a/mvg_live/plugin.yaml +++ b/mvg_live/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1108867-neues-plugin-mvg_live version: 1.6.0 # Plugin version - sh_minversion: 1.5 # minimum shNG version to use this plugin + sh_minversion: '1.5' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: unknown diff --git a/neato/plugin.yaml b/neato/plugin.yaml index eebd19e18..7562321e7 100755 --- a/neato/plugin.yaml +++ b/neato/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1417295-support-thread-plugin-neato version: 1.6.9 # Plugin version - sh_minversion: 1.8.0 # minimum shNG version to use this plugin + sh_minversion: '1.8.0' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: unknown diff --git a/network/plugin.yaml b/network/plugin.yaml index dfdc7af83..b751c6901 100755 --- a/network/plugin.yaml +++ b/network/plugin.yaml @@ -13,7 +13,7 @@ plugin: documentation: https://www.smarthomeng.de/user/plugins_doc/config/network.html # url of documentation (wiki) page version: 1.6.2 # Plugin version - sh_minversion: 1.9.0 # minimum shNG version to use this plugin + sh_minversion: '1.9.0' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: false # plugin supports multi instance restartable: true diff --git a/nuki/plugin.yaml b/nuki/plugin.yaml index 0db276228..3863784bc 100755 --- a/nuki/plugin.yaml +++ b/nuki/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: https://knx-user-forum.de/node/1052437 version: 1.6.4 # Plugin version - sh_minversion: 1.9.0 # minimum shNG version to use this plugin + sh_minversion: '1.9.0' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: unknown diff --git a/nut/plugin.yaml b/nut/plugin.yaml index 749f32d8d..3c1b9b152 100755 --- a/nut/plugin.yaml +++ b/nut/plugin.yaml @@ -22,7 +22,7 @@ plugin: # support: https://knx-user-forum.de/forum/supportforen/smarthome-py version: 1.3.4 # Plugin version - sh_minversion: 1.3 # minimum shNG version to use this plugin + sh_minversion: '1.3' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: True # plugin supports multi instance restartable: unknown diff --git a/odlinfo/plugin.yaml b/odlinfo/plugin.yaml index 1f73e7852..c658a9735 100755 --- a/odlinfo/plugin.yaml +++ b/odlinfo/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: https://knx-user-forum.de/node/986480 version: 1.5.3 # Plugin version - sh_minversion: 1.6 # minimum shNG version to use this plugin + sh_minversion: '1.6' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: unknown diff --git a/onewire/plugin.yaml b/onewire/plugin.yaml index a74228138..b880b7521 100755 --- a/onewire/plugin.yaml +++ b/onewire/plugin.yaml @@ -12,7 +12,7 @@ plugin: documentation: '' support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1493319-support-thread-zum-onewire-plugin version: 1.9.4 # Plugin version - sh_minversion: 1.9.3.5 # minimum shNG version to use this plugin + sh_minversion: '1.9.3.5' # minimum shNG version to use this plugin multi_instance: True restartable: True classname: OneWire # class containing the plugin diff --git a/openweathermap/plugin.yaml b/openweathermap/plugin.yaml index 5cc6eb83b..6550508e6 100755 --- a/openweathermap/plugin.yaml +++ b/openweathermap/plugin.yaml @@ -12,7 +12,7 @@ plugin: documentation: '' support: 'https://knx-user-forum.de/forum/supportforen/smarthome-py/1246998-support-thread-zum-openweathermap-plugin' version: 1.8.7 # Plugin version - sh_minversion: 1.9.0 # minimum shNG version to use this plugin + sh_minversion: '1.9.0' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: True # plugin supports multi instance restartable: unknown diff --git a/operationlog/plugin.yaml b/operationlog/plugin.yaml index d8f67f939..d982d98eb 100755 --- a/operationlog/plugin.yaml +++ b/operationlog/plugin.yaml @@ -15,7 +15,7 @@ plugin: state: deprecated support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1496323-support-thread-für-operationlog-plugin version: 1.3.6 # Plugin version - sh_minversion: 1.4 # minimum shNG version to use this plugin + sh_minversion: '1.4' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: false # plugin supports multi instance restartable: true diff --git a/oppo/plugin.yaml b/oppo/plugin.yaml index 225667609..1210475d2 100755 --- a/oppo/plugin.yaml +++ b/oppo/plugin.yaml @@ -7,8 +7,8 @@ plugin: state: develop keywords: iot device version: 1.0.0 - sh_minversion: 1.9.5.1 - py_minversion: 3.6 + sh_minversion: '1.9.5.1' + py_minversion: '3.6' multi_instance: false restartable: true classname: oppo diff --git a/philips_tv/plugin.yaml b/philips_tv/plugin.yaml index d659fe332..adf3c77ad 100755 --- a/philips_tv/plugin.yaml +++ b/philips_tv/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1698335-supportthread-f%C3%BCr-philips_tv-plugin version: 1.9.2 # Plugin version - sh_minversion: 1.8.0 # minimum shNG version to use this plugin + sh_minversion: '1.8.0' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: unknown diff --git a/pioneer/plugin.yaml b/pioneer/plugin.yaml index 86aebd39e..f6904e8ed 100755 --- a/pioneer/plugin.yaml +++ b/pioneer/plugin.yaml @@ -7,8 +7,8 @@ plugin: state: develop keywords: iot device av pioneer sdp version: 1.0.2 - sh_minversion: 1.9.5 - py_minversion: 3.7 + sh_minversion: '1.9.5' + py_minversion: '3.7' multi_instance: false restartable: true classname: pioneer diff --git a/piratewthr/plugin.yaml b/piratewthr/plugin.yaml index 571a4843e..ae9fdd62b 100755 --- a/piratewthr/plugin.yaml +++ b/piratewthr/plugin.yaml @@ -12,7 +12,7 @@ plugin: #documentation: '' support: 'https://knx-user-forum.de/forum/supportforen/smarthome-py/1852685' version: 1.2.5 # Plugin version - sh_minversion: 1.9.5.4 # minimum shNG version to use this plugin + sh_minversion: '1.9.5.4' # minimum shNG version to use this plugin #sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: True # plugin supports multi instance restartable: True diff --git a/plex/plugin.yaml b/plex/plugin.yaml index 302dd0894..9090f4598 100755 --- a/plex/plugin.yaml +++ b/plex/plugin.yaml @@ -13,7 +13,7 @@ plugin: # support: https://knx-user-forum.de/forum/supportforen/smarthome-py version: 1.0.1 # Plugin version - sh_minversion: 1.6 # minimum shNG version to use this plugin + sh_minversion: '1.6' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: false # plugin supports multi instance restartable: true diff --git a/pluggit/plugin.yaml b/pluggit/plugin.yaml index a98bb2411..86d4f9c7b 100755 --- a/pluggit/plugin.yaml +++ b/pluggit/plugin.yaml @@ -12,7 +12,7 @@ plugin: # documentation: # support: version: 2.0.6 # Plugin version - sh_minversion: 1.8 # minimum shNG version to use this plugin + sh_minversion: '1.8' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance classname: Pluggit # class containing the plugin diff --git a/prowl/plugin.yaml b/prowl/plugin.yaml index df2855f0c..dba418733 100755 --- a/prowl/plugin.yaml +++ b/prowl/plugin.yaml @@ -17,7 +17,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1492644-support-thread-zum-prowl-plugin version: 1.3.3 # Plugin version - sh_minversion: 1.3 # minimum shNG version to use this plugin + sh_minversion: '1.3' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: true # plugin supports multi instance restartable: true diff --git a/pushbullet/plugin.yaml b/pushbullet/plugin.yaml index 1c834d69b..84b00fe83 100755 --- a/pushbullet/plugin.yaml +++ b/pushbullet/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/36001-neues-plugin-pushbullet version: 1.5.2 # Plugin version - sh_minversion: 1.5 # minimum shNG version to use this plugin + sh_minversion: '1.5' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: unknown diff --git a/pushover/plugin.yaml b/pushover/plugin.yaml index bbf782578..c928f30a2 100755 --- a/pushover/plugin.yaml +++ b/pushover/plugin.yaml @@ -11,7 +11,7 @@ plugin: tester: None state: ready version: 1.6.2 # Plugin version - sh_minversion: 1.5 # minimum shNG version to use this plugin + sh_minversion: '1.5' # minimum shNG version to use this plugin multi_instance: True # plugin supports multi instance restartable: unknown classname: Pushover # class containing the plugin diff --git a/raumfeld_ng/plugin.yaml b/raumfeld_ng/plugin.yaml index 023546cb3..331667b7f 100755 --- a/raumfeld_ng/plugin.yaml +++ b/raumfeld_ng/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1927851 version: 1.5.2 # Plugin version - sh_minversion: 1.5 # minimum shNG version to use this plugin + sh_minversion: '1.5' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: True diff --git a/rcs1000n/plugin.yaml b/rcs1000n/plugin.yaml index 960e88eb8..1df2c47c1 100755 --- a/rcs1000n/plugin.yaml +++ b/rcs1000n/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/39094-logic-und-howto-für-433mhz-steckdosen version: 1.0.0 # Plugin version - sh_minversion: 1.9 # minimum shNG version to use this plugin + sh_minversion: '1.9' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: unknown diff --git a/rcswitch/plugin.yaml b/rcswitch/plugin.yaml index 42b301887..5df70ce40 100755 --- a/rcswitch/plugin.yaml +++ b/rcswitch/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/39094-logic-und-howto-für-433mhz-steckdosen version: 1.2.2 # Plugin version - sh_minversion: 1.6 # minimum shNG version to use this plugin + sh_minversion: '1.6' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: false # plugin supports multi instance restartable: true diff --git a/resol/plugin.yaml b/resol/plugin.yaml index 650efafa4..6df5805e9 100755 --- a/resol/plugin.yaml +++ b/resol/plugin.yaml @@ -14,7 +14,7 @@ plugin: # Following entries are for Smart-Plugins: version: 1.0.7 # Plugin version - sh_minversion: 1.7.2 # minimum shNG version to use this plugin + sh_minversion: '1.7.2' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: True restartable: unknown diff --git a/robonect/plugin.yaml b/robonect/plugin.yaml index b89492bfe..19a63ce67 100755 --- a/robonect/plugin.yaml +++ b/robonect/plugin.yaml @@ -12,7 +12,7 @@ plugin: # documentation: https://github.com/smarthomeNG/smarthome/wiki/CLI-Plugin # url of documentation (wiki) page support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1515205-support-thread version: 1.0.5 # Plugin version (must match the version specified in __init__.py) - sh_minversion: 1.6 # minimum shNG version to use this plugin + sh_minversion: '1.6' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) # py_minversion: 3.6 # minimum Python version to use this plugin # py_maxversion: # maximum Python version to use this plugin (leave empty if latest) diff --git a/roomba/plugin.yaml b/roomba/plugin.yaml index 2f056d3a3..37c2becc9 100755 --- a/roomba/plugin.yaml +++ b/roomba/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py version: 1.6.0 # Plugin version - sh_minversion: 1.4 # minimum shNG version to use this plugin + sh_minversion: '1.4' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: False diff --git a/roomba_980/plugin.yaml b/roomba_980/plugin.yaml index 1da819cd6..df49c45d9 100755 --- a/roomba_980/plugin.yaml +++ b/roomba_980/plugin.yaml @@ -14,7 +14,7 @@ plugin: # Following entries are for Smart-Plugins: version: 1.0.2 # Plugin version - sh_minversion: 1.6 # minimum shNG version to use this plugin + sh_minversion: '1.6' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: false restartable: true diff --git a/roombapysh/plugin.yaml b/roombapysh/plugin.yaml index f349b61a8..3ea237b54 100644 --- a/roombapysh/plugin.yaml +++ b/roombapysh/plugin.yaml @@ -14,7 +14,7 @@ plugin: # Following entries are for Smart-Plugins: version: 1.0.0 # Plugin version - sh_minversion: 1.6 # minimum shNG version to use this plugin + sh_minversion: '1.6' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False restartable: unknown diff --git a/rpi1wire/plugin.yaml b/rpi1wire/plugin.yaml index c3f62d714..611d5a876 100755 --- a/rpi1wire/plugin.yaml +++ b/rpi1wire/plugin.yaml @@ -14,7 +14,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1451916-support-thread-für-rpi1wire-plugin version: 1.8.1 # Plugin version - sh_minversion: 1.5 # minimum shNG version to use this plugin + sh_minversion: '1.5' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) # py_minversion: 3.6 # minimum Python version to use for this plugin # py_maxversion: # maximum Python version to use for this plugin (leave empty if latest) diff --git a/rpi_info/plugin.yaml b/rpi_info/plugin.yaml index 98c98348a..2d54620d9 100755 --- a/rpi_info/plugin.yaml +++ b/rpi_info/plugin.yaml @@ -13,7 +13,7 @@ plugin: # support: https://knx-user-forum.de/forum/supportforen/smarthome-py version: 1.0.0 # Plugin version (must match the version specified in __init__.py) - sh_minversion: 1.8 # minimum shNG version to use this plugin + sh_minversion: '1.8' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) # py_minversion: 3.6 # minimum Python version to use for this plugin # py_maxversion: # maximum Python version to use for this plugin (leave empty if latest) diff --git a/rrd/plugin.yaml b/rrd/plugin.yaml index 46b925c68..38bc0cda6 100755 --- a/rrd/plugin.yaml +++ b/rrd/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/rrd-plugin version: 1.6.2 # Plugin version - sh_minversion: 1.5 # minimum shNG version to use this plugin + sh_minversion: '1.5' # minimum shNG version to use this plugin #sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: True diff --git a/rtr/plugin.yaml b/rtr/plugin.yaml index 3f155b031..4da05d98b 100755 --- a/rtr/plugin.yaml +++ b/rtr/plugin.yaml @@ -10,7 +10,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/31450-rtr-heizungs-plugin documentation: https://www.smarthomeng.de/dev/user/plugins/rtr/README.html version: 1.6.0 # Plugin version - sh_minversion: 1.6 # minimum shNG version to use this plugin + sh_minversion: '1.6' # minimum shNG version to use this plugin restartable: True multi_instance: False # plugin supports multi instance classname: RTR # class containing the plugin diff --git a/rtr2/plugin.yaml b/rtr2/plugin.yaml index 6c259ca0d..be8440be1 100755 --- a/rtr2/plugin.yaml +++ b/rtr2/plugin.yaml @@ -13,9 +13,9 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1586747-support-thread-für-das-rtr2-plugin version: 2.2.0 # Plugin version (must match the version specified in __init__.py) - sh_minversion: 1.8.0 # minimum shNG version to use this plugin + sh_minversion: '1.8.0' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) - py_minversion: 3.6 # minimum Python version to use for this plugin + py_minversion: '3.6' # minimum Python version to use for this plugin # py_maxversion: # maximum Python version to use for this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: False diff --git a/russound/plugin.yaml b/russound/plugin.yaml index 96dd49907..a10bc0484 100755 --- a/russound/plugin.yaml +++ b/russound/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1800440-support-thread-für-das-russound-plugin version: 1.7.2 # Plugin version - sh_minversion: 1.9.0 # minimum shNG version to use this plugin + sh_minversion: '1.9.0' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: false # plugin supports multi instance restartable: true diff --git a/shelly/plugin.yaml b/shelly/plugin.yaml index a795934df..eccdf8aca 100755 --- a/shelly/plugin.yaml +++ b/shelly/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1451853-support-thread-für-das-shelly-plugin version: 1.8.2 # Plugin version - sh_minversion: 1.9.5.6 # minimum shNG version to use this plugin + sh_minversion: '1.9.5.6' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: True # plugin supports multi instance restartable: unknown diff --git a/simulation/plugin.yaml b/simulation/plugin.yaml index 16a2ccc61..472cce8e0 100755 --- a/simulation/plugin.yaml +++ b/simulation/plugin.yaml @@ -28,7 +28,7 @@ plugin: documentation: https://www.smarthomeng.de/user/plugins/simulation/user_doc.html # url of documentation (wiki) page support: 'https://knx-user-forum.de/forum/supportforen/smarthome-py/841097' version: 1.5.1 # Plugin version - sh_minversion: 1.5 # minimum shNG version to use this plugin + sh_minversion: '1.5' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: unknown diff --git a/slack/plugin.yaml b/slack/plugin.yaml index ce2dddd65..0e4062567 100755 --- a/slack/plugin.yaml +++ b/slack/plugin.yaml @@ -13,7 +13,7 @@ plugin: # support: https://knx-user-forum.de/forum/supportforen/smarthome-py version: 1.0.0 # Plugin version - sh_minversion: 1.4 # minimum shNG version to use this plugin + sh_minversion: '1.4' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: True # plugin supports multi instance restartable: True # restartable: yes diff --git a/sma/plugin.yaml b/sma/plugin.yaml index f18e18f74..b95c672cc 100755 --- a/sma/plugin.yaml +++ b/sma/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/27997-beitrag-plugin-zum-lesen-von-sma-wechselrichtern-sunnyboy-5000tl-21-getestet version: 1.3.2 # Plugin version - sh_minversion: 1.6 # minimum shNG version to use this plugin + sh_minversion: '1.6' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: false # plugin supports multi instance restartable: true diff --git a/sma_em/plugin.yaml b/sma_em/plugin.yaml index 5c8345940..ba1dd99c8 100755 --- a/sma_em/plugin.yaml +++ b/sma_em/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1030610-sma_em-plugin version: 1.6.1 # Plugin version - sh_minversion: 1.6 # minimum shNG version to use this plugin + sh_minversion: '1.6' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: True diff --git a/sma_mb/plugin.yaml b/sma_mb/plugin.yaml index 6047e72db..2d2a66a32 100755 --- a/sma_mb/plugin.yaml +++ b/sma_mb/plugin.yaml @@ -13,9 +13,9 @@ plugin: # support: https://knx-user-forum.de/forum/supportforen/smarthome-py version: 1.5.4 # Plugin version (must match the version specified in __init__.py) - sh_minversion: 1.8 # minimum shNG version to use this plugin + sh_minversion: '1.8' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) - py_minversion: 3.8 # minimum Python version to use for this plugin + py_minversion: '3.8' # minimum Python version to use for this plugin # py_maxversion: # maximum Python version to use for this plugin (leave empty if latest) multi_instance: True # plugin supports multi instance restartable: unknown diff --git a/smarttv/plugin.yaml b/smarttv/plugin.yaml index 8127d8b16..9b0edabfc 100755 --- a/smarttv/plugin.yaml +++ b/smarttv/plugin.yaml @@ -13,7 +13,7 @@ plugin: # support: https://knx-user-forum.de/forum/supportforen/smarthome-py version: 1.3.3 # Plugin version - sh_minversion: 1.6 # minimum shNG version to use this plugin + sh_minversion: '1.6' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: true # plugin supports multi instance restartable: true diff --git a/smartvisu/plugin.yaml b/smartvisu/plugin.yaml index 5d73c7f08..2b0fbb9fa 100755 --- a/smartvisu/plugin.yaml +++ b/smartvisu/plugin.yaml @@ -13,9 +13,9 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1586800-support-thread-für-das-smartvisu-plugin version: 1.8.14 # Plugin version - sh_minversion: 1.9.3.5 # minimum shNG version to use this plugin + sh_minversion: '1.9.3.5' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) - py_minversion: 3.6 # minimum Python version to use for this plugin + py_minversion: '3.6' # minimum Python version to use for this plugin # py_maxversion: # maximum Python version to use for this plugin (leave empty if latest) multi_instance: True # plugin supports multi instance restartable: True diff --git a/sml/plugin.yaml b/sml/plugin.yaml index 924f846ba..4953fbf5d 100755 --- a/sml/plugin.yaml +++ b/sml/plugin.yaml @@ -13,7 +13,7 @@ plugin: # support: https://knx-user-forum.de/forum/supportforen/smarthome-py version: 1.0.1 # Plugin version - sh_minversion: 1.6 # minimum shNG version to use this plugin + sh_minversion: '1.6' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: true # plugin supports multi instance restartable: true diff --git a/sml2/plugin.yaml b/sml2/plugin.yaml index 40e59a7de..1538e34b5 100755 --- a/sml2/plugin.yaml +++ b/sml2/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/39119-sml-plugin-datenblock-größenfehler restartable: True version: 2.0.1 # Plugin version - sh_minversion: 1.4.2 # minimum shNG version to use this plugin + sh_minversion: '1.4.2' # minimum shNG version to use this plugin #sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: True # plugin supports multi instance classname: Sml2 # class containing the plugin diff --git a/smlx/plugin.yaml b/smlx/plugin.yaml index b6e41e772..149b39dde 100755 --- a/smlx/plugin.yaml +++ b/smlx/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/39119-sml-plugin-datenblock-größenfehler restartable: True version: 1.1.8 # Plugin version - sh_minversion: 1.4.2 # minimum shNG version to use this plugin + sh_minversion: '1.4.2' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) # py_minversion: 3.6 # minimum Python version to use for this plugin # py_maxversion: # maximum Python version to use for this plugin (leave empty if latest) diff --git a/snap7_logo/plugin.yaml b/snap7_logo/plugin.yaml index 864998b77..b41aefe2d 100755 --- a/snap7_logo/plugin.yaml +++ b/snap7_logo/plugin.yaml @@ -14,7 +14,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/36372-plugin-siemens-logo-0ba7 version: 1.6.0 # Plugin version - sh_minversion: 1.8 # minimum shNG version to use this plugin + sh_minversion: '1.8' # minimum shNG version to use this plugin multi_instance: True # plugin supports multi instance restartable: unknown classname: snap7_logo # class containing the plugin diff --git a/snmp/plugin.yaml b/snmp/plugin.yaml index 49fe3d2b6..0ff1c7357 100755 --- a/snmp/plugin.yaml +++ b/snmp/plugin.yaml @@ -10,7 +10,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1455436-support-thread-f%C3%BCr-snmp-plugin version: 1.6.1 # Plugin version - sh_minversion: 1.6.0 # minimum shNG version to use this plugin + sh_minversion: '1.6.0' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: True # plugin supports multi instance restartable: unknown diff --git a/solarforecast/plugin.yaml b/solarforecast/plugin.yaml index ca3011080..216572c4e 100755 --- a/solarforecast/plugin.yaml +++ b/solarforecast/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1842817-support-thread-f%C3%BCr-das-solarforecast-plugin version: 1.9.5 # Plugin version - sh_minversion: 1.10.0 # minimum shNG version to use this plugin + sh_minversion: '1.10.0' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: true # plugin supports multi instance restartable: true diff --git a/solarlog/plugin.yaml b/solarlog/plugin.yaml index 2b51a4488..0c555b73e 100755 --- a/solarlog/plugin.yaml +++ b/solarlog/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1442978-support-thread-für-solarlog-plugin-fw-2-x version: 1.6.2 # Plugin version - sh_minversion: 1.4 # minimum shNG version to use this plugin + sh_minversion: '1.4' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: unknown diff --git a/sonos/plugin.yaml b/sonos/plugin.yaml index 9b2079687..3b2ef4ac5 100755 --- a/sonos/plugin.yaml +++ b/sonos/plugin.yaml @@ -13,8 +13,8 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/25151-sonos-anbindung version: 1.8.7 # Plugin version - sh_minversion: 1.5.1 # minimum shNG version to use this plugin - py_minversion: 3.8 # minimum Python version to use for this plugin + sh_minversion: '1.5.1' # minimum shNG version to use this plugin + py_minversion: '3.8' # minimum Python version to use for this plugin multi_instance: False # plugin supports multi instance restartable: unknown classname: Sonos # class containing the plugin diff --git a/speech/plugin.yaml b/speech/plugin.yaml index bfaf0ad21..95338f1b5 100755 --- a/speech/plugin.yaml +++ b/speech/plugin.yaml @@ -12,7 +12,7 @@ plugin: documentation: http://smarthomeng.de/user/plugins/speech/user_doc.html support: http://knx-user-forum.de/forum/supportforen/smarthome-py/39857-plugin-speech-parser version: 1.7.0 # Plugin version - sh_minversion: 1.9.0 # minimum shNG version to use this plugin + sh_minversion: '1.9.0' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: false # plugin supports multi instance restartable: true diff --git a/sqlite_visu2_8/plugin.yaml b/sqlite_visu2_8/plugin.yaml index 0ef6a830b..614b7fcdc 100755 --- a/sqlite_visu2_8/plugin.yaml +++ b/sqlite_visu2_8/plugin.yaml @@ -12,7 +12,7 @@ plugin: # documentation: https://github.com/smarthomeNG/smarthome/wiki/CLI-Plugin # url of documentation (wiki) page version: 1.3.1 # Plugin version - sh_minversion: 1.4 # minimum shNG version to use this plugin + sh_minversion: '1.4' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: unknown diff --git a/squeezebox/plugin.yaml b/squeezebox/plugin.yaml index 1b3a17a71..138618371 100755 --- a/squeezebox/plugin.yaml +++ b/squeezebox/plugin.yaml @@ -32,7 +32,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/28692-√-neues-plugin-logitech-squeezebox-anregungen version: 1.4.0 # Plugin version - sh_minversion: 1.3 # minimum shNG version to use this plugin + sh_minversion: '1.3' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance classname: Squeezebox # class containing the plugin diff --git a/stateengine/plugin.yaml b/stateengine/plugin.yaml index ee92b2ab8..c08e5f59b 100755 --- a/stateengine/plugin.yaml +++ b/stateengine/plugin.yaml @@ -40,7 +40,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1303071-stateengine-plugin-support version: 2.0.0 - sh_minversion: 1.6 + sh_minversion: '1.6' multi_instance: False classname: StateEngine restartable: unknown diff --git a/systemair/plugin.yaml b/systemair/plugin.yaml index 98e015e4d..469dd7d13 100755 --- a/systemair/plugin.yaml +++ b/systemair/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/939623-systemair-modbus-plugin-zentrale-lüftungsanlage version: 1.3.1 # Plugin version - sh_minversion: 1.6 # minimum shNG version to use this plugin + sh_minversion: '1.6' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: false # plugin supports multi instance restartable: true diff --git a/tankerkoenig/plugin.yaml b/tankerkoenig/plugin.yaml index 66b491821..e2313ed68 100755 --- a/tankerkoenig/plugin.yaml +++ b/tankerkoenig/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/938924-benzinpreis-plugin keywords: petrol station, fuel prices, petrol prices version: 2.0.4 # Plugin version - sh_minversion: 1.9 # minimum shNG version to use this plugin + sh_minversion: '1.9' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) # py_minversion: # minimum Python version to use for this plugin # py_maxversion: # maximum Python version to use for this plugin (leave empty if latest) diff --git a/tasmota/plugin.yaml b/tasmota/plugin.yaml index 1260ae6af..c154faea9 100644 --- a/tasmota/plugin.yaml +++ b/tasmota/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1520293-support-thread-für-das-tasmota-plugin version: 1.5.2 # Plugin version - sh_minversion: 1.9.3 # minimum shNG version to use this plugin + sh_minversion: '1.9.3' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) # py_minversion: # minimum Python version to use for this plugin multi_instance: True # plugin supports multi instance diff --git a/telegram/plugin.yaml b/telegram/plugin.yaml index 2173691c7..09d9dfc1a 100755 --- a/telegram/plugin.yaml +++ b/telegram/plugin.yaml @@ -12,7 +12,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1548691-support-thread-für-das-telegram-plugin version: 2.0.1 # Plugin version - sh_minversion: 1.9.5 # minimum shNG version to use this plugin + sh_minversion: '1.9.5' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) # py_minversion: 3.6 # minimum Python version to use for this plugin # py_maxversion: # maximum Python version to use for this plugin (leave empty if latest) diff --git a/text_display/plugin.yaml b/text_display/plugin.yaml index b061a6a09..8665133a7 100755 --- a/text_display/plugin.yaml +++ b/text_display/plugin.yaml @@ -13,9 +13,9 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1678358-meldungsplugin-f%C3%BCr-textmeldungen-auf-knx-tastern version: 1.8.1 # Plugin version (must match the version specified in __init__.py) - sh_minversion: 1.8.0 # minimum shNG version to use this plugin + sh_minversion: '1.8.0' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) - py_minversion: 3.6 # minimum Python version to use for this plugin + py_minversion: '3.6' # minimum Python version to use for this plugin # py_maxversion: # maximum Python version to use for this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: False diff --git a/thz/plugin.yaml b/thz/plugin.yaml index c69c89845..b10f20c69 100755 --- a/thz/plugin.yaml +++ b/thz/plugin.yaml @@ -10,7 +10,7 @@ plugin: documentation: https://www.smarthomeng.de/user/plugins/thz/README.html support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1358489-support-thread-zum-thz-lwz-30x-40x-plugin version: 1.0.0 # Plugin version - sh_minversion: 1.4 + sh_minversion: '1.4' multi_instance: False restartable: unknown classname: THZ diff --git a/timmy/plugin.yaml b/timmy/plugin.yaml index 50055430a..468248639 100755 --- a/timmy/plugin.yaml +++ b/timmy/plugin.yaml @@ -13,9 +13,9 @@ plugin: # support: https://knx-user-forum.de/forum/supportforen/smarthome-py version: 1.8.1 # Plugin version (must match the version specified in __init__.py) - sh_minversion: 1.8.0 # minimum shNG version to use this plugin + sh_minversion: '1.8.0' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) - py_minversion: 3.6 # minimum Python version to use for this plugin + py_minversion: '3.6' # minimum Python version to use for this plugin # py_maxversion: # maximum Python version to use for this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: False diff --git a/traffic/plugin.yaml b/traffic/plugin.yaml index 6487605f5..ea8a9b774 100755 --- a/traffic/plugin.yaml +++ b/traffic/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1048446-traffic-plugin-support-thread version: 1.5.1 # Plugin version - sh_minversion: 1.5 # minimum shNG version to use this plugin + sh_minversion: '1.5' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: unknown diff --git a/trovis557x/plugin.yaml b/trovis557x/plugin.yaml index 867cdf30e..6210165dd 100755 --- a/trovis557x/plugin.yaml +++ b/trovis557x/plugin.yaml @@ -11,7 +11,7 @@ plugin: documentation: https://github.com/Tom-Bom-badil/samson_trovis_557x/wiki support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1390281-trovis-557x-heizungsregler-plugin version: 2.0.0 - sh_minversion: 1.5 + sh_minversion: '1.5' # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False restartable: unknown diff --git a/unifi/plugin.yaml b/unifi/plugin.yaml index 285c0eacd..eed50b6ca 100755 --- a/unifi/plugin.yaml +++ b/unifi/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1278068-unifi-controller-api-wlan version: 1.6.3 # Plugin version - sh_minversion: 1.9.0 # minimum shNG version to use this plugin + sh_minversion: '1.9.0' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: True diff --git a/uzsu/plugin.yaml b/uzsu/plugin.yaml index 3de41fcb0..12c7eb150 100755 --- a/uzsu/plugin.yaml +++ b/uzsu/plugin.yaml @@ -25,7 +25,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1364692-supportthread-für-uzsu-plugin version: 2.0.2 # Plugin version - sh_minversion: 1.6 # minimum shNG version to use this plugin + sh_minversion: '1.6' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: unknown diff --git a/vacations/plugin.yaml b/vacations/plugin.yaml index 85b831073..39909fc4e 100755 --- a/vacations/plugin.yaml +++ b/vacations/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: 'https://knx-user-forum.de/forum/supportforen/smarthome-py/1443788-support-thread-f%C3%BCr-das-vacations-plugin' version: 1.0.3 # Plugin version - sh_minversion: 1.6 # minimum shNG version to use this plugin + sh_minversion: '1.6' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance classname: Vacations # class containing the plugin diff --git a/vicare/plugin.yaml b/vicare/plugin.yaml index b1a44c6a7..6bc06a741 100644 --- a/vicare/plugin.yaml +++ b/vicare/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1916122-support-thread-f%C3%BCr-das-viessmann-plugin version: 1.9.5 # Plugin version - sh_minversion: 1.9.0 # minimum shNG version to use this plugin + sh_minversion: '1.9.0' # minimum shNG version to use this plugin #sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: True diff --git a/viessmann/plugin.yaml b/viessmann/plugin.yaml index a77d0a8ed..3ed88e2ea 100755 --- a/viessmann/plugin.yaml +++ b/viessmann/plugin.yaml @@ -14,8 +14,8 @@ plugin: keywords: viessmann heating optolink state: ready # change to ready when done with development version: 1.2.3 # Plugin version - sh_minversion: 1.6.0 # minimum shNG version to use this plugin - py_minversion: 3.6 + sh_minversion: '1.6.0' # minimum shNG version to use this plugin + py_minversion: '3.6' multi_instance: false # plugin supports multi instance restartable: true classname: Viessmann # class containing the plugin diff --git a/visu_smartvisu/plugin.yaml b/visu_smartvisu/plugin.yaml index 2995827e5..ce035bc55 100755 --- a/visu_smartvisu/plugin.yaml +++ b/visu_smartvisu/plugin.yaml @@ -13,7 +13,7 @@ plugin: # documentation: '' # Hier eine url zu evtl. vorhandener Doku einfügen - NICHT die url der user_doc!!! version: 1.3.4 # Plugin version - sh_minversion: 1.4 # minimum shNG version to use this plugin + sh_minversion: '1.4' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: True # plugin supports multi instance restartable: True diff --git a/visu_websocket/plugin.yaml b/visu_websocket/plugin.yaml index a1f6795b5..af4b713fe 100755 --- a/visu_websocket/plugin.yaml +++ b/visu_websocket/plugin.yaml @@ -13,7 +13,7 @@ plugin: documentation: http://smarthomeng.de/user/plugins/visu_websocket/user_doc.html version: 1.5.3 # Plugin version - sh_minversion: 1.9.0 # minimum shNG version to use this plugin + sh_minversion: '1.9.0' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: unknown diff --git a/volkszaehler/plugin.yaml b/volkszaehler/plugin.yaml index 9b69dff9b..d94774b4e 100755 --- a/volkszaehler/plugin.yaml +++ b/volkszaehler/plugin.yaml @@ -14,7 +14,7 @@ plugin: # Following entries are for Smart-Plugins: version: 1.6.1 # Plugin version - sh_minversion: 1.3 # minimum shNG version to use this plugin + sh_minversion: '1.3' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: True restartable: True diff --git a/waterkotte/plugin.yaml b/waterkotte/plugin.yaml index 7dfb386f9..281763843 100755 --- a/waterkotte/plugin.yaml +++ b/waterkotte/plugin.yaml @@ -15,7 +15,7 @@ plugin: # support: version: 1.0.0 # Plugin version - sh_minversion: 1.3 # minimum shNG version to use this plugin + sh_minversion: '1.3' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: unknown diff --git a/webpush/plugin.yaml b/webpush/plugin.yaml index df6291e47..7148c9f00 100755 --- a/webpush/plugin.yaml +++ b/webpush/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1817454-support-thread version: 1.1.0 # Plugin version (must match the version specified in __init__.py) - sh_minversion: 1.8 # minimum shNG version to use this plugin + sh_minversion: '1.8' # minimum shNG version to use this plugin multi_instance: false # plugin supports multi instance restartable: unknown classname: WebPush # class containing the plugin diff --git a/webservices/plugin.yaml b/webservices/plugin.yaml index 09bd4ba4d..1623c9b10 100755 --- a/webservices/plugin.yaml +++ b/webservices/plugin.yaml @@ -14,7 +14,7 @@ plugin: support: https://knx-user-forum.de/node/1163886 version: 1.6.4 # Plugin version - sh_minversion: 1.6 # minimum shNG version to use this plugin + sh_minversion: '1.6' # minimum shNG version to use this plugin #sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: unknown diff --git a/wettercom/plugin.yaml b/wettercom/plugin.yaml index 5693dc18e..ae11df1e4 100755 --- a/wettercom/plugin.yaml +++ b/wettercom/plugin.yaml @@ -13,7 +13,7 @@ plugin: # support: https://knx-user-forum.de/forum/supportforen/smarthome-py version: 1.4.0 # Plugin version - sh_minversion: 1.4 # minimum shNG version to use this plugin + sh_minversion: '1.4' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: unknown diff --git a/withings_health/plugin.yaml b/withings_health/plugin.yaml index 5fdb60754..b42d35a1d 100755 --- a/withings_health/plugin.yaml +++ b/withings_health/plugin.yaml @@ -12,7 +12,7 @@ plugin: support: 'https://knx-user-forum.de/forum/supportforen/smarthome-py/1141179-nokia-health-plugin' version: 1.8.4 # Plugin version - sh_minversion: 1.7 # minimum shNG version to use this plugin + sh_minversion: '1.7' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: True # plugin supports multi instance restartable: True diff --git a/wol/plugin.yaml b/wol/plugin.yaml index de60e8522..20f4a653a 100755 --- a/wol/plugin.yaml +++ b/wol/plugin.yaml @@ -12,9 +12,9 @@ plugin: #documentation: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1661427 version: 1.2.0 # Plugin version - sh_minversion: 1.8 # minimum shNG version to use this plugin + sh_minversion: '1.8' # minimum shNG version to use this plugin #sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) - py_minversion: 3.6 # minimum Python version to use for this plugin + py_minversion: '3.6' # minimum Python version to use for this plugin #py_maxversion: # maximum Python version to use for this plugin (leave empty if latest) multi_instance: True # plugin supports multi instance restartable: True diff --git a/wunderground/plugin.yaml b/wunderground/plugin.yaml index 9a544e10d..31c15d808 100755 --- a/wunderground/plugin.yaml +++ b/wunderground/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1059124-neues-wunderground-plugin version: 1.4.9 # Plugin version - sh_minversion: 1.5 # minimum shNG version to use this plugin + sh_minversion: '1.5' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: True # plugin supports multi instance restartable: unknown diff --git a/xiaomi_vac/plugin.yaml b/xiaomi_vac/plugin.yaml index 27ef287af..cb5e4c998 100755 --- a/xiaomi_vac/plugin.yaml +++ b/xiaomi_vac/plugin.yaml @@ -23,7 +23,7 @@ plugin: ' support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1453597-support-thread-f%C3%BCr-xiaomi-saugroboter-plugin version: 1.2.4 # Plugin version - sh_minversion: 1.4 # minimum shNG version to use this plugin + sh_minversion: '1.4' # minimum shNG version to use this plugin multi_instance: False # plugin supports multi instance classname: Robvac # class containing the plugin restartable: unknown diff --git a/xmpp/plugin.yaml b/xmpp/plugin.yaml index 1967ecc11..1e4b58e95 100755 --- a/xmpp/plugin.yaml +++ b/xmpp/plugin.yaml @@ -13,7 +13,7 @@ plugin: # Following entries are for Smart-Plugins: version: 1.4.1 # Plugin version - sh_minversion: 1.3 # minimum shNG version to use this plugin + sh_minversion: '1.3' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False restartable: unknown diff --git a/yamaha/plugin.yaml b/yamaha/plugin.yaml index 60762f9f8..052f0f71b 100755 --- a/yamaha/plugin.yaml +++ b/yamaha/plugin.yaml @@ -13,7 +13,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/896468-plugin-yamaha version: 1.0.3 # Plugin version - sh_minversion: 1.1 # minimum shNG version to use this plugin + sh_minversion: '1.1' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: True diff --git a/yamahayxc/plugin.yaml b/yamahayxc/plugin.yaml index 106297c51..9da7b64bf 100755 --- a/yamahayxc/plugin.yaml +++ b/yamahayxc/plugin.yaml @@ -65,7 +65,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1174064-plugin-yamaha-musiccast-geräte-neuere-generation version: 1.0.6 # Plugin version - sh_minversion: 1.3 # minimum shNG version to use this plugin + sh_minversion: '1.3' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: False diff --git a/zigbee2mqtt/plugin.yaml b/zigbee2mqtt/plugin.yaml index 9629740b5..d9de7f569 100755 --- a/zigbee2mqtt/plugin.yaml +++ b/zigbee2mqtt/plugin.yaml @@ -13,9 +13,9 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1856775-support-thread-f%C3%BCr-das-zigbee2mqtt-plugin version: 2.0.1 # Plugin version - sh_minversion: 1.9.5.6 # minimum shNG version to use this plugin + sh_minversion: '1.9.5.6' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) - py_minversion: 3.8 # minimum Python version to use for this plugin + py_minversion: '3.8' # minimum Python version to use for this plugin multi_instance: true # plugin supports multi instance restartable: true classname: Zigbee2Mqtt # class containing the plugin diff --git a/zwave/plugin.yaml b/zwave/plugin.yaml index 0ffc9132c..0e327f456 100755 --- a/zwave/plugin.yaml +++ b/zwave/plugin.yaml @@ -12,10 +12,10 @@ plugin: # documentation: https://github.com/smarthomeNG/smarthome/wiki/CLI-Plugin # url of documentation (wiki) page version: 1.4.2 # Plugin version - sh_minversion: 1.3 # minimum shNG version to use this plugin + sh_minversion: '1.3' # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) # py_minversion: 3.6 # minimum Python version to use for this plugin - py_maxversion: 3.8 # maximum Python version to use for this plugin (leave empty if latest) + py_maxversion: '3.8' # maximum Python version to use for this plugin (leave empty if latest) py_versioncomment: "Die aktuellste Version (0.4.19, released März 2019) des benötigten Packages 'openzwave' ist nicht kompatibel mit Python 3.9" multi_instance: False # plugin supports multi instance classname: ZWave # class containing the plugin From d01d4226237aad073de0393b44bd17f3b14ea1b9 Mon Sep 17 00:00:00 2001 From: Morg42 <43153739+Morg42@users.noreply.github.com> Date: Fri, 28 Jun 2024 09:16:32 +0200 Subject: [PATCH 19/23] enocean: update plugin.yaml --- enocean/plugin.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/enocean/plugin.yaml b/enocean/plugin.yaml index 38c69d850..1d677fb49 100755 --- a/enocean/plugin.yaml +++ b/enocean/plugin.yaml @@ -17,7 +17,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/26542-featurewunsch-enocean-plugin/page13 version: 1.4.0 # Plugin version - sh_minversion: '1.3'1. # minimum shNG version to use this plugin + sh_minversion: '1.3' # minimum shNG version to use this plugin #sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False # plugin supports multi instance restartable: unknown From 6100458f4ba519fef6aa911c301582d6885659ee Mon Sep 17 00:00:00 2001 From: Morg42 <43153739+Morg42@users.noreply.github.com> Date: Fri, 28 Jun 2024 09:18:38 +0200 Subject: [PATCH 20/23] kodi: update plugin.yaml --- kodi/plugin.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kodi/plugin.yaml b/kodi/plugin.yaml index 50b961527..48bcc26e5 100644 --- a/kodi/plugin.yaml +++ b/kodi/plugin.yaml @@ -7,7 +7,9 @@ plugin: state: develop keywords: iot device mediacenter kodi xmbc sdp version: 1.7.2 - sh_minversion: '1.9.5'9. py_minversion: '3.7'3. multi_instance: true + sh_minversion: '1.9.5' + py_minversion: '3.7' + multi_instance: true restartable: true classname: kodi From dde2173825b7f45e99c821704419179e6eb8616d Mon Sep 17 00:00:00 2001 From: Morg42 <43153739+Morg42@users.noreply.github.com> Date: Fri, 28 Jun 2024 09:22:51 +0200 Subject: [PATCH 21/23] enocean: fix sh min version number --- enocean/plugin.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/enocean/plugin.yaml b/enocean/plugin.yaml index e8eaaf319..443a10827 100755 --- a/enocean/plugin.yaml +++ b/enocean/plugin.yaml @@ -17,7 +17,7 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/26542-featurewunsch-enocean-plugin/page13 version: 1.4.2 # Plugin version - sh_minversion: 1.9 # minimum shNG version to use this plugin + sh_minversion: '1.9' # minimum shNG version to use this plugin #sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: false # plugin supports multi instance restartable: true From 1f1d1b56dd7185c7ef219f7183d7e0cc7c207946 Mon Sep 17 00:00:00 2001 From: msinn Date: Fri, 28 Jun 2024 17:13:07 +0200 Subject: [PATCH 22/23] workflows: Removed Python3.8 from unittests --- .github/workflows/unittests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/unittests.yml b/.github/workflows/unittests.yml index 6476dbe37..9abf4e7d5 100755 --- a/.github/workflows/unittests.yml +++ b/.github/workflows/unittests.yml @@ -15,7 +15,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: [ '3.8', '3.9', '3.10', '3.11', '3.12' ] + python-version: [ '3.9', '3.10', '3.11', '3.12' ] name: Python ${{ matrix.python-version }} steps: - name: Setup OS (Ubuntu) From 86a2576f3d2e60d8a22c4cf81e1c7de0d895dde2 Mon Sep 17 00:00:00 2001 From: aschwith Date: Sat, 29 Jun 2024 15:38:18 +0200 Subject: [PATCH 23/23] resol: fixed an issue preventing the plugin from being restartable --- resol/__init__.py | 55 +++++++++++++++++++++++++++++------------------ resol/plugin.yaml | 6 +++--- 2 files changed, 37 insertions(+), 24 deletions(-) diff --git a/resol/__init__.py b/resol/__init__.py index cf20f1b53..60776642c 100755 --- a/resol/__init__.py +++ b/resol/__init__.py @@ -25,12 +25,12 @@ class Resol(SmartPlugin): - PLUGIN_VERSION = '1.0.7' # (must match the version specified in plugin.yaml) + PLUGIN_VERSION = '1.1.0' # (must match the version specified in plugin.yaml) def __init__(self, sh): """ - Initalizes the plugin. + Initializes the plugin. """ @@ -44,12 +44,14 @@ def __init__(self, sh): self._cycle = self.get_parameter_value('cycle') self._password = self.get_parameter_value('password') self._to_do = True + self.socket = None + self._last_request_successfull = False #self._client = Tcp_client(name=name, host=self._ip, port=self._port, binary=True, autoreconnect=True, connect_cycle=5, retry_cycle=30) def run(self): self.alive = True - self.scheduler_add('PollData', self.sock, prio=5, cycle=self._cycle, offset=2) + self.scheduler_add('PollData', self.pull_socket, prio=5, cycle=self._cycle, offset=2) # if you want to create child threads, do not make them daemon = True! # They will not shutdown properly. (It's a python bug) @@ -57,13 +59,13 @@ def stop(self): self.scheduler_remove('PollData') try: - if self.sock: - self.sock.shutdown(0) - self.sock.close() + if self.socket: + self.socket.shutdown(0) + self.socket.close() except: pass - self.sock = None + self.socket = None self.alive = False def parse_item(self, item): @@ -90,18 +92,21 @@ def update_item(self, item, caller=None, source=None, dest=None): # do nothing if items are changed from outside the plugin pass - def sock(self): + def pull_socket(self): if not self.alive: + self._last_request_successfull = False return self.logger.info("1) Starting sock function") - self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.logger.info("2) Connecting socket") try: - self.sock.connect((self._ip, self._port)) + self.socket.connect((self._ip, self._port)) except socket.timeout as e: self.logger.warning("Timeout exception during socket connect: %s" % str(e)) + self._last_request_successfull = False return except Exception as e: + self._last_request_successfull = False self.logger.error("Exception during socket connect: %s" % str(e)) return self.logger.info("3) Logging in") @@ -110,16 +115,17 @@ def sock(self): self.load_data() else: self.logger.warning("Cannot login") + self._last_request_successfull = False self.logger.info("5) Shutting down socket") try: - if self.sock: - self.sock.shutdown(0) - self.sock.close() + if self.socket: + self.socket.shutdown(0) + self.socket.close() except Exception as e: self.logger.warning("Exception during shutdown socket command: {0}".format(e)) pass self.logger.info("6) Ending socket function") - self.sock = None + self.socket = None # Logs in onto the DeltaSol BS Plus over LAN. Also starts (and maintains) the # actual stream of data. @@ -158,14 +164,16 @@ def load_data(self): if not dat: self.logger.warning("Could not receive data via socket") + self._last_request_successfull = False return self.logger.info("Response to data: " + str(dat)) #Check if device is ready to send Data if not dat.startswith("+OK"): - self.logger.warning("Vbus Lan is not ready, reply: " + str(dat)) - return + self.logger.warning("Vbus Lan is not ready, reply: " + str(dat)) + self._last_request_successfull = False + return buf = self.readstream() #self.logger.warning("Readstream {0} bytes as asci: {1}".format(len(buf),str(buf))) @@ -182,6 +190,7 @@ def load_data(self): #self.logger.warning("Readstream hex {0} bytes: {1}".format(len(buf), s.hex())) if not buf: + self._last_request_successfull = False return msgs = self.splitmsg(buf) @@ -195,12 +204,12 @@ def load_data(self): # Receive 1024 bytes from stream def recv(self): - if not self.sock: + if not self.socket: self.logger.error("Error during data reception: Socket is not valid") return None - self.sock.settimeout(5) + self.socket.settimeout(5) try: - dat = self.sock.recv(1024).decode('Cp1252') + dat = self.socket.recv(1024).decode('Cp1252') except socket.timeout: self.logger.info("Exception in recv(): Socket reception timeout") return None @@ -212,9 +221,9 @@ def recv(self): # Sends given bytes over the stream. Adds debug def send(self, dat): - if not self.sock: + if not self.socket: return - self.sock.send(dat.encode('utf-8')) + self.socket.send(dat.encode('utf-8')) # Read Data until minimum 1 message is received def readstream(self): @@ -323,6 +332,7 @@ def parse_payload_pv1(self, msg): if not self.check_header_crc(msg): self.logger.warning("Header crc error") + self._last_request_successfull = False return command = self.get_command(msg) @@ -339,7 +349,10 @@ def parse_payload_pv1(self, msg): if payload == '': self.logger.warning("Payload is empty") + self._last_request_successfull = False return + + self._last_request_successfull = True for item in self._items: parentItem = item.return_parent() diff --git a/resol/plugin.yaml b/resol/plugin.yaml index 6df5805e9..65972efec 100755 --- a/resol/plugin.yaml +++ b/resol/plugin.yaml @@ -13,11 +13,11 @@ plugin: support: https://knx-user-forum.de/forum/supportforen/smarthome-py/919242-neues-plugin-resol-vbus-cosmo-multi-solarthermie-logging/page4 # Following entries are for Smart-Plugins: - version: 1.0.7 # Plugin version - sh_minversion: '1.7.2' # minimum shNG version to use this plugin + version: 1.1.0 # Plugin version + sh_minversion: 1.7.2 # minimum shNG version to use this plugin # sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: True - restartable: unknown + restartable: True classname: Resol # class containing the plugin parameters: