From 36261d57caf817a228084974f3aa29b8801057c7 Mon Sep 17 00:00:00 2001 From: flexxor Date: Thu, 4 Aug 2022 22:06:51 +0200 Subject: [PATCH 01/40] add callback-property --- enocean/communicators/communicator.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/enocean/communicators/communicator.py b/enocean/communicators/communicator.py index 9aff7c0..f5eeceb 100644 --- a/enocean/communicators/communicator.py +++ b/enocean/communicators/communicator.py @@ -81,6 +81,14 @@ def parse(self): self.__callback(packet) self.logger.debug(packet) + @property # getter + def callback(self): + return self.__callback + + @callback.setter + def callback(self, callback): + self.__callback = callback + @property def base_id(self): ''' Fetches Base ID from the transmitter, if required. Otherwise returns the currently set Base ID. ''' From c54beafa958240daf88dc7c928588dd107743a2c Mon Sep 17 00:00:00 2001 From: Henning Kerstan Date: Sat, 27 Aug 2022 09:56:46 +0200 Subject: [PATCH 02/40] get version info and chip id from module --- enocean/communicators/communicator.py | 77 +++++++++++++++++++++++++-- enocean/protocol/version_info.py | 15 ++++++ examples/enocean_example.py | 1 + 3 files changed, 89 insertions(+), 4 deletions(-) create mode 100644 enocean/protocol/version_info.py diff --git a/enocean/communicators/communicator.py b/enocean/communicators/communicator.py index 9aff7c0..5e4965f 100644 --- a/enocean/communicators/communicator.py +++ b/enocean/communicators/communicator.py @@ -4,6 +4,8 @@ import datetime import threading +from enocean.protocol.version_info import VersionInfo + try: import queue except ImportError: @@ -32,6 +34,8 @@ def __init__(self, callback=None, teach_in=True): self.__callback = callback # Internal variable for the Base ID of the module. self._base_id = None + # Internal variable for the version info of the module. + self._version_info = None # Should new messages be learned automatically? Defaults to True. # TODO: Not sure if we should use CO_WR_LEARNMODE?? self.teach_in = teach_in @@ -88,12 +92,17 @@ def base_id(self): if self._base_id is not None: return self._base_id + start = datetime.datetime.now() + # Send COMMON_COMMAND 0x08, CO_RD_IDBASE request to the module self.send(Packet(PACKET.COMMON_COMMAND, data=[0x08])) - # Loop over 10 times, to make sure we catch the response. - # Thanks to timeout, shouldn't take more than a second. - # Unfortunately, all other messages received during this time are ignored. - for i in range(0, 10): + + # wait at most 1 second for the response + while True: + seconds_elapsed = (datetime.datetime.now() - start).total_seconds() + if seconds_elapsed > 1: + self.logger.error("Could not obtain base id from module within 1 second (timeout).") + break try: packet = self.receive.get(block=True, timeout=0.1) # We're only interested in responses to the request in question. @@ -114,3 +123,63 @@ def base_id(self): def base_id(self, base_id): ''' Sets the Base ID manually, only for testing purposes. ''' self._base_id = base_id + + @property + def chip_id(self): + ''' Fetches Chip ID from the transmitter, if required. Otherwise returns the currently set Chip ID. ''' + return self.version_info.chip_id + + @property + def version_info(self): + ''' Fetches version info from the transmitter, if required. Otherwise returns the currently set version info. ''' + + # If version info is already set, return it. + if self._version_info is not None: + return self._version_info + + start = datetime.datetime.now() + + # Send COMMON_COMMAND 0x03, CO_RD_VERSION request to the module + self.send(Packet(PACKET.COMMON_COMMAND, data=[0x03])) + + # wait at most 1 second for the response + while True: + seconds_elapsed = (datetime.datetime.now() - start).total_seconds() + if seconds_elapsed > 1: + self.logger.error("Could not obtain version info from module within 1 second (timeout).") + break + + try: + packet = self.receive.get(block=True, timeout=0.1) + if packet.packet_type == PACKET.RESPONSE and packet.response == RETURN_CODE.OK and len(packet.response_data) == 32: + # interpret the version info + self._version_info: VersionInfo = VersionInfo() + res = packet.response_data + + self._version_info.app_version.main = res[0] + self._version_info.app_version.beta = res[1] + self._version_info.app_version.alpha = res[2] + self._version_info.app_version.build = res[3] + + self._version_info.api_version.main = res[4] + self._version_info.api_version.beta = res[5] + self._version_info.api_version.alpha = res[6] + self._version_info.api_version.build = res[7] + + self._version_info.chip_id = [ + res[8], res[9], res[10], res[11] + ] + self._version_info.chip_version = int.from_bytes(res[12:15], 'big') + + self._version_info.app_description = bytearray(res[16:32]).decode('utf8').strip() + + # Put packet back to the Queue, so the user can also react to it if required... + self.receive.put(packet) + break + # Put other packets back to the Queue. + self.receive.put(packet) + except queue.Empty: + continue + # Return the current version info (might be None). + return self._version_info + diff --git a/enocean/protocol/version_info.py b/enocean/protocol/version_info.py new file mode 100644 index 0000000..af98c4b --- /dev/null +++ b/enocean/protocol/version_info.py @@ -0,0 +1,15 @@ +# -*- encoding: utf-8 -*- +from __future__ import print_function, unicode_literals, division, absolute_import + +class VersionIdentifier(object): + main = 0 + beta = 0 + alpha = 0 + build = 0 + +class VersionInfo(object): + app_version: VersionIdentifier = VersionIdentifier() + api_version: VersionIdentifier = VersionIdentifier() + chip_id: 0 + chip_version = 0 + app_description = '' \ No newline at end of file diff --git a/examples/enocean_example.py b/examples/enocean_example.py index be9eb0c..bfebc59 100755 --- a/examples/enocean_example.py +++ b/examples/enocean_example.py @@ -25,6 +25,7 @@ def assemble_radio_packet(transmitter_id): init_logging() communicator = SerialCommunicator() communicator.start() +print('The Chip ID of your module is %s.' % enocean.utils.to_hex_string(communicator.chip_id)) print('The Base ID of your module is %s.' % enocean.utils.to_hex_string(communicator.base_id)) if communicator.base_id is not None: From fab299b9f56d8e9c72d2cc8ef1e4230fc45c43c0 Mon Sep 17 00:00:00 2001 From: Henning Kerstan Date: Sat, 27 Aug 2022 10:10:49 +0200 Subject: [PATCH 03/40] return None for chip id if version info is None --- enocean/communicators/communicator.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/enocean/communicators/communicator.py b/enocean/communicators/communicator.py index 5e4965f..880acc3 100644 --- a/enocean/communicators/communicator.py +++ b/enocean/communicators/communicator.py @@ -127,7 +127,10 @@ def base_id(self, base_id): @property def chip_id(self): ''' Fetches Chip ID from the transmitter, if required. Otherwise returns the currently set Chip ID. ''' - return self.version_info.chip_id + if self.version_info is not None: + return self.version_info.chip_id + + return None @property def version_info(self): From f93bc9da17c7063f289ff6634c471a4a6b421d7d Mon Sep 17 00:00:00 2001 From: Henning Kerstan Date: Sat, 27 Aug 2022 13:47:11 +0200 Subject: [PATCH 04/40] add enum with the used EnOcean common command codes --- enocean/communicators/communicator.py | 6 +++--- enocean/protocol/constants.py | 4 ++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/enocean/communicators/communicator.py b/enocean/communicators/communicator.py index 880acc3..c227f21 100644 --- a/enocean/communicators/communicator.py +++ b/enocean/communicators/communicator.py @@ -11,7 +11,7 @@ except ImportError: import Queue as queue from enocean.protocol.packet import Packet, UTETeachInPacket -from enocean.protocol.constants import PACKET, PARSE_RESULT, RETURN_CODE +from enocean.protocol.constants import COMMON_COMMAND_CODE, PACKET, PARSE_RESULT, RETURN_CODE class Communicator(threading.Thread): @@ -95,7 +95,7 @@ def base_id(self): start = datetime.datetime.now() # Send COMMON_COMMAND 0x08, CO_RD_IDBASE request to the module - self.send(Packet(PACKET.COMMON_COMMAND, data=[0x08])) + self.send(Packet(PACKET.COMMON_COMMAND, data=[COMMON_COMMAND_CODE.CO_RD_IDBASE.value])) # wait at most 1 second for the response while True: @@ -143,7 +143,7 @@ def version_info(self): start = datetime.datetime.now() # Send COMMON_COMMAND 0x03, CO_RD_VERSION request to the module - self.send(Packet(PACKET.COMMON_COMMAND, data=[0x03])) + self.send(Packet(PACKET.COMMON_COMMAND, data=[COMMON_COMMAND_CODE.CO_RD_VERSION.value])) # wait at most 1 second for the response while True: diff --git a/enocean/protocol/constants.py b/enocean/protocol/constants.py index 8dd3e74..38cf492 100644 --- a/enocean/protocol/constants.py +++ b/enocean/protocol/constants.py @@ -146,3 +146,7 @@ class DB6(object): BIT_5 = -54 BIT_6 = -55 BIT_7 = -56 + +class COMMON_COMMAND_CODE(IntEnum): + CO_RD_VERSION = 0x03 + CO_RD_IDBASE = 0x08 \ No newline at end of file From b3e37c3ff4d439ed65607d9dadb03dd4c914b8f6 Mon Sep 17 00:00:00 2001 From: Yusuf Can Bayrak Date: Sat, 12 Nov 2022 19:10:18 +0100 Subject: [PATCH 05/40] Update EEP.xml --- enocean/protocol/EEP.xml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/enocean/protocol/EEP.xml b/enocean/protocol/EEP.xml index b7d79e2..765e781 100644 --- a/enocean/protocol/EEP.xml +++ b/enocean/protocol/EEP.xml @@ -745,6 +745,20 @@ + + + + + 0 + 255 + + + 0 + 2000 + + + + From 82865f99c3df6e3d1b850b73db96ad846524c5a8 Mon Sep 17 00:00:00 2001 From: lolplusultra <59609126+lolplusultra@users.noreply.github.com> Date: Wed, 22 Feb 2023 09:05:15 +0100 Subject: [PATCH 06/40] Continue on error If the port is busy do not parse anything. Without this I am able to start multiple communicators per port and send a command to it in this short timeframe. --- enocean/communicators/serialcommunicator.py | 1 + 1 file changed, 1 insertion(+) diff --git a/enocean/communicators/serialcommunicator.py b/enocean/communicators/serialcommunicator.py index ffe4201..f13998c 100644 --- a/enocean/communicators/serialcommunicator.py +++ b/enocean/communicators/serialcommunicator.py @@ -36,6 +36,7 @@ def run(self): except serial.SerialException: self.logger.error('Serial port exception! (device disconnected or multiple access on port?)') self.stop() + continue self.parse() time.sleep(0) From 2a16ea3edef0bee7fad0339fa4e6b846e8ac3a3a Mon Sep 17 00:00:00 2001 From: topic2k Date: Mon, 26 Aug 2024 18:00:04 +0200 Subject: [PATCH 07/40] Adds EEP A5-07-03 --- enocean/protocol/EEP.xml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/enocean/protocol/EEP.xml b/enocean/protocol/EEP.xml index b7d79e2..94c9700 100644 --- a/enocean/protocol/EEP.xml +++ b/enocean/protocol/EEP.xml @@ -622,6 +622,34 @@ + + + + + 0 + 250 + + + 0 + 5.000000 + + + + + 0 + 1000 + + + 0 + 1000 + + + + + + + + From db1be6e374f2dabfc945cce7be2f53fe728d8804 Mon Sep 17 00:00:00 2001 From: topic2k Date: Mon, 26 Aug 2024 18:00:46 +0200 Subject: [PATCH 08/40] Adds EEP D2-01-0F --- enocean/protocol/EEP.xml | 78 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/enocean/protocol/EEP.xml b/enocean/protocol/EEP.xml index 94c9700..6a20b83 100644 --- a/enocean/protocol/EEP.xml +++ b/enocean/protocol/EEP.xml @@ -1649,6 +1649,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From d6946ca26e7e240d027e84bdeb46487b091e3aaf Mon Sep 17 00:00:00 2001 From: topic2k Date: Mon, 26 Aug 2024 18:02:09 +0200 Subject: [PATCH 09/40] group multiple if's to if/elif --- enocean/protocol/eep.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/enocean/protocol/eep.py b/enocean/protocol/eep.py index 8da3156..f0b675b 100644 --- a/enocean/protocol/eep.py +++ b/enocean/protocol/eep.py @@ -200,12 +200,17 @@ def get_values(self, profile, bitarray, status): for source in profile.contents: if not source.name: continue - if source.name == 'value': + elif source.name == 'value': output.update(self._get_value(source, bitarray)) - if source.name == 'enum': - output.update(self._get_enum(source, bitarray)) - if source.name == 'status': + elif source.name == 'enum': + try: + output.update(self._get_enum(source, bitarray)) + except (ValueError, TypeError): + pass + elif source.name == 'status': output.update(self._get_boolean(source, status)) + # else: + # pass return output.keys(), output def set_values(self, profile, data, status, properties): From 532629f9510d4987e9514c6de650fac3f2f4bdd9 Mon Sep 17 00:00:00 2001 From: topic2k Date: Mon, 26 Aug 2024 18:04:18 +0200 Subject: [PATCH 10/40] update SUPPORTED_PROFILES.md --- SUPPORTED_PROFILES.md | 139 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) diff --git a/SUPPORTED_PROFILES.md b/SUPPORTED_PROFILES.md index 1ca36d8..f73a5a7 100644 --- a/SUPPORTED_PROFILES.md +++ b/SUPPORTED_PROFILES.md @@ -326,6 +326,16 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian | | | |1 - on | +##### RORG 0xA5 - FUNC 0x07 - TYPE 0x03 - Occupancy with Supply voltage monitor and 10-bit illumination measurement + +|shortcut|description |type |values | +|--------|--------------------------------------------------|--------|---- | +|SVC |Supply voltage (OPTIONAL) |value |0.0-250.0 ↔ 0.0-5.0 V | +|ILL |Illumination (linear) |value |0.0-1000.0 ↔ 0.0-1000.0 lx | +|PIR |PIR Status |enum |0 - Uncertain of occupancy status | +| | | |1 - Motion detected | + + ##### RORG 0xA5 - FUNC 0x08 - TYPE 0x01 - Range 0lx to 510lx, 0°C to +51°C and Occupancy Button @@ -495,6 +505,73 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian +##### RORG 0xA5 - FUNC 0x13 - TYPE 0x01 - Weather Station + +###### command: 1 +|shortcut|description |type |values | +|--------|--------------------------------------------------|--------|---- | +|DWS |Dawn sensor |value |0.0-255.0 ↔ 0.0-999.0 lx | +|TMP |Outdoor Temp |value |0.0-255.0 ↔ -40.0-80.0 °C | +|WND |Wind speed |value |0.0-255.0 ↔ 0.0-70.0 m/s | +|D/N |Day / Night |enum |0 - day | +| | | |1 - night | +|RAN |Rain Indication |enum |0 - no rain | +| | | |1 - rain | + +###### command: 2 +|shortcut|description |type |values | +|--------|--------------------------------------------------|--------|---- | +|SNW |Sun - West |value |0.0-255.0 ↔ 0.0-150.0 klx | +|SNS |Sun - South |value |0.0-255.0 ↔ 0.0-150.0 klx | +|SNE |Sun - East |value |0.0-255.0 ↔ 0.0-150.0 klx | +|HEM |Hemisphere |enum |0 - North | +| | | |1 - South | + +###### command: 3 +|shortcut|description |type |values | +|--------|--------------------------------------------------|--------|---- | +|DY |Day |value |1.0-31.0 ↔ 1.0-31.0 | +|MTH |Month |value |1.0-12.0 ↔ 1.0-12.0 | +|YR |Year |value |0.0-99.0 ↔ 2000.0-2099.0 | +|SRC |Source |enum |0 - Real Time Clock | +| | | |1 - GPS or equivalent | + +###### command: 4 +|shortcut|description |type |values | +|--------|--------------------------------------------------|--------|---- | +|WDY |Weekday |enum |1 - Monday | +| | | |2 - Tuesday | +| | | |3 - Wednesday | +| | | |4 - Thursday | +| | | |5 - Friday | +| | | |6 - Saturday | +| | | |7 - Sunday | +|HR |Hour |value |0.0-23.0 ↔ 0.0-23.0 | +|MIN |Minute |value |0.0-59.0 ↔ 0.0-59.0 | +|SEC |Second |value |0.0-59.0 ↔ 0.0-59.0 | +|TMF |Time Format |enum |0 - 24 Hours | +| | | |1 - 12 Hours | +|A/PM |AM/PM |enum |0 - AM | +| | | |1 - PM | +|SRC |Source |enum |0 - Real Time Clock | +| | | |1 - GPS or equivalent | + +###### command: 5 +|shortcut|description |type |values | +|--------|--------------------------------------------------|--------|---- | +|ELV |Elevation |value |0.0-180.0 ↔ -90.0-90.0 ° | +|AZM |Azimut |value |0.0-359.0 ↔ 0.0-359.0 ° | + +###### command: 6 +|shortcut|description |type |values | +|--------|--------------------------------------------------|--------|---- | +|LAT(MSB)|Latitude(MSB) |value |0.0-15.0 ↔ 0.0-15.0 | +|LOT(MSB)|Longitude(MSB) |value |0.0-15.0 ↔ 0.0-15.0 | +|LAT(LSB)|Latitude(LSB) |value |0.0-255.0 ↔ 0.0-255.0 | +|LOT(LSB)|Longitude(LSB) |value |0.0-255.0 ↔ 0.0-255.0 | + + + ##### RORG 0xA5 - FUNC 0x14 - TYPE 0x01 - Single Input Contact (Window/Door), Supply voltage monitor |shortcut|description |type |values | @@ -660,6 +737,50 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian | | | |127 - output value not valid / not set | +##### RORG 0xD2 - FUNC 0x01 - TYPE 0x0F - Electronic switch with Local Control + +###### command: 4 +|shortcut|description |type |values | +|--------|--------------------------------------------------|--------|---- | +|PF |Power Failure |enum |0 - Power Failure Detection disabled/not supported | +| | | |1 - Power Failure Detection enabled | +|PFD |Power Failure Detection |enum |0 - Power Failure Detection not detected/not supported/disabled | +| | | |1 - Power Failure Detection Detected | +|CMD |Command indentifier |enum |0-13 - Command ID {value} | +|OC |Over current switch off |enum |0 - Over current switch off: ready / not supported | +| | | |1 - Over current switch off: executed | +|EL |Error level |enum |0 - Error level 0: hardware OK | +| | | |1 - Error level 1: hardware warning | +| | | |2 - Error level 2: hardware failure | +| | | |3 - Error level not supported | +|IO |I/O channel |enum |0-29 - Output channel {value} (to load) | +| | | |30 - Not applicable, do not use | +| | | |31 - Input channel (from mains supply) | +|LC |Local control |enum |0 - Local control disabled / not supported | +| | | |1 - Local control enabled | +|OV |Output value |enum |0 - Output value 0% or OFF | +| | | |1-100 - Output value {value}% or ON | +| | | |101-126 - Not used | +| | | |127 - output value not valid / not set | + +###### command: 1 +|shortcut|description |type |values | +|--------|--------------------------------------------------|--------|---- | +|CMD |Command indentifier |enum |0-13 - Command ID {value} | +|DV |Dim value |enum |0 - Switch to new output value | +| | | |1 - Dim to new output level - dim timer 1 | +| | | |2 - Dim to new output level - dim timer 2 | +| | | |3 - Dim to new output level - dim timer 3 | +| | | |4 - Stop dimming | +|IO |I/O channel |enum |0-29 - Output channel {value} (to load) | +| | | |30 - All output channels supported by the device | +| | | |31 - Input channel (from mains supply) | +|OV |Output value |enum |0 - Output value 0% or OFF | +| | | |1-100 - Output value {value}% or ON | +| | | |101-126 - Not used | +| | | |127 - output value not valid / not set | + + ##### RORG 0xD2 - FUNC 0x05 - TYPE 0x00 - Type 0x00 @@ -721,3 +842,21 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian +##### RORG 0xD2 - FUNC 0x14 - TYPE 0x41 - Indoor -Temperature, Humidity XYZ Acceleration, Illumination Sensor + +|shortcut|description |type |values | +|--------|--------------------------------------------------|--------|---- | +|TMP |Temperature 10 |value |0.0-1000.0 ↔ -40.0-60.0 °C | +|HUM |Rel. Humidity linear) |value |0.0-200.0 ↔ 0.0-100.0 % | +|ILL |Illumination linear) |value |0.0-100000.0 ↔ 0.0-100000.0 lx | +|ACC |Acceleration Status |enum |0 - Periodic Update | +| | | |1 - Threshold 1 exceeded | +| | | |2 - Threshold 2 exceeded | +|ACX |Absolute Acceleration on X axis |value |0.0-1000.0 ↔ -2.5-2.5 g | +|ACY |Absolute Acceleration on Y axis |value |0.0-1000.0 ↔ -2.5-2.5 g | +|ACZ |Absolute Acceleration on Z axis |value |0.0-1000.0 ↔ -2.5-2.5 g | +|CO |Contact |enum |0 - Open | +| | | |1 - Closed | + + + From e391acbd821da59f8381a316d36d3f0991ff43d8 Mon Sep 17 00:00:00 2001 From: topic2k Date: Thu, 29 Aug 2024 18:38:53 +0200 Subject: [PATCH 11/40] Use lxml for parsing EEP.xml and remove Python 2.x compatibility code for opening the file. --- enocean/protocol/eep.py | 10 +++------- requirements.txt | 7 ++++--- setup.py | 7 ++++--- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/enocean/protocol/eep.py b/enocean/protocol/eep.py index f0b675b..2f54dc2 100644 --- a/enocean/protocol/eep.py +++ b/enocean/protocol/eep.py @@ -20,14 +20,10 @@ def __init__(self): eep_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'EEP.xml') try: - if version_info[0] > 2: - with open(eep_path, 'r', encoding='UTF-8') as xml_file: - self.soup = BeautifulSoup(xml_file.read(), "html.parser") - else: - with open(eep_path, 'r') as xml_file: - self.soup = BeautifulSoup(xml_file.read(), "html.parser") - self.init_ok = True + with open(eep_path, 'r', encoding='UTF-8') as xml_file: + self.soup = BeautifulSoup(xml_file.read(), features="lxml-xml") # ,"html.parser") self.__load_xml() + self.init_ok = True except IOError: # Impossible to test with the current structure? # To be honest, as the XML is included with the library, diff --git a/requirements.txt b/requirements.txt index fb7ca9a..28d974f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ -enum-compat>=0.0.2 -pyserial>=3.0 -beautifulsoup4>=4.3.2 +enum-compat>=0.0.3 +pyserial>=3.5 +beautifulsoup4>=4.12.3 +lxml>=5.3.0 \ No newline at end of file diff --git a/setup.py b/setup.py index 3fea09c..7befb2f 100644 --- a/setup.py +++ b/setup.py @@ -23,7 +23,8 @@ '': ['EEP.xml'] }, install_requires=[ - 'enum-compat>=0.0.2', - 'pyserial>=3.0', - 'beautifulsoup4>=4.3.2', + 'enum-compat>=0.0.3', + 'pyserial>=3.5', + 'beautifulsoup4>=4.12.3', + 'lxml>=5.3.0', ]) From 31972ea7a69f8a7b7bfb1b1dc21873b362fa88c5 Mon Sep 17 00:00:00 2001 From: topic2k Date: Sat, 31 Aug 2024 11:07:13 +0200 Subject: [PATCH 12/40] add constants for COMMON COMMANDS --- enocean/communicators/communicator.py | 4 ++-- enocean/protocol/constants.py | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/enocean/communicators/communicator.py b/enocean/communicators/communicator.py index 9aff7c0..1c6e219 100644 --- a/enocean/communicators/communicator.py +++ b/enocean/communicators/communicator.py @@ -9,7 +9,7 @@ except ImportError: import Queue as queue from enocean.protocol.packet import Packet, UTETeachInPacket -from enocean.protocol.constants import PACKET, PARSE_RESULT, RETURN_CODE +from enocean.protocol.constants import COMMON_COMMAND, PACKET, PARSE_RESULT, RETURN_CODE class Communicator(threading.Thread): @@ -89,7 +89,7 @@ def base_id(self): return self._base_id # Send COMMON_COMMAND 0x08, CO_RD_IDBASE request to the module - self.send(Packet(PACKET.COMMON_COMMAND, data=[0x08])) + self.send(Packet(PACKET.COMMON_COMMAND, data=[COMMON_COMMAND.CO_RD_IDBASE])) # Loop over 10 times, to make sure we catch the response. # Thanks to timeout, shouldn't take more than a second. # Unfortunately, all other messages received during this time are ignored. diff --git a/enocean/protocol/constants.py b/enocean/protocol/constants.py index 8dd3e74..34ee952 100644 --- a/enocean/protocol/constants.py +++ b/enocean/protocol/constants.py @@ -26,6 +26,12 @@ class PACKET(IntEnum): COMMAND_2_4 = 0x11 +# EnOceanSerialProtocol3-1-1.pdf / 33 +class COMMON_COMMAND(IntEnum): + CO_RD_VERSION = 0x03 + CO_RD_IDBASE = 0x08 + + # EnOceanSerialProtocol3.pdf / 18 class RETURN_CODE(IntEnum): OK = 0x00 From 5a8a1226ec0d20a192cc9342d76182ee68c417c9 Mon Sep 17 00:00:00 2001 From: topic2k Date: Sat, 31 Aug 2024 11:22:12 +0200 Subject: [PATCH 13/40] optimize imports --- enocean/communicators/__init__.py | 1 + enocean/communicators/communicator.py | 13 +++++-------- enocean/communicators/serialcommunicator.py | 5 +++-- enocean/communicators/tcpcommunicator.py | 2 +- .../communicators/tests/test_communicator.py | 5 ++--- enocean/communicators/utils.py | 2 +- enocean/consolelogger.py | 2 +- enocean/decorators.py | 4 ++-- enocean/protocol/constants.py | 2 +- enocean/protocol/crc8.py | 1 - enocean/protocol/eep.py | 6 +++--- enocean/protocol/packet.py | 4 ++-- enocean/protocol/tests/test_eep.py | 7 +++---- enocean/protocol/tests/test_packet.py | 5 ++--- enocean/protocol/tests/test_packet_creation.py | 6 +++--- enocean/protocol/tests/test_teachin.py | 5 ++--- .../protocol/tests/test_temperature_sensors.py | 2 +- enocean/tests/test_utils.py | 2 +- enocean/utils.py | 2 -- examples/co_rd_version_example.py | 17 +++++++---------- examples/enocean_example.py | 16 +++++++--------- examples/example_D2-05-00.py | 10 +++------- examples/example_DO21-11B-E.py | 9 +++------ examples/serial_to_tcp.py | 12 +++++------- examples/tcp_server.py | 12 +++++------- generate_supported_profiles.py | 3 ++- 26 files changed, 66 insertions(+), 89 deletions(-) diff --git a/enocean/communicators/__init__.py b/enocean/communicators/__init__.py index 7408bbe..65dbe44 100644 --- a/enocean/communicators/__init__.py +++ b/enocean/communicators/__init__.py @@ -1,4 +1,5 @@ ''' Provider for different Communicator -classes for EnOcean. ''' + from enocean.communicators.communicator import Communicator from enocean.communicators.serialcommunicator import SerialCommunicator from enocean.communicators.tcpcommunicator import TCPCommunicator diff --git a/enocean/communicators/communicator.py b/enocean/communicators/communicator.py index 1c6e219..a73b04e 100644 --- a/enocean/communicators/communicator.py +++ b/enocean/communicators/communicator.py @@ -1,15 +1,12 @@ # -*- encoding: utf-8 -*- -from __future__ import print_function, unicode_literals, division, absolute_import -import logging -import datetime +import datetime +import logging +import queue import threading -try: - import queue -except ImportError: - import Queue as queue -from enocean.protocol.packet import Packet, UTETeachInPacket + from enocean.protocol.constants import COMMON_COMMAND, PACKET, PARSE_RESULT, RETURN_CODE +from enocean.protocol.packet import Packet, UTETeachInPacket class Communicator(threading.Thread): diff --git a/enocean/communicators/serialcommunicator.py b/enocean/communicators/serialcommunicator.py index ffe4201..6f32518 100644 --- a/enocean/communicators/serialcommunicator.py +++ b/enocean/communicators/serialcommunicator.py @@ -1,9 +1,10 @@ # -*- encoding: utf-8 -*- -from __future__ import print_function, unicode_literals, division, absolute_import + import logging -import serial import time +import serial + from enocean.communicators.communicator import Communicator diff --git a/enocean/communicators/tcpcommunicator.py b/enocean/communicators/tcpcommunicator.py index 239012d..a847977 100644 --- a/enocean/communicators/tcpcommunicator.py +++ b/enocean/communicators/tcpcommunicator.py @@ -1,5 +1,5 @@ # -*- encoding: utf-8 -*- -from __future__ import print_function, unicode_literals, division, absolute_import + import logging import socket diff --git a/enocean/communicators/tests/test_communicator.py b/enocean/communicators/tests/test_communicator.py index c4d1d54..98082b8 100644 --- a/enocean/communicators/tests/test_communicator.py +++ b/enocean/communicators/tests/test_communicator.py @@ -1,10 +1,9 @@ # -*- encoding: utf-8 -*- -from __future__ import print_function, unicode_literals, division, absolute_import from enocean.communicators.communicator import Communicator -from enocean.protocol.packet import Packet, RadioPacket -from enocean.protocol.constants import PACKET from enocean.decorators import timing +from enocean.protocol.constants import PACKET +from enocean.protocol.packet import Packet, RadioPacket @timing(1000) diff --git a/enocean/communicators/utils.py b/enocean/communicators/utils.py index 646841d..604aaad 100644 --- a/enocean/communicators/utils.py +++ b/enocean/communicators/utils.py @@ -1,5 +1,5 @@ # -*- encoding: utf-8 -*- -from __future__ import print_function, unicode_literals, division, absolute_import + import socket diff --git a/enocean/consolelogger.py b/enocean/consolelogger.py index de3e01d..0557c15 100644 --- a/enocean/consolelogger.py +++ b/enocean/consolelogger.py @@ -1,5 +1,5 @@ # -*- encoding: utf-8 -*- -from __future__ import print_function, unicode_literals, division, absolute_import + import logging import logging.handlers diff --git a/enocean/decorators.py b/enocean/decorators.py index e953709..f03ff95 100644 --- a/enocean/decorators.py +++ b/enocean/decorators.py @@ -1,7 +1,7 @@ # -*- encoding: utf-8 -*- -from __future__ import print_function, unicode_literals, division -import time + import functools +import time from os import environ diff --git a/enocean/protocol/constants.py b/enocean/protocol/constants.py index 34ee952..584cb28 100644 --- a/enocean/protocol/constants.py +++ b/enocean/protocol/constants.py @@ -1,5 +1,5 @@ # -*- encoding: utf-8 -*- -from __future__ import print_function, unicode_literals, division, absolute_import + from enum import IntEnum diff --git a/enocean/protocol/crc8.py b/enocean/protocol/crc8.py index 5dd9b9d..c4a061b 100644 --- a/enocean/protocol/crc8.py +++ b/enocean/protocol/crc8.py @@ -1,5 +1,4 @@ # -*- encoding: utf-8 -*- -from __future__ import print_function, unicode_literals, division, absolute_import # https://gist.github.com/hypebeast/3833758 CRC_TABLE = ( diff --git a/enocean/protocol/eep.py b/enocean/protocol/eep.py index 2f54dc2..36a8db1 100644 --- a/enocean/protocol/eep.py +++ b/enocean/protocol/eep.py @@ -1,9 +1,9 @@ # -*- encoding: utf-8 -*- -from __future__ import print_function, unicode_literals, division, absolute_import -import os + import logging -from sys import version_info +import os from collections import OrderedDict + from bs4 import BeautifulSoup import enocean.utils diff --git a/enocean/protocol/packet.py b/enocean/protocol/packet.py index 14fdc34..1b10765 100644 --- a/enocean/protocol/packet.py +++ b/enocean/protocol/packet.py @@ -1,12 +1,12 @@ # -*- encoding: utf-8 -*- -from __future__ import print_function, unicode_literals, division, absolute_import + import logging from collections import OrderedDict import enocean.utils from enocean.protocol import crc8 -from enocean.protocol.eep import EEP from enocean.protocol.constants import PACKET, RORG, PARSE_RESULT, DB0, DB2, DB3, DB4, DB6 +from enocean.protocol.eep import EEP class Packet(object): diff --git a/enocean/protocol/tests/test_eep.py b/enocean/protocol/tests/test_eep.py index acdecdf..e810731 100644 --- a/enocean/protocol/tests/test_eep.py +++ b/enocean/protocol/tests/test_eep.py @@ -1,10 +1,9 @@ # -*- encoding: utf-8 -*- -from __future__ import print_function, unicode_literals, division, absolute_import -from enocean.protocol.packet import Packet -from enocean.protocol.eep import EEP -from enocean.protocol.constants import RORG from enocean.decorators import timing +from enocean.protocol.constants import RORG +from enocean.protocol.eep import EEP +from enocean.protocol.packet import Packet @timing(1000) diff --git a/enocean/protocol/tests/test_packet.py b/enocean/protocol/tests/test_packet.py index 96fa4c3..ad3b19f 100644 --- a/enocean/protocol/tests/test_packet.py +++ b/enocean/protocol/tests/test_packet.py @@ -1,9 +1,8 @@ # -*- encoding: utf-8 -*- -from __future__ import print_function, unicode_literals, division, absolute_import -from enocean.protocol.packet import Packet, EventPacket -from enocean.protocol.constants import PACKET, PARSE_RESULT, EVENT_CODE from enocean.decorators import timing +from enocean.protocol.constants import PACKET, PARSE_RESULT, EVENT_CODE +from enocean.protocol.packet import Packet, EventPacket @timing(1000) diff --git a/enocean/protocol/tests/test_packet_creation.py b/enocean/protocol/tests/test_packet_creation.py index 03a6a8b..1a5eff9 100644 --- a/enocean/protocol/tests/test_packet_creation.py +++ b/enocean/protocol/tests/test_packet_creation.py @@ -1,10 +1,10 @@ # -*- encoding: utf-8 -*- -from __future__ import print_function, unicode_literals, division, absolute_import + from nose.tools import raises -from enocean.protocol.packet import Packet, RadioPacket -from enocean.protocol.constants import PACKET, RORG from enocean.decorators import timing +from enocean.protocol.constants import PACKET, RORG +from enocean.protocol.packet import Packet, RadioPacket @timing(1000) diff --git a/enocean/protocol/tests/test_teachin.py b/enocean/protocol/tests/test_teachin.py index d908a94..ddef3e0 100644 --- a/enocean/protocol/tests/test_teachin.py +++ b/enocean/protocol/tests/test_teachin.py @@ -1,10 +1,9 @@ # -*- encoding: utf-8 -*- -from __future__ import print_function, unicode_literals, division, absolute_import from enocean.communicators import Communicator -from enocean.protocol.packet import Packet -from enocean.protocol.constants import RORG, DB6 from enocean.decorators import timing +from enocean.protocol.constants import RORG, DB6 +from enocean.protocol.packet import Packet @timing(rounds=100, limit=750) diff --git a/enocean/protocol/tests/test_temperature_sensors.py b/enocean/protocol/tests/test_temperature_sensors.py index b5bba06..9ae2919 100644 --- a/enocean/protocol/tests/test_temperature_sensors.py +++ b/enocean/protocol/tests/test_temperature_sensors.py @@ -1,7 +1,7 @@ # -*- encoding: utf-8 -*- -from __future__ import print_function, unicode_literals, division, absolute_import from enocean.protocol.eep import EEP + eep = EEP() # profiles = eep. diff --git a/enocean/tests/test_utils.py b/enocean/tests/test_utils.py index 9112d7d..f3ac1ed 100644 --- a/enocean/tests/test_utils.py +++ b/enocean/tests/test_utils.py @@ -1,5 +1,5 @@ # -*- encoding: utf-8 -*- -from __future__ import print_function, unicode_literals, division, absolute_import + import enocean.utils diff --git a/enocean/utils.py b/enocean/utils.py index 528b5af..e704034 100644 --- a/enocean/utils.py +++ b/enocean/utils.py @@ -1,6 +1,4 @@ # -*- encoding: utf-8 -*- -from __future__ import print_function, unicode_literals, division, absolute_import - def get_bit(byte, bit): ''' Get bit value from byte ''' diff --git a/examples/co_rd_version_example.py b/examples/co_rd_version_example.py index 7ff2d42..68014d5 100755 --- a/examples/co_rd_version_example.py +++ b/examples/co_rd_version_example.py @@ -10,18 +10,15 @@ in the ESP3 document. """ -from enocean.consolelogger import init_logging -from enocean.communicators.serialcommunicator import SerialCommunicator -from enocean.protocol.packet import Packet -from enocean.protocol.constants import PACKET -from enocean import utils -import traceback +import queue import sys +import traceback -try: - import queue -except ImportError: - import Queue as queue +from enocean import utils +from enocean.communicators.serialcommunicator import SerialCommunicator +from enocean.consolelogger import init_logging +from enocean.protocol.constants import PACKET +from enocean.protocol.packet import Packet init_logging() """ diff --git a/examples/enocean_example.py b/examples/enocean_example.py index be9eb0c..afebf9b 100755 --- a/examples/enocean_example.py +++ b/examples/enocean_example.py @@ -1,17 +1,15 @@ #!/usr/bin/env python # -*- encoding: utf-8 -*- -from enocean.consolelogger import init_logging -import enocean.utils -from enocean.communicators.serialcommunicator import SerialCommunicator -from enocean.protocol.packet import RadioPacket -from enocean.protocol.constants import PACKET, RORG + +import queue import sys import traceback -try: - import queue -except ImportError: - import Queue as queue +import enocean.utils +from enocean.communicators.serialcommunicator import SerialCommunicator +from enocean.consolelogger import init_logging +from enocean.protocol.constants import PACKET, RORG +from enocean.protocol.packet import RadioPacket def assemble_radio_packet(transmitter_id): diff --git a/examples/example_D2-05-00.py b/examples/example_D2-05-00.py index 20f655d..5b87593 100644 --- a/examples/example_D2-05-00.py +++ b/examples/example_D2-05-00.py @@ -7,18 +7,14 @@ Waits for UTE Teach-ins, sends the response automatically and prints the ID of new device. ''' +import queue import sys -import time import traceback + import enocean.utils from enocean.communicators import SerialCommunicator -from enocean.protocol.packet import RadioPacket, UTETeachInPacket from enocean.protocol.constants import RORG - -try: - import queue -except ImportError: - import Queue as queue +from enocean.protocol.packet import RadioPacket, UTETeachInPacket def set_position(destination, percentage): diff --git a/examples/example_DO21-11B-E.py b/examples/example_DO21-11B-E.py index 52464ec..0015532 100644 --- a/examples/example_DO21-11B-E.py +++ b/examples/example_DO21-11B-E.py @@ -7,18 +7,15 @@ Waits for UTE Teach-ins, sends the response automatically and prints the ID of new device. ''' +import queue import sys import time import traceback + import enocean.utils from enocean.communicators import SerialCommunicator -from enocean.protocol.packet import RadioPacket, UTETeachInPacket from enocean.protocol.constants import RORG - -try: - import queue -except ImportError: - import Queue as queue +from enocean.protocol.packet import RadioPacket, UTETeachInPacket def send_command(destination, output_value): diff --git a/examples/serial_to_tcp.py b/examples/serial_to_tcp.py index e68d19c..899fda6 100755 --- a/examples/serial_to_tcp.py +++ b/examples/serial_to_tcp.py @@ -1,15 +1,13 @@ #!/usr/bin/env python # -*- encoding: utf-8 -*- -from enocean.consolelogger import init_logging -from enocean.communicators.serialcommunicator import SerialCommunicator -from enocean.communicators.utils import send_to_tcp_socket + +import queue import sys import traceback -try: - import queue -except ImportError: - import Queue as queue +from enocean.communicators.serialcommunicator import SerialCommunicator +from enocean.communicators.utils import send_to_tcp_socket +from enocean.consolelogger import init_logging init_logging() communicator = SerialCommunicator() diff --git a/examples/tcp_server.py b/examples/tcp_server.py index d7fa94d..5d6b2e4 100755 --- a/examples/tcp_server.py +++ b/examples/tcp_server.py @@ -1,15 +1,13 @@ #!/usr/bin/env python # -*- encoding: utf-8 -*- -from enocean.consolelogger import init_logging -from enocean.communicators.tcpcommunicator import TCPCommunicator -from enocean.protocol.constants import PACKET, RORG + +import queue import sys import traceback -try: - import queue -except ImportError: - import Queue as queue +from enocean.communicators.tcpcommunicator import TCPCommunicator +from enocean.consolelogger import init_logging +from enocean.protocol.constants import PACKET, RORG init_logging() communicator = TCPCommunicator() diff --git a/generate_supported_profiles.py b/generate_supported_profiles.py index 30ee887..b6d0bff 100755 --- a/generate_supported_profiles.py +++ b/generate_supported_profiles.py @@ -1,7 +1,8 @@ #!/usr/bin/env python # -*- encoding: utf-8 -*- -from __future__ import print_function, unicode_literals, division, absolute_import + import codecs + from enocean.protocol.eep import EEP ROW_FORMAT = '|{:8s}|{:50s}|{:8s}|{:70s}|\n' From 4abd456ec061d3d456dc46f823f0fd95961026e1 Mon Sep 17 00:00:00 2001 From: topic2k Date: Sat, 31 Aug 2024 11:57:10 +0200 Subject: [PATCH 14/40] Update setup.py Update python-publish.yml Update python-package.yml trying to get pypi upload working logger.warn -> logger.warning fix testing --- .github/workflows/python-package.yml | 14 +++---- .github/workflows/python-publish.yml | 39 ++++++++++--------- enocean/protocol/eep.py | 10 ++--- .../protocol/tests/test_packet_creation.py | 17 ++++---- requirements.txt | 2 +- run_tests_with_timing.sh | 2 +- setup.py | 16 ++++---- 7 files changed, 53 insertions(+), 47 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 892f484..5acb8a1 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -5,9 +5,9 @@ name: Python package on: push: - branches: [ master ] + branches: [ topix ] pull_request: - branches: [ master ] + branches: [ topix ] jobs: test: @@ -15,18 +15,18 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ['3.5', '3.6', '3.7', '3.8', '3.9'] + python-version: ['3.11', '3.12', '3.13.0-rc.1'] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip - python -m pip install flake8 nose coverage + python -m pip install flake8 nose2 coverage if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - name: Lint with flake8 run: | @@ -36,7 +36,7 @@ jobs: flake8 enocean --count --exit-zero --max-complexity=15 --max-line-length=127 --statistics - name: Test with nose run: | - nosetests -s -q --with-coverage --cover-package=enocean + nose2 -s . --quiet --log-level 100 --with-coverage - name: Coveralls uses: AndreMiras/coveralls-python-action@develop with: diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 4e1ef42..6933f34 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -8,24 +8,25 @@ on: types: [created] jobs: - deploy: - + testpypi-publish: + name: Upload release to PyPI runs-on: ubuntu-latest - + environment: + name: testpypi + url: https://test.pypi.org/p/enocean4ha + permissions: + id-token: write # IMPORTANT: this permission is mandatory for trusted publishing steps: - - uses: actions/checkout@v2 - - name: Set up Python - uses: actions/setup-python@v2 - with: - python-version: '3.x' - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install setuptools wheel twine - - name: Build and publish - env: - TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} - TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} - run: | - python setup.py sdist bdist_wheel - twine upload dist/* + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install setuptools wheel twine + - name: Build + run: | + python setup.py sdist bdist_wheel + - name: Publish package distributions to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + repository-url: https://test.pypi.org/legacy/ diff --git a/enocean/protocol/eep.py b/enocean/protocol/eep.py index 36a8db1..41ad843 100644 --- a/enocean/protocol/eep.py +++ b/enocean/protocol/eep.py @@ -28,7 +28,7 @@ def __init__(self): # Impossible to test with the current structure? # To be honest, as the XML is included with the library, # there should be no possibility of ever reaching this... - self.logger.warn('Cannot load protocol file!') + self.logger.warning('Cannot load protocol file!') self.init_ok = False def __load_xml(self): @@ -153,19 +153,19 @@ def _set_boolean(target, data, bitarray): def find_profile(self, bitarray, eep_rorg, rorg_func, rorg_type, direction=None, command=None): ''' Find profile and data description, matching RORG, FUNC and TYPE ''' if not self.init_ok: - self.logger.warn('EEP.xml not loaded!') + self.logger.warning('EEP.xml not loaded!') return None if eep_rorg not in self.telegrams.keys(): - self.logger.warn('Cannot find rorg %s in EEP!', hex(eep_rorg)) + self.logger.warning('Cannot find rorg %s in EEP!', hex(eep_rorg)) return None if rorg_func not in self.telegrams[eep_rorg].keys(): - self.logger.warn('Cannot find rorg %s func %s in EEP!', hex(eep_rorg), hex(rorg_func)) + self.logger.warning('Cannot find rorg %s func %s in EEP!', hex(eep_rorg), hex(rorg_func)) return None if rorg_type not in self.telegrams[eep_rorg][rorg_func].keys(): - self.logger.warn('Cannot find rorg %s func %s type %s in EEP!', hex(eep_rorg), hex(rorg_func), hex(rorg_type)) + self.logger.warning('Cannot find rorg %s func %s type %s in EEP!', hex(eep_rorg), hex(rorg_func), hex(rorg_type)) return None profile = self.telegrams[eep_rorg][rorg_func][rorg_type] diff --git a/enocean/protocol/tests/test_packet_creation.py b/enocean/protocol/tests/test_packet_creation.py index 1a5eff9..2eed69e 100644 --- a/enocean/protocol/tests/test_packet_creation.py +++ b/enocean/protocol/tests/test_packet_creation.py @@ -1,6 +1,7 @@ # -*- encoding: utf-8 -*- -from nose.tools import raises + +from unittest.case import TestCase from enocean.decorators import timing from enocean.protocol.constants import PACKET, RORG @@ -242,15 +243,17 @@ def test_switch(): @timing(1000) -@raises(ValueError) -def test_illegal_eep_enum1(): - RadioPacket.create(rorg=RORG.RPS, rorg_func=0x02, rorg_type=0x02, sender=[0x00, 0x29, 0x89, 0x79], EB='inexisting') +class TestIllegalEEPEnum1(TestCase): + def test_illegal_eep_enum1(self): + with self.assertRaises(ValueError): + RadioPacket.create(rorg=RORG.RPS, rorg_func=0x02, rorg_type=0x02, sender=[0x00, 0x29, 0x89, 0x79], EB='inexisting') -@raises(ValueError) @timing(1000) -def test_illegal_eep_enum2(): - RadioPacket.create(rorg=RORG.RPS, rorg_func=0x02, rorg_type=0x02, sender=[0x00, 0x29, 0x89, 0x79], EB=2) +class TestIllegalEEPEnum2(TestCase): + def test_illegal_eep_enum2(self): + with self.assertRaises(ValueError): + RadioPacket.create(rorg=RORG.RPS, rorg_func=0x02, rorg_type=0x02, sender=[0x00, 0x29, 0x89, 0x79], EB=2) # Corresponds to the tests done in test_eep diff --git a/requirements.txt b/requirements.txt index 28d974f..a8256ee 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ enum-compat>=0.0.3 pyserial>=3.5 beautifulsoup4>=4.12.3 -lxml>=5.3.0 \ No newline at end of file +lxml>=5.3.0 diff --git a/run_tests_with_timing.sh b/run_tests_with_timing.sh index 4cd310e..72f6562 100755 --- a/run_tests_with_timing.sh +++ b/run_tests_with_timing.sh @@ -1,2 +1,2 @@ #!/bin/sh -WITH_TIMINGS=1 nosetests -s -q +WITH_TIMINGS=1 python -m nose2 -s . --quiet --log-level 100 --with-coverage diff --git a/setup.py b/setup.py index 7befb2f..7243592 100644 --- a/setup.py +++ b/setup.py @@ -1,16 +1,18 @@ #!/usr/bin/env python -try: - from setuptools import setup -except ImportError: - from distutils.core import setup + +from setuptools import setup setup( - name='enocean', - version='0.60.1', + name='enocean4ha', + version='1.0.6', description='EnOcean serial protocol implementation', + long_description='A Python library for reading and controlling EnOcean devices.', + long_description_content_type="text/plain", author='Kimmo Huoman', author_email='kipenroskaposti@gmail.com', - url='https://github.com/kipe/enocean', + maintainer='topic2k', + maintainer_email='topic2k@atlogger.de', + url='https://github.com/topic2k/enocean4ha', packages=[ 'enocean', 'enocean.protocol', From 469226c44cbcd7886135e7d21f2789f3da03b88b Mon Sep 17 00:00:00 2001 From: topic2k Date: Sat, 31 Aug 2024 20:32:05 +0200 Subject: [PATCH 15/40] setup.py -> pyproject.toml restructure the folder a little bit use relative imports a few code changes --- .github/workflows/python-package.yml | 4 +- README.md | 2 +- enocean/decorators.py => decorators.py | 0 enocean/__init__.py | 3 ++ enocean/communicators/__init__.py | 6 +-- enocean/communicators/communicator.py | 4 +- enocean/communicators/serialcommunicator.py | 2 +- enocean/communicators/tcpcommunicator.py | 2 +- enocean/protocol/eep.py | 23 ++++----- enocean/protocol/packet.py | 48 +++++++++---------- enocean/tests/test_utils.py | 30 ------------ pyproject.toml | 46 ++++++++++++++++++ requirements.txt | 4 -- run_tests_with_timing.sh | 2 - setup.py | 32 ------------- tests/__init__.py | 0 .../tests => tests}/test_communicator.py | 2 +- .../test_eep.py => tests/test_protocol_eep.py | 2 +- .../test_protocol_packet.py | 2 +- .../test_protocol_packet_creation.py | 3 +- .../test_protocol_teachin.py | 2 +- .../test_protocol_temperature_sensors.py | 0 tests/test_utils.py | 30 ++++++++++++ 23 files changed, 128 insertions(+), 121 deletions(-) rename enocean/decorators.py => decorators.py (100%) delete mode 100644 enocean/tests/test_utils.py create mode 100644 pyproject.toml delete mode 100644 requirements.txt delete mode 100755 run_tests_with_timing.sh delete mode 100644 setup.py create mode 100644 tests/__init__.py rename {enocean/communicators/tests => tests}/test_communicator.py (98%) rename enocean/protocol/tests/test_eep.py => tests/test_protocol_eep.py (99%) rename enocean/protocol/tests/test_packet.py => tests/test_protocol_packet.py (99%) rename enocean/protocol/tests/test_packet_creation.py => tests/test_protocol_packet_creation.py (99%) rename enocean/protocol/tests/test_teachin.py => tests/test_protocol_teachin.py (97%) rename enocean/protocol/tests/test_temperature_sensors.py => tests/test_protocol_temperature_sensors.py (100%) create mode 100644 tests/test_utils.py diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 5acb8a1..22c9690 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ['3.11', '3.12', '3.13.0-rc.1'] + python-version: ['3.9', '3.10','3.11', '3.12', '3.13.0-rc.1'] steps: - uses: actions/checkout@v4 @@ -36,7 +36,7 @@ jobs: flake8 enocean --count --exit-zero --max-complexity=15 --max-line-length=127 --statistics - name: Test with nose run: | - nose2 -s . --quiet --log-level 100 --with-coverage + python -m nose2 -s . --quiet --log-level 100 --with-coverage - name: Coveralls uses: AndreMiras/coveralls-python-action@develop with: diff --git a/README.md b/README.md index 0395414..0e0f4d2 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ If not installed already, install [pip](https://pypi.python.org/pypi/pip) by run After pip is installed, install the module by running -`sudo pip install enocean` (or `sudo pip install git+https://github.com/kipe/enocean.git` if you want the "bleeding edge"). +`sudo pip install enocean4ha` (or `sudo pip install git+https://github.com/topic2k/enocean4ha.git` if you want the "bleeding edge"). After this, it's just a matter of running `enocean_example.py` and pressing the learn button on magnetic contact or temperature switch or pressing the rocker switch. diff --git a/enocean/decorators.py b/decorators.py similarity index 100% rename from enocean/decorators.py rename to decorators.py diff --git a/enocean/__init__.py b/enocean/__init__.py index e69de29..39f2314 100644 --- a/enocean/__init__.py +++ b/enocean/__init__.py @@ -0,0 +1,3 @@ +# -*- encoding: utf-8 -*- + +__version__ = '1.0.0' diff --git a/enocean/communicators/__init__.py b/enocean/communicators/__init__.py index 65dbe44..a0682e7 100644 --- a/enocean/communicators/__init__.py +++ b/enocean/communicators/__init__.py @@ -1,5 +1,5 @@ ''' Provider for different Communicator -classes for EnOcean. ''' -from enocean.communicators.communicator import Communicator -from enocean.communicators.serialcommunicator import SerialCommunicator -from enocean.communicators.tcpcommunicator import TCPCommunicator +from .communicator import Communicator +from .serialcommunicator import SerialCommunicator +from .tcpcommunicator import TCPCommunicator diff --git a/enocean/communicators/communicator.py b/enocean/communicators/communicator.py index a73b04e..3e5a16d 100644 --- a/enocean/communicators/communicator.py +++ b/enocean/communicators/communicator.py @@ -5,8 +5,8 @@ import queue import threading -from enocean.protocol.constants import COMMON_COMMAND, PACKET, PARSE_RESULT, RETURN_CODE -from enocean.protocol.packet import Packet, UTETeachInPacket +from ..protocol.constants import COMMON_COMMAND, PACKET, PARSE_RESULT, RETURN_CODE +from ..protocol.packet import Packet, UTETeachInPacket class Communicator(threading.Thread): diff --git a/enocean/communicators/serialcommunicator.py b/enocean/communicators/serialcommunicator.py index 6f32518..2954ce5 100644 --- a/enocean/communicators/serialcommunicator.py +++ b/enocean/communicators/serialcommunicator.py @@ -5,7 +5,7 @@ import serial -from enocean.communicators.communicator import Communicator +from .communicator import Communicator class SerialCommunicator(Communicator): diff --git a/enocean/communicators/tcpcommunicator.py b/enocean/communicators/tcpcommunicator.py index a847977..a8fbdf8 100644 --- a/enocean/communicators/tcpcommunicator.py +++ b/enocean/communicators/tcpcommunicator.py @@ -3,7 +3,7 @@ import logging import socket -from enocean.communicators.communicator import Communicator +from .communicator import Communicator class TCPCommunicator(Communicator): diff --git a/enocean/protocol/eep.py b/enocean/protocol/eep.py index 41ad843..7edfa95 100644 --- a/enocean/protocol/eep.py +++ b/enocean/protocol/eep.py @@ -1,14 +1,12 @@ # -*- encoding: utf-8 -*- import logging -import os from collections import OrderedDict +from importlib.resources import files from bs4 import BeautifulSoup -import enocean.utils -# Left as a helper -from enocean.protocol.constants import RORG # noqa: F401 +from .. import utils class EEP(object): @@ -18,11 +16,10 @@ def __init__(self): self.init_ok = False self.telegrams = {} - eep_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'EEP.xml') try: - with open(eep_path, 'r', encoding='UTF-8') as xml_file: - self.soup = BeautifulSoup(xml_file.read(), features="lxml-xml") # ,"html.parser") - self.__load_xml() + xml_content = files('enocean.protocol').joinpath('EEP.xml').read_text() + self.soup = BeautifulSoup(xml_content, features="lxml-xml") + self.__xml_to_dict() self.init_ok = True except IOError: # Impossible to test with the current structure? @@ -31,12 +28,12 @@ def __init__(self): self.logger.warning('Cannot load protocol file!') self.init_ok = False - def __load_xml(self): + def __xml_to_dict(self): self.telegrams = { - enocean.utils.from_hex_string(telegram['rorg']): { - enocean.utils.from_hex_string(function['func']): { - enocean.utils.from_hex_string(type['type'], ): type - for type in function.find_all('profile') + utils.from_hex_string(telegram['rorg']): { + utils.from_hex_string(function['func']): { + utils.from_hex_string(typ['type'], ): typ + for typ in function.find_all('profile') } for function in telegram.find_all('profiles') } diff --git a/enocean/protocol/packet.py b/enocean/protocol/packet.py index 1b10765..0aa0171 100644 --- a/enocean/protocol/packet.py +++ b/enocean/protocol/packet.py @@ -3,13 +3,13 @@ import logging from collections import OrderedDict -import enocean.utils -from enocean.protocol import crc8 -from enocean.protocol.constants import PACKET, RORG, PARSE_RESULT, DB0, DB2, DB3, DB4, DB6 -from enocean.protocol.eep import EEP +from .crc8 import calc +from .constants import PACKET, RORG, PARSE_RESULT, DB0, DB2, DB3, DB4, DB6 +from .eep import EEP +from ..utils import combine_hex, from_bitarray, to_hex_string, to_bitarray -class Packet(object): +class Packet: ''' Base class for Packet. Mainly used for for packet generation and @@ -69,13 +69,13 @@ def _bit_data(self): # Packet.data would then only have the actual, documented data-bytes. # Packet.message would contain the whole message. # See discussion in issue #14 - return enocean.utils.to_bitarray(self.data[1:len(self.data) - 5], (len(self.data) - 6) * 8) + return to_bitarray(self.data[1:len(self.data) - 5], (len(self.data) - 6) * 8) @_bit_data.setter def _bit_data(self, value): # The same as getting the data, first and last 5 bits are ommitted, as they are defined... for byte in range(len(self.data) - 6): - self.data[byte+1] = enocean.utils.from_bitarray(value[byte*8:(byte+1)*8]) + self.data[byte+1] = from_bitarray(value[byte * 8:(byte + 1) * 8]) # # COMMENTED OUT, AS NOTHING TOUCHES _bit_optional FOR NOW. # # Thus, this is also untested. @@ -93,11 +93,11 @@ def _bit_data(self, value): @property def _bit_status(self): - return enocean.utils.to_bitarray(self.status) + return to_bitarray(self.status) @_bit_status.setter def _bit_status(self, value): - self.status = enocean.utils.from_bitarray(value) + self.status = from_bitarray(value) @staticmethod def parse_msg(buf): @@ -137,12 +137,12 @@ def parse_msg(buf): opt_data = msg[6 + data_len:6 + data_len + opt_len] # Check CRCs for header and data - if msg[5] != crc8.calc(msg[1:5]): + if msg[5] != calc(msg[1:5]): # Fail if doesn't match message Packet.logger.error('Header CRC error!') # Return CRC_MISMATCH return PARSE_RESULT.CRC_MISMATCH, buf, None - if msg[6 + data_len + opt_len] != crc8.calc(msg[6:6 + data_len + opt_len]): + if msg[6 + data_len + opt_len] != calc(msg[6:6 + data_len + opt_len]): # Fail if doesn't match message Packet.logger.error('Data CRC error!') # Return CRC_MISMATCH @@ -256,7 +256,7 @@ def parse(self): if self.rorg in [RORG.RPS, RORG.BS1, RORG.BS4]: # These message types should have repeater count in the last for bits of status. - self.repeater_count = enocean.utils.from_bitarray(self._bit_status[4:]) + self.repeater_count = from_bitarray(self._bit_status[4:]) return self.parsed def select_eep(self, rorg_func, rorg_type, direction=None, command=None): @@ -285,10 +285,10 @@ def build(self): ''' Build Packet for sending to EnOcean controller ''' data_length = len(self.data) ords = [0x55, (data_length >> 8) & 0xFF, data_length & 0xFF, len(self.optional), int(self.packet_type)] - ords.append(crc8.calc(ords[1:5])) + ords.append(calc(ords[1:5])) ords.extend(self.data) ords.extend(self.optional) - ords.append(crc8.calc(ords[6:])) + ords.append(calc(ords[6:])) return ords @@ -311,19 +311,19 @@ def create(rorg, rorg_func, rorg_type, direction=None, command=None, @property def sender_int(self): - return enocean.utils.combine_hex(self.sender) + return combine_hex(self.sender) @property def sender_hex(self): - return enocean.utils.to_hex_string(self.sender) + return to_hex_string(self.sender) @property def destination_int(self): - return enocean.utils.combine_hex(self.destination) + return combine_hex(self.destination) @property def destination_hex(self): - return enocean.utils.to_hex_string(self.destination) + return to_hex_string(self.destination) def parse(self): self.destination = self.optional[1:5] @@ -343,9 +343,9 @@ def parse(self): self.contains_eep = self._bit_data[DB0.BIT_7] if self.contains_eep: # Get rorg_func and rorg_type from an unidirectional learn packet - self.rorg_func = enocean.utils.from_bitarray(self._bit_data[DB3.BIT_7:DB3.BIT_1]) - self.rorg_type = enocean.utils.from_bitarray(self._bit_data[DB3.BIT_1:DB2.BIT_2]) - self.rorg_manufacturer = enocean.utils.from_bitarray(self._bit_data[DB2.BIT_2:DB0.BIT_7]) + self.rorg_func = from_bitarray(self._bit_data[DB3.BIT_7:DB3.BIT_1]) + self.rorg_type = from_bitarray(self._bit_data[DB3.BIT_1:DB2.BIT_2]) + self.rorg_manufacturer = from_bitarray(self._bit_data[DB2.BIT_2:DB0.BIT_7]) self.logger.debug('learn received, EEP detected, RORG: 0x%02X, FUNC: 0x%02X, TYPE: 0x%02X, Manufacturer: 0x%02X' % (self.rorg, self.rorg_func, self.rorg_type, self.rorg_manufacturer)) # noqa: E501 return super(RadioPacket, self).parse() @@ -388,8 +388,8 @@ def parse(self): super(UTETeachInPacket, self).parse() self.unidirectional = not self._bit_data[DB6.BIT_7] self.response_expected = not self._bit_data[DB6.BIT_6] - self.request_type = enocean.utils.from_bitarray(self._bit_data[DB6.BIT_5:DB6.BIT_3]) - self.rorg_manufacturer = enocean.utils.from_bitarray(self._bit_data[DB3.BIT_2:DB2.BIT_7] + self._bit_data[DB4.BIT_7:DB3.BIT_7]) # noqa: E501 + self.request_type = from_bitarray(self._bit_data[DB6.BIT_5:DB6.BIT_3]) + self.rorg_manufacturer = from_bitarray(self._bit_data[DB3.BIT_2:DB2.BIT_7] + self._bit_data[DB4.BIT_7:DB3.BIT_7]) # noqa: E501 self.channel = self.data[2] self.rorg_type = self.data[5] self.rorg_func = self.data[6] @@ -405,7 +405,7 @@ def create_response_packet(self, sender_id, response=TEACHIN_ACCEPTED): # - Databytes 5 to 0 are copied from the original message # - Set sender id and status data = [self.rorg] + \ - [enocean.utils.from_bitarray([True, False] + response + [False, False, False, True])] + \ + [from_bitarray([True, False] + response + [False, False, False, True])] + \ self.data[2:8] + \ sender_id + [0] diff --git a/enocean/tests/test_utils.py b/enocean/tests/test_utils.py deleted file mode 100644 index f3ac1ed..0000000 --- a/enocean/tests/test_utils.py +++ /dev/null @@ -1,30 +0,0 @@ -# -*- encoding: utf-8 -*- - -import enocean.utils - - -def test_get_bit(): - assert enocean.utils.get_bit(1, 0) == 1 - assert enocean.utils.get_bit(8, 3) == 1 - assert enocean.utils.get_bit(6, 2) == 1 - assert enocean.utils.get_bit(6, 1) == 1 - - -def test_to_hex_string(): - assert enocean.utils.to_hex_string(0) == '00' - assert enocean.utils.to_hex_string(15) == '0F' - assert enocean.utils.to_hex_string(16) == '10' - assert enocean.utils.to_hex_string(22) == '16' - - assert enocean.utils.to_hex_string([0, 15, 16, 22]) == '00:0F:10:16' - assert enocean.utils.to_hex_string([0x00, 0x0F, 0x10, 0x16]) == '00:0F:10:16' - - -def test_from_hex_string(): - assert enocean.utils.from_hex_string('00') == 0 - assert enocean.utils.from_hex_string('0F') == 15 - assert enocean.utils.from_hex_string('10') == 16 - assert enocean.utils.from_hex_string('16') == 22 - - assert enocean.utils.from_hex_string('00:0F:10:16') == [0, 15, 16, 22] - assert enocean.utils.from_hex_string('00:0F:10:16') == [0x00, 0x0F, 0x10, 0x16] diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..0a53f73 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,46 @@ +[build-system] +requires = [ "setuptools>=74.0"] +build-backend = "setuptools.build_meta" + +[project] +name = "enocean4ha" +dynamic = [ "version", ] +authors = [ + { name="Kimmo Huoman", email="kipenroskaposti@gmail.com" }, +] +maintainers = [ + { name="topic2k", email="topic2k@atlogger.de" }, +] +description = "EnOcean serial protocol implementation" +keywords = [ "EnOcean", "HomeAssistant", ] +readme = "README.md" +requires-python = ">=3.9" +classifiers = [ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + "Intended Audience :: Developers", + "Topic :: Home Automation", +] +dependencies = [ + 'pyserial>=3.5', + 'beautifulsoup4>=4.12.3', + 'lxml>=5.3.0', +] + +[project.urls] +Homepage = "https://github.com/topic2k/enocean4ha" +Issues = "https://github.com/topic2k/enocean4ha/issues" + +[project.optional-dependencies] +tests = [ "nose2>=0.15.1", ] + +[tool.setuptools.dynamic] +version = { attr = "enocean.__version__" } + +[tool.setuptools.packages.find] +where = [ "." ] +include = [ "enocean*" ] + +[tool.setuptools.package-data] +"*" = [ "*.xml", ] diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index a8256ee..0000000 --- a/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ -enum-compat>=0.0.3 -pyserial>=3.5 -beautifulsoup4>=4.12.3 -lxml>=5.3.0 diff --git a/run_tests_with_timing.sh b/run_tests_with_timing.sh deleted file mode 100755 index 72f6562..0000000 --- a/run_tests_with_timing.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -WITH_TIMINGS=1 python -m nose2 -s . --quiet --log-level 100 --with-coverage diff --git a/setup.py b/setup.py deleted file mode 100644 index 7243592..0000000 --- a/setup.py +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env python - -from setuptools import setup - -setup( - name='enocean4ha', - version='1.0.6', - description='EnOcean serial protocol implementation', - long_description='A Python library for reading and controlling EnOcean devices.', - long_description_content_type="text/plain", - author='Kimmo Huoman', - author_email='kipenroskaposti@gmail.com', - maintainer='topic2k', - maintainer_email='topic2k@atlogger.de', - url='https://github.com/topic2k/enocean4ha', - packages=[ - 'enocean', - 'enocean.protocol', - 'enocean.communicators', - ], - scripts=[ - 'examples/enocean_example.py', - ], - package_data={ - '': ['EEP.xml'] - }, - install_requires=[ - 'enum-compat>=0.0.3', - 'pyserial>=3.5', - 'beautifulsoup4>=4.12.3', - 'lxml>=5.3.0', - ]) diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/enocean/communicators/tests/test_communicator.py b/tests/test_communicator.py similarity index 98% rename from enocean/communicators/tests/test_communicator.py rename to tests/test_communicator.py index 98082b8..05fe2a5 100644 --- a/enocean/communicators/tests/test_communicator.py +++ b/tests/test_communicator.py @@ -1,7 +1,7 @@ # -*- encoding: utf-8 -*- +from decorators import timing from enocean.communicators.communicator import Communicator -from enocean.decorators import timing from enocean.protocol.constants import PACKET from enocean.protocol.packet import Packet, RadioPacket diff --git a/enocean/protocol/tests/test_eep.py b/tests/test_protocol_eep.py similarity index 99% rename from enocean/protocol/tests/test_eep.py rename to tests/test_protocol_eep.py index e810731..f0f8644 100644 --- a/enocean/protocol/tests/test_eep.py +++ b/tests/test_protocol_eep.py @@ -1,6 +1,6 @@ # -*- encoding: utf-8 -*- -from enocean.decorators import timing +from decorators import timing from enocean.protocol.constants import RORG from enocean.protocol.eep import EEP from enocean.protocol.packet import Packet diff --git a/enocean/protocol/tests/test_packet.py b/tests/test_protocol_packet.py similarity index 99% rename from enocean/protocol/tests/test_packet.py rename to tests/test_protocol_packet.py index ad3b19f..43c5a1e 100644 --- a/enocean/protocol/tests/test_packet.py +++ b/tests/test_protocol_packet.py @@ -1,6 +1,6 @@ # -*- encoding: utf-8 -*- -from enocean.decorators import timing +from decorators import timing from enocean.protocol.constants import PACKET, PARSE_RESULT, EVENT_CODE from enocean.protocol.packet import Packet, EventPacket diff --git a/enocean/protocol/tests/test_packet_creation.py b/tests/test_protocol_packet_creation.py similarity index 99% rename from enocean/protocol/tests/test_packet_creation.py rename to tests/test_protocol_packet_creation.py index 2eed69e..a097bd1 100644 --- a/enocean/protocol/tests/test_packet_creation.py +++ b/tests/test_protocol_packet_creation.py @@ -1,9 +1,8 @@ # -*- encoding: utf-8 -*- - from unittest.case import TestCase -from enocean.decorators import timing +from decorators import timing from enocean.protocol.constants import PACKET, RORG from enocean.protocol.packet import Packet, RadioPacket diff --git a/enocean/protocol/tests/test_teachin.py b/tests/test_protocol_teachin.py similarity index 97% rename from enocean/protocol/tests/test_teachin.py rename to tests/test_protocol_teachin.py index ddef3e0..b197645 100644 --- a/enocean/protocol/tests/test_teachin.py +++ b/tests/test_protocol_teachin.py @@ -1,7 +1,7 @@ # -*- encoding: utf-8 -*- +from decorators import timing from enocean.communicators import Communicator -from enocean.decorators import timing from enocean.protocol.constants import RORG, DB6 from enocean.protocol.packet import Packet diff --git a/enocean/protocol/tests/test_temperature_sensors.py b/tests/test_protocol_temperature_sensors.py similarity index 100% rename from enocean/protocol/tests/test_temperature_sensors.py rename to tests/test_protocol_temperature_sensors.py diff --git a/tests/test_utils.py b/tests/test_utils.py new file mode 100644 index 0000000..f49f531 --- /dev/null +++ b/tests/test_utils.py @@ -0,0 +1,30 @@ +# -*- encoding: utf-8 -*- + +from enocean.utils import from_hex_string, to_hex_string, get_bit + + +def test_get_bit(): + assert get_bit(1, 0) == 1 + assert get_bit(8, 3) == 1 + assert get_bit(6, 2) == 1 + assert get_bit(6, 1) == 1 + + +def test_to_hex_string(): + assert to_hex_string(0) == '00' + assert to_hex_string(15) == '0F' + assert to_hex_string(16) == '10' + assert to_hex_string(22) == '16' + + assert to_hex_string([0, 15, 16, 22]) == '00:0F:10:16' + assert to_hex_string([0x00, 0x0F, 0x10, 0x16]) == '00:0F:10:16' + + +def test_from_hex_string(): + assert from_hex_string('00') == 0 + assert from_hex_string('0F') == 15 + assert from_hex_string('10') == 16 + assert from_hex_string('16') == 22 + + assert from_hex_string('00:0F:10:16') == [0, 15, 16, 22] + assert from_hex_string('00:0F:10:16') == [0x00, 0x0F, 0x10, 0x16] From 61ec71aa18db57ee95c02e6e93d6893162edd04c Mon Sep 17 00:00:00 2001 From: topic2k Date: Sat, 31 Aug 2024 20:42:56 +0200 Subject: [PATCH 16/40] setup.py -> pyproject.toml restructure the folder a little bit use relative imports a few code changes --- .github/workflows/{python-package.yml => lint_and_test.yml} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename .github/workflows/{python-package.yml => lint_and_test.yml} (95%) diff --git a/.github/workflows/python-package.yml b/.github/workflows/lint_and_test.yml similarity index 95% rename from .github/workflows/python-package.yml rename to .github/workflows/lint_and_test.yml index 22c9690..8654386 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/lint_and_test.yml @@ -27,7 +27,7 @@ jobs: run: | python -m pip install --upgrade pip python -m pip install flake8 nose2 coverage - if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + python -m pip install . - name: Lint with flake8 run: | # stop the test if there are Python syntax errors or undefined names From 616e51dd27754636e603672dc91074d1833ae3b7 Mon Sep 17 00:00:00 2001 From: topic2k Date: Sat, 31 Aug 2024 20:56:10 +0200 Subject: [PATCH 17/40] fix publish action --- .github/workflows/python-publish.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 6933f34..d4a188f 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -22,10 +22,10 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install setuptools wheel twine + python -m pip install setuptools wheel twine - name: Build run: | - python setup.py sdist bdist_wheel + python -m build - name: Publish package distributions to PyPI uses: pypa/gh-action-pypi-publish@release/v1 with: From b07033d0c92edc2662aaa59324e27cdf56c47e85 Mon Sep 17 00:00:00 2001 From: topic2k Date: Sat, 31 Aug 2024 21:00:56 +0200 Subject: [PATCH 18/40] fix publish action (2nd try) --- pyproject.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 0a53f73..c909391 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = [ "setuptools>=74.0"] +requires = [ "setuptools>=74.0", "build>=1.2"] build-backend = "setuptools.build_meta" [project] @@ -24,8 +24,8 @@ classifiers = [ ] dependencies = [ 'pyserial>=3.5', - 'beautifulsoup4>=4.12.3', - 'lxml>=5.3.0', + 'beautifulsoup4>=4.12', + 'lxml>=5.3', ] [project.urls] From 37862cce011e8f4109d4a22b39afc8cba8ad23a4 Mon Sep 17 00:00:00 2001 From: topic2k Date: Sat, 31 Aug 2024 21:04:38 +0200 Subject: [PATCH 19/40] fix publish action (3rd try) --- .github/workflows/python-publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index d4a188f..cab7d30 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -22,7 +22,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - python -m pip install setuptools wheel twine + python -m pip install setuptools wheel twine build - name: Build run: | python -m build From 86a0c7648ba00924ba9a06a29c0778aedf3eeafa Mon Sep 17 00:00:00 2001 From: topic2k Date: Sat, 31 Aug 2024 21:12:06 +0200 Subject: [PATCH 20/40] version test --- enocean/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/enocean/__init__.py b/enocean/__init__.py index 39f2314..adaf3ab 100644 --- a/enocean/__init__.py +++ b/enocean/__init__.py @@ -1,3 +1,3 @@ # -*- encoding: utf-8 -*- -__version__ = '1.0.0' +__version__ = '1.0.1' From 414eff0a42090eae385dffc605380c2e0c55b715 Mon Sep 17 00:00:00 2001 From: topic2k Date: Sat, 31 Aug 2024 21:12:32 +0200 Subject: [PATCH 21/40] version test --- enocean/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/enocean/__init__.py b/enocean/__init__.py index adaf3ab..e23d576 100644 --- a/enocean/__init__.py +++ b/enocean/__init__.py @@ -1,3 +1,3 @@ # -*- encoding: utf-8 -*- -__version__ = '1.0.1' +__version__ = '1.1.1' From 9bb87a8fe9140f41203149759af80a87cb02213e Mon Sep 17 00:00:00 2001 From: topic2k Date: Sun, 1 Sep 2024 11:06:20 +0200 Subject: [PATCH 22/40] small changes to README.md and SUPPORTED_PROFILES.md --- .github/workflows/lint_and_test.yml | 2 +- README.md | 16 ++- SUPPORTED_PROFILES.md | 146 +++++++++++++++++----------- enocean/protocol/eep.py | 2 +- generate_supported_profiles.py | 52 +++++++--- pyproject.toml | 4 +- 6 files changed, 141 insertions(+), 81 deletions(-) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index 8654386..eb7e66d 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -1,7 +1,7 @@ # This workflow will install Python dependencies, run tests and lint with a variety of Python versions # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions -name: Python package +name: Linting and testing on: push: diff --git a/README.md b/README.md index 0e0f4d2..27ecc9b 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,20 @@ # Python EnOcean # -[![Build Status](https://travis-ci.org/kipe/enocean.svg?branch=master)](https://travis-ci.org/kipe/enocean) -[![Coverage Status](https://coveralls.io/repos/github/kipe/enocean/badge.svg?branch=master)](https://coveralls.io/github/kipe/enocean?branch=master) +[![Linting And Testing Status](https://github.com/topic2k/enocean4ha/actions/workflows/lint_and_test.yml/badge.svg?branch=topix)](https://github.com/topic2k/enocean4ha/actions/workflows/lint_and_test.yml) +[![Coverage Status](https://coveralls.io/repos/github/topic2k/enocean4ha/badge.svg?branch=topix)](https://coveralls.io/github/topic2k/enocean4ha?branch=topix) +[![PyPi](https://img.shields.io/pypi/v/wxpython?logo=pypi&logoColor=959DA5)](https://test.pypi.org/project/enocean4ha/) + A Python library for reading and controlling [EnOcean](http://www.enocean.com/) devices. -Started as a part of [Forget Me Not](http://www.element14.com/community/community/design-challenges/forget-me-not) +It started as a part of the [Forget Me Not](http://www.element14.com/community/community/design-challenges/forget-me-not) design challenge @ [element14](http://www.element14.com/). +This fork was created, because the [original repo](https://github.com/kipe/enocean) seems to be inactive, and i needed +to add EEPs to make some devices usable in [Home Assistant](https://www.home-assistant.io/). + + + ## Install ## If not installed already, install [pip](https://pypi.python.org/pypi/pip) by running @@ -25,3 +32,6 @@ You should be displayed with a log of the presses, as well as parsed values (assuming the sensors are the ones provided in the [EnOcean Starter Kit](https://www.enocean.com/en/enocean_modules/esk-300)). The example script can be stopped by pressing `CTRL+C` + +--- + diff --git a/SUPPORTED_PROFILES.md b/SUPPORTED_PROFILES.md index f73a5a7..5b73b58 100644 --- a/SUPPORTED_PROFILES.md +++ b/SUPPORTED_PROFILES.md @@ -1,7 +1,90 @@ # Supported profiles All profiles (should) correspond to the official [EEP](http://www.enocean-alliance.org/eep/) by EnOcean. +
RPS Telegram (0xF6) + +- [FUNC 0x01 - TYPE 0x01 - Push Button](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xF6---func-0x01---type-0x01---push-button) +- [FUNC 0x02 - TYPE 0x01 - Light and Blind Control - Application Style 1](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xF6---func-0x02---type-0x01---light-and-blind-control---application-style-1) +- [FUNC 0x02 - TYPE 0x02 - Light and Blind Control - Application Style 2](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xF6---func-0x02---type-0x02---light-and-blind-control---application-style-2) +- [FUNC 0x05 - TYPE 0x01 - Liquid Leakage Sensor (mechanic harvester)](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xF6---func-0x05---type-0x01---liquid-leakage-sensor-(mechanic-harvester)) +- [FUNC 0x05 - TYPE 0x02 - Smoke Detector](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xF6---func-0x05---type-0x02---smoke-detector) +- [FUNC 0x10 - TYPE 0x00 - Window Handle](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xF6---func-0x10---type-0x00---window-handle) + +
+ +
1BS Telegram (0xD5) + +- [FUNC 0x00 - TYPE 0x01 - Single Input Contact](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xD5---func-0x00---type-0x01---single-input-contact) + +
+ +
4BS Telegram (0xA5) + +- [FUNC 0x02 - TYPE 0x01 - Temperature Sensor Range -40°C to 0°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x01---temperature-sensor-range--40°c-to-0°c) +- [FUNC 0x02 - TYPE 0x02 - Temperature Sensor Range -30°C to +10°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x02---temperature-sensor-range--30°c-to-+10°c) +- [FUNC 0x02 - TYPE 0x03 - Temperature Sensor Range -20°C to +20°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x03---temperature-sensor-range--20°c-to-+20°c) +- [FUNC 0x02 - TYPE 0x04 - Temperature Sensor Range -10°C to +30°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x04---temperature-sensor-range--10°c-to-+30°c) +- [FUNC 0x02 - TYPE 0x05 - Temperature Sensor Range 0°C to +40°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x05---temperature-sensor-range-0°c-to-+40°c) +- [FUNC 0x02 - TYPE 0x06 - Temperature Sensor Range +10°C to +50°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x06---temperature-sensor-range-+10°c-to-+50°c) +- [FUNC 0x02 - TYPE 0x07 - Temperature Sensor Range +20°C to +60°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x07---temperature-sensor-range-+20°c-to-+60°c) +- [FUNC 0x02 - TYPE 0x08 - Temperature Sensor Range +30°C to +70°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x08---temperature-sensor-range-+30°c-to-+70°c) +- [FUNC 0x02 - TYPE 0x09 - Temperature Sensor Range +40°C to +80°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x09---temperature-sensor-range-+40°c-to-+80°c) +- [FUNC 0x02 - TYPE 0x0A - Temperature Sensor Range +50°C to +90°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x0A---temperature-sensor-range-+50°c-to-+90°c) +- [FUNC 0x02 - TYPE 0x0B - Temperature Sensor Range +60°C to +100°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x0B---temperature-sensor-range-+60°c-to-+100°c) +- [FUNC 0x02 - TYPE 0x10 - Temperature Sensor Range -60°C to +20°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x10---temperature-sensor-range--60°c-to-+20°c) +- [FUNC 0x02 - TYPE 0x11 - Temperature Sensor Range -50°C to +30°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x11---temperature-sensor-range--50°c-to-+30°c) +- [FUNC 0x02 - TYPE 0x12 - Temperature Sensor Range -40°C to +40°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x12---temperature-sensor-range--40°c-to-+40°c) +- [FUNC 0x02 - TYPE 0x13 - Temperature Sensor Range -30°C to +50°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x13---temperature-sensor-range--30°c-to-+50°c) +- [FUNC 0x02 - TYPE 0x14 - Temperature Sensor Range -20°C to +60°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x14---temperature-sensor-range--20°c-to-+60°c) +- [FUNC 0x02 - TYPE 0x15 - Temperature Sensor Range -10°C to +70°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x15---temperature-sensor-range--10°c-to-+70°c) +- [FUNC 0x02 - TYPE 0x16 - Temperature Sensor Range 0°C to +80°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x16---temperature-sensor-range-0°c-to-+80°c) +- [FUNC 0x02 - TYPE 0x17 - Temperature Sensor Range +10°C to +90°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x17---temperature-sensor-range-+10°c-to-+90°c) +- [FUNC 0x02 - TYPE 0x18 - Temperature Sensor Range +20°C to +100°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x18---temperature-sensor-range-+20°c-to-+100°c) +- [FUNC 0x02 - TYPE 0x19 - Temperature Sensor Range +30°C to +110°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x19---temperature-sensor-range-+30°c-to-+110°c) +- [FUNC 0x02 - TYPE 0x1A - Temperature Sensor Range +40°C to +120°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x1A---temperature-sensor-range-+40°c-to-+120°c) +- [FUNC 0x02 - TYPE 0x1B - Temperature Sensor Range +50°C to +130°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x1B---temperature-sensor-range-+50°c-to-+130°c) +- [FUNC 0x02 - TYPE 0x20 - 10 Bit Temperature Sensor Range -10°C to +41.2°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x20---10-bit-temperature-sensor-range--10°c-to-+41.2°c) +- [FUNC 0x02 - TYPE 0x30 - 10 Bit Temperature Sensor Range -40°C to +62.3°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x30---10-bit-temperature-sensor-range--40°c-to-+62.3°c) +- [FUNC 0x04 - TYPE 0x01 - Range 0°C to +40°C and 0% to 100%](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x04---type-0x01---range-0°c-to-+40°c-and-0%-to-100%) +- [FUNC 0x04 - TYPE 0x03 - Range -20°C to +60°C 10bit-measurement and 0% to 100%](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x04---type-0x03---range--20°c-to-+60°c-10bit-measurement-and-0%-to-100%) +- [FUNC 0x06 - TYPE 0x01 - Range 300lx to 60.000lx](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x06---type-0x01---range-300lx-to-60.000lx) +- [FUNC 0x06 - TYPE 0x02 - Range 0lx to 1.020lx](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x06---type-0x02---range-0lx-to-1.020lx) +- [FUNC 0x07 - TYPE 0x01 - Occupancy with Supply voltage monitor](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x07---type-0x01---occupancy-with-supply-voltage-monitor) +- [FUNC 0x07 - TYPE 0x03 - Occupancy with Supply voltage monitor and 10-bit illumination measurement](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x07---type-0x03---occupancy-with-supply-voltage-monitor-and-10-bit-illumination-measurement) +- [FUNC 0x08 - TYPE 0x01 - Range 0lx to 510lx, 0°C to +51°C and Occupancy Button](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x08---type-0x01---range-0lx-to-510lx,-0°c-to-+51°c-and-occupancy-button) +- [FUNC 0x09 - TYPE 0x04 - CO2 Sensor](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x09---type-0x04---co2-sensor) +- [FUNC 0x09 - TYPE 0x05 - VOC Sensor](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x09---type-0x05---voc-sensor) +- [FUNC 0x10 - TYPE 0x03 - Temperature Sensor and Set Point](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x10---type-0x03---temperature-sensor-and-set-point) +- [FUNC 0x10 - TYPE 0x05 - Temperature Sensor, Set Point and Occupancy Control](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x10---type-0x05---temperature-sensor,-set-point-and-occupancy-control) +- [FUNC 0x10 - TYPE 0x06 - Temperature Sensor, Set Point and Day/Night Control](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x10---type-0x06---temperature-sensor,-set-point-and-day/night-control) +- [FUNC 0x10 - TYPE 0x10 - Temperature and Humidity Sensor, Set Point and Occupancy Control](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x10---type-0x10---temperature-and-humidity-sensor,-set-point-and-occupancy-control) +- [FUNC 0x10 - TYPE 0x12 - Temperature and Humidity Sensor and Set Point](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x10---type-0x12---temperature-and-humidity-sensor-and-set-point) +- [FUNC 0x11 - TYPE 0x02 - Temperature Controller Output](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x11---type-0x02---temperature-controller-output) +- [FUNC 0x11 - TYPE 0x03 - Blind Status](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x11---type-0x03---blind-status) +- [FUNC 0x13 - TYPE 0x01 - Weather Station](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x13---type-0x01---weather-station) +- [FUNC 0x14 - TYPE 0x01 - Single Input Contact (Window/Door), Supply voltage monitor](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x14---type-0x01---single-input-contact-(window/door),-supply-voltage-monitor) +- [FUNC 0x20 - TYPE 0x01 - Battery Powered Actuator (BI-DIR)](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x20---type-0x01---battery-powered-actuator-(bi-dir)) +- [FUNC 0x12 - TYPE 0x01 - Electricity](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x12---type-0x01---electricity) +- [FUNC 0x30 - TYPE 0x03 - Digital Inputs, Wake and Temperature](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x30---type-0x03---digital-inputs,-wake-and-temperature) +- [FUNC 0x38 - TYPE 0x08 - Gateway](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x38---type-0x08---gateway) + +
+ +
VLD Telegram (0xD2) + +- [FUNC 0x01 - TYPE 0x01 - Electronic switch with Local Control](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xD2---func-0x01---type-0x01---electronic-switch-with-local-control) +- [FUNC 0x01 - TYPE 0x0F - Electronic switch with Local Control](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xD2---func-0x01---type-0x0F---electronic-switch-with-local-control) +- [FUNC 0x05 - TYPE 0x00 - Type 0x00](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xD2---func-0x05---type-0x00---type-0x00) +- [FUNC 0x14 - TYPE 0x41 - Indoor -Temperature, Humidity XYZ Acceleration, Illumination Sensor](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xD2---func-0x14---type-0x41---indoor--temperature,-humidity-xyz-acceleration,-illumination-sensor) + +
+ + + +--- + ### RPS Telegram (0xF6) + ##### RORG 0xF6 - FUNC 0x01 - TYPE 0x01 - Push Button |shortcut|description |type |values | @@ -10,7 +93,6 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian | | | |1 - Pressed | - ##### RORG 0xF6 - FUNC 0x02 - TYPE 0x01 - Light and Blind Control - Application Style 1 |shortcut|description |type |values | @@ -30,7 +112,6 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian |T21 |T21 |status | | |NU |NU |status | | - ##### RORG 0xF6 - FUNC 0x02 - TYPE 0x02 - Light and Blind Control - Application Style 2 |shortcut|description |type |values | @@ -51,7 +132,6 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian |NU |NU |status | | - ##### RORG 0xF6 - FUNC 0x05 - TYPE 0x01 - Liquid Leakage Sensor (mechanic harvester) |shortcut|description |type |values | @@ -62,7 +142,6 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian |T21 |T21 |status | | |NU |NU |status | | - ##### RORG 0xF6 - FUNC 0x05 - TYPE 0x02 - Smoke Detector |shortcut|description |type |values | @@ -72,7 +151,6 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian | | | |48 - Energy LOW | - ##### RORG 0xF6 - FUNC 0x10 - TYPE 0x00 - Window Handle |shortcut|description |type |values | @@ -85,8 +163,8 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian |NU |NU |status | | - ### 1BS Telegram (0xD5) + ##### RORG 0xD5 - FUNC 0x00 - TYPE 0x01 - Single Input Contact |shortcut|description |type |values | @@ -95,176 +173,152 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian | | | |1 - closed | - ### 4BS Telegram (0xA5) + ##### RORG 0xA5 - FUNC 0x02 - TYPE 0x01 - Temperature Sensor Range -40°C to 0°C |shortcut|description |type |values | |--------|--------------------------------------------------|--------|---- | |TMP |Temperature (linear) |value |255.0-0.0 ↔ -40.0-0.0 °C | - ##### RORG 0xA5 - FUNC 0x02 - TYPE 0x02 - Temperature Sensor Range -30°C to +10°C |shortcut|description |type |values | |--------|--------------------------------------------------|--------|---- | |TMP |Temperature (linear) |value |255.0-0.0 ↔ -30.0-10.0 °C | - ##### RORG 0xA5 - FUNC 0x02 - TYPE 0x03 - Temperature Sensor Range -20°C to +20°C |shortcut|description |type |values | |--------|--------------------------------------------------|--------|---- | |TMP |Temperature (linear) |value |255.0-0.0 ↔ -20.0-20.0 °C | - ##### RORG 0xA5 - FUNC 0x02 - TYPE 0x04 - Temperature Sensor Range -10°C to +30°C |shortcut|description |type |values | |--------|--------------------------------------------------|--------|---- | |TMP |Temperature (linear) |value |255.0-0.0 ↔ -10.0-30.0 °C | - ##### RORG 0xA5 - FUNC 0x02 - TYPE 0x05 - Temperature Sensor Range 0°C to +40°C |shortcut|description |type |values | |--------|--------------------------------------------------|--------|---- | |TMP |Temperature (linear) |value |255.0-0.0 ↔ 0.0-40.0 °C | - ##### RORG 0xA5 - FUNC 0x02 - TYPE 0x06 - Temperature Sensor Range +10°C to +50°C |shortcut|description |type |values | |--------|--------------------------------------------------|--------|---- | |TMP |Temperature (linear) |value |255.0-0.0 ↔ 10.0-50.0 °C | - ##### RORG 0xA5 - FUNC 0x02 - TYPE 0x07 - Temperature Sensor Range +20°C to +60°C |shortcut|description |type |values | |--------|--------------------------------------------------|--------|---- | |TMP |Temperature (linear) |value |255.0-0.0 ↔ 20.0-60.0 °C | - ##### RORG 0xA5 - FUNC 0x02 - TYPE 0x08 - Temperature Sensor Range +30°C to +70°C |shortcut|description |type |values | |--------|--------------------------------------------------|--------|---- | |TMP |Temperature (linear) |value |255.0-0.0 ↔ 30.0-70.0 °C | - ##### RORG 0xA5 - FUNC 0x02 - TYPE 0x09 - Temperature Sensor Range +40°C to +80°C |shortcut|description |type |values | |--------|--------------------------------------------------|--------|---- | |TMP |Temperature (linear) |value |255.0-0.0 ↔ 40.0-80.0 °C | - ##### RORG 0xA5 - FUNC 0x02 - TYPE 0x0A - Temperature Sensor Range +50°C to +90°C |shortcut|description |type |values | |--------|--------------------------------------------------|--------|---- | |TMP |Temperature (linear) |value |255.0-0.0 ↔ 50.0-90.0 °C | - ##### RORG 0xA5 - FUNC 0x02 - TYPE 0x0B - Temperature Sensor Range +60°C to +100°C |shortcut|description |type |values | |--------|--------------------------------------------------|--------|---- | |TMP |Temperature (linear) |value |255.0-0.0 ↔ 60.0-100.0 °C | - ##### RORG 0xA5 - FUNC 0x02 - TYPE 0x10 - Temperature Sensor Range -60°C to +20°C |shortcut|description |type |values | |--------|--------------------------------------------------|--------|---- | |TMP |Temperature (linear) |value |255.0-0.0 ↔ -60.0-20.0 °C | - ##### RORG 0xA5 - FUNC 0x02 - TYPE 0x11 - Temperature Sensor Range -50°C to +30°C |shortcut|description |type |values | |--------|--------------------------------------------------|--------|---- | |TMP |Temperature (linear) |value |255.0-0.0 ↔ -50.0-30.0 °C | - ##### RORG 0xA5 - FUNC 0x02 - TYPE 0x12 - Temperature Sensor Range -40°C to +40°C |shortcut|description |type |values | |--------|--------------------------------------------------|--------|---- | |TMP |Temperature (linear) |value |255.0-0.0 ↔ -40.0-40.0 °C | - ##### RORG 0xA5 - FUNC 0x02 - TYPE 0x13 - Temperature Sensor Range -30°C to +50°C |shortcut|description |type |values | |--------|--------------------------------------------------|--------|---- | |TMP |Temperature (linear) |value |255.0-0.0 ↔ -30.0-50.0 °C | - ##### RORG 0xA5 - FUNC 0x02 - TYPE 0x14 - Temperature Sensor Range -20°C to +60°C |shortcut|description |type |values | |--------|--------------------------------------------------|--------|---- | |TMP |Temperature (linear) |value |255.0-0.0 ↔ -20.0-60.0 °C | - ##### RORG 0xA5 - FUNC 0x02 - TYPE 0x15 - Temperature Sensor Range -10°C to +70°C |shortcut|description |type |values | |--------|--------------------------------------------------|--------|---- | |TMP |Temperature (linear) |value |255.0-0.0 ↔ -10.0-70.0 °C | - ##### RORG 0xA5 - FUNC 0x02 - TYPE 0x16 - Temperature Sensor Range 0°C to +80°C |shortcut|description |type |values | |--------|--------------------------------------------------|--------|---- | |TMP |Temperature (linear) |value |255.0-0.0 ↔ 0.0-80.0 °C | - ##### RORG 0xA5 - FUNC 0x02 - TYPE 0x17 - Temperature Sensor Range +10°C to +90°C |shortcut|description |type |values | |--------|--------------------------------------------------|--------|---- | |TMP |Temperature (linear) |value |255.0-0.0 ↔ 10.0-90.0 °C | - ##### RORG 0xA5 - FUNC 0x02 - TYPE 0x18 - Temperature Sensor Range +20°C to +100°C |shortcut|description |type |values | |--------|--------------------------------------------------|--------|---- | |TMP |Temperature (linear) |value |255.0-0.0 ↔ 20.0-100.0 °C | - ##### RORG 0xA5 - FUNC 0x02 - TYPE 0x19 - Temperature Sensor Range +30°C to +110°C |shortcut|description |type |values | |--------|--------------------------------------------------|--------|---- | |TMP |Temperature (linear) |value |255.0-0.0 ↔ 30.0-110.0 °C | - ##### RORG 0xA5 - FUNC 0x02 - TYPE 0x1A - Temperature Sensor Range +40°C to +120°C |shortcut|description |type |values | |--------|--------------------------------------------------|--------|---- | |TMP |Temperature (linear) |value |255.0-0.0 ↔ 40.0-120.0 °C | - ##### RORG 0xA5 - FUNC 0x02 - TYPE 0x1B - Temperature Sensor Range +50°C to +130°C |shortcut|description |type |values | |--------|--------------------------------------------------|--------|---- | |TMP |Temperature (linear) |value |255.0-0.0 ↔ 50.0-130.0 °C | - ##### RORG 0xA5 - FUNC 0x02 - TYPE 0x20 - 10 Bit Temperature Sensor Range -10°C to +41.2°C |shortcut|description |type |values | |--------|--------------------------------------------------|--------|---- | |TMP |Temperature (linear) |value |1023.0-0.0 ↔ -10.0-41.2 °C | - ##### RORG 0xA5 - FUNC 0x02 - TYPE 0x30 - 10 Bit Temperature Sensor Range -40°C to +62.3°C |shortcut|description |type |values | @@ -272,7 +326,6 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian |TMP |Temperature (linear) |value |1023.0-0.0 ↔ -40.0-62.3 °C | - ##### RORG 0xA5 - FUNC 0x04 - TYPE 0x01 - Range 0°C to +40°C and 0% to 100% |shortcut|description |type |values | @@ -282,7 +335,6 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian |TSN |Availability of the Temperature Sensor |enum |0 - not available | | | | |1 - available | - ##### RORG 0xA5 - FUNC 0x04 - TYPE 0x03 - Range -20°C to +60°C 10bit-measurement and 0% to 100% |shortcut|description |type |values | @@ -293,7 +345,6 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian | | | |1 - Event triggered | - ##### RORG 0xA5 - FUNC 0x06 - TYPE 0x01 - Range 300lx to 60.000lx |shortcut|description |type |values | @@ -304,7 +355,6 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian |RS |Range select |enum |0 - Range acc. to DB_1 (ILL1) | | | | |1 - Range acc. to DB_2 (ILL2) | - ##### RORG 0xA5 - FUNC 0x06 - TYPE 0x02 - Range 0lx to 1.020lx |shortcut|description |type |values | @@ -316,7 +366,6 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian | | | |1 - Range acc. to DB_2 (ILL2) | - ##### RORG 0xA5 - FUNC 0x07 - TYPE 0x01 - Occupancy with Supply voltage monitor |shortcut|description |type |values | @@ -325,7 +374,6 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian |PIR |PIR Status |enum |0 - off | | | | |1 - on | - ##### RORG 0xA5 - FUNC 0x07 - TYPE 0x03 - Occupancy with Supply voltage monitor and 10-bit illumination measurement |shortcut|description |type |values | @@ -336,7 +384,6 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian | | | |1 - Motion detected | - ##### RORG 0xA5 - FUNC 0x08 - TYPE 0x01 - Range 0lx to 510lx, 0°C to +51°C and Occupancy Button |shortcut|description |type |values | @@ -350,7 +397,6 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian | | | |1 - Button released | - ##### RORG 0xA5 - FUNC 0x09 - TYPE 0x04 - CO2 Sensor |shortcut|description |type |values | @@ -359,7 +405,6 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian |Conc |Concentration (linear) |value |0.0-255.0 ↔ 0.0-2550.0 ppm | |TMP |Temperature (linear) |value |0.0-255.0 ↔ 0.0-51.0 °C | - ##### RORG 0xA5 - FUNC 0x09 - TYPE 0x05 - VOC Sensor |shortcut|description |type |values | @@ -394,7 +439,6 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian | | | |255 - ozone | - ##### RORG 0xA5 - FUNC 0x10 - TYPE 0x03 - Temperature Sensor and Set Point |shortcut|description |type |values | @@ -402,7 +446,6 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian |SP |Set Point (linear) |value |0.0-255.0 ↔ 0.0-255.0 % | |TMP |Temperature (linear) |value |255.0-0.0 ↔ 0.0-40.0 °C | - ##### RORG 0xA5 - FUNC 0x10 - TYPE 0x05 - Temperature Sensor, Set Point and Occupancy Control |shortcut|description |type |values | @@ -412,7 +455,6 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian |OCC |Occupancy Button |enum |0 - Button pressed | | | | |1 - Button released | - ##### RORG 0xA5 - FUNC 0x10 - TYPE 0x06 - Temperature Sensor, Set Point and Day/Night Control |shortcut|description |type |values | @@ -422,7 +464,6 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian |SLSW |Slide switch |enum |0 - Position I / Night / Off | | | | |1 - Position O / Day / On | - ##### RORG 0xA5 - FUNC 0x10 - TYPE 0x10 - Temperature and Humidity Sensor, Set Point and Occupancy Control |shortcut|description |type |values | @@ -433,7 +474,6 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian |OCC |Occupancy Button |enum |0 - Button pressed | | | | |1 - Button released | - ##### RORG 0xA5 - FUNC 0x10 - TYPE 0x12 - Temperature and Humidity Sensor and Set Point |shortcut|description |type |values | @@ -443,7 +483,6 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian |TMP |Temperature (linear) |value |0.0-250.0 ↔ 0.0-40.0 °C | - ##### RORG 0xA5 - FUNC 0x11 - TYPE 0x02 - Temperature Controller Output |shortcut|description |type |values | @@ -473,7 +512,6 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian | | | |2 - StandBy | | | | |3 - Frost | - ##### RORG 0xA5 - FUNC 0x11 - TYPE 0x03 - Blind Status |shortcut|description |type |values | @@ -504,7 +542,6 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian | | | |1 - Inverse ode | - ##### RORG 0xA5 - FUNC 0x13 - TYPE 0x01 - Weather Station ###### command: 1 @@ -565,13 +602,12 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian ###### command: 6 |shortcut|description |type |values | |--------|--------------------------------------------------|--------|---- | -|LAT(MSB)|Latitude(MSB) |value |0.0-15.0 ↔ 0.0-15.0 | +| |Latitude(MSB) |value |0.0-15.0 ↔ 0.0-15.0 | |LOT(MSB)|Longitude(MSB) |value |0.0-15.0 ↔ 0.0-15.0 | |LAT(LSB)|Latitude(LSB) |value |0.0-255.0 ↔ 0.0-255.0 | |LOT(LSB)|Longitude(LSB) |value |0.0-255.0 ↔ 0.0-255.0 | - ##### RORG 0xA5 - FUNC 0x14 - TYPE 0x01 - Single Input Contact (Window/Door), Supply voltage monitor |shortcut|description |type |values | @@ -581,7 +617,6 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian | | | |0 - closed | - ##### RORG 0xA5 - FUNC 0x20 - TYPE 0x01 - Battery Powered Actuator (BI-DIR) ###### direction: 1 @@ -629,7 +664,6 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian | | | |1 - service on | - ##### RORG 0xA5 - FUNC 0x12 - TYPE 0x01 - Electricity |shortcut|description |type |values | @@ -644,7 +678,6 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian | | | |3 - x/1000 | - ##### RORG 0xA5 - FUNC 0x30 - TYPE 0x03 - Digital Inputs, Wake and Temperature |shortcut|description |type |values | @@ -662,7 +695,6 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian | | | |1 - High | - ##### RORG 0xA5 - FUNC 0x38 - TYPE 0x08 - Gateway ###### command: 1 @@ -691,8 +723,8 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian | | | |1 - On | - ### VLD Telegram (0xD2) + ##### RORG 0xD2 - FUNC 0x01 - TYPE 0x01 - Electronic switch with Local Control ###### command: 4 @@ -736,7 +768,6 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian | | | |101-126 - Not used | | | | |127 - output value not valid / not set | - ##### RORG 0xD2 - FUNC 0x01 - TYPE 0x0F - Electronic switch with Local Control ###### command: 4 @@ -781,7 +812,6 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian | | | |127 - output value not valid / not set | - ##### RORG 0xD2 - FUNC 0x05 - TYPE 0x00 - Type 0x00 ###### command: 1 @@ -841,7 +871,6 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian |CMD |Command Id |enum |0-5 - Command ID {value} | - ##### RORG 0xD2 - FUNC 0x14 - TYPE 0x41 - Indoor -Temperature, Humidity XYZ Acceleration, Illumination Sensor |shortcut|description |type |values | @@ -859,4 +888,3 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian | | | |1 - Closed | - diff --git a/enocean/protocol/eep.py b/enocean/protocol/eep.py index 7edfa95..cdc3a70 100644 --- a/enocean/protocol/eep.py +++ b/enocean/protocol/eep.py @@ -17,7 +17,7 @@ def __init__(self): self.telegrams = {} try: - xml_content = files('enocean.protocol').joinpath('EEP.xml').read_text() + xml_content = files('enocean.protocol').joinpath('EEP.xml').read_text(encoding='utf-8') self.soup = BeautifulSoup(xml_content, features="lxml-xml") self.__xml_to_dict() self.init_ok = True diff --git a/generate_supported_profiles.py b/generate_supported_profiles.py index b6d0bff..07413f6 100755 --- a/generate_supported_profiles.py +++ b/generate_supported_profiles.py @@ -6,20 +6,40 @@ from enocean.protocol.eep import EEP ROW_FORMAT = '|{:8s}|{:50s}|{:8s}|{:70s}|\n' - +BASE_URL = "https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#" eep = EEP() with codecs.open('SUPPORTED_PROFILES.md', 'w', 'utf-8') as f_handle: - f_handle.write('# Supported profiles\n') - f_handle.write('All profiles (should) correspond to the official [EEP](http://www.enocean-alliance.org/eep/) by EnOcean.\n\n') + write = f_handle.write + write("# Supported profiles\n") + write( + "All profiles (should) correspond to the official" + " [EEP](http://www.enocean-alliance.org/eep/) by EnOcean.\n\n" + ) + + # table of contents + for telegram in eep.soup.find_all('telegram'): + write("
") + write(f" {telegram['description']} ({telegram['rorg']}) \n\n") + for func in telegram.find_all('profiles'): + # f_handle.write('##### FUNC %s - %s\n' % (func['func'], func['description'])) + for profile in func.find_all('profile'): + write( + f"- [FUNC {func['func']} - TYPE {profile['type']} - {profile['description']}]" + f"({BASE_URL}rorg-{telegram['rorg']}---func-{func['func']}---type-{profile['type']}---{profile['description'].lower().replace(' ', '-')})\n" + ) + + write("\n
\n\n") + write('\n\n---\n\n') + # contents for telegram in eep.soup.find_all('telegram'): - f_handle.write('### %s (%s)\n' % (telegram['description'], telegram['rorg'])) + write('### %s (%s)\n\n' % (telegram['description'], telegram['rorg'])) for func in telegram.find_all('profiles'): # f_handle.write('##### FUNC %s - %s\n' % (func['func'], func['description'])) for profile in func.find_all('profile'): - f_handle.write('##### RORG %s - FUNC %s - TYPE %s - %s\n\n' % (telegram['rorg'], func['func'], profile['type'], profile['description'])) + write('##### RORG %s - FUNC %s - TYPE %s - %s\n\n' % (telegram['rorg'], func['func'], profile['type'], profile['description'])) for data in profile.find_all('data'): header = [] @@ -30,10 +50,10 @@ header.append('command: %s' % (data.get('command'))) if header: - f_handle.write('###### %s\n' % ' '.join(header)) + write('###### %s\n' % ' '.join(header)) - f_handle.write(ROW_FORMAT.format('shortcut', 'description', 'type', 'values')) - f_handle.write(ROW_FORMAT.format('--------', '--------------------------------------------------', '--------', '----')) + write(ROW_FORMAT.format('shortcut', 'description', 'type', 'values')) + write(ROW_FORMAT.format('--------', '--------------------------------------------------', '--------', '----')) for child in data.children: if child.name is None: continue @@ -58,13 +78,15 @@ values.append('%s-%s ↔ %s-%s %s' % (range_min, range_max, scale_min, scale_max, parent['unit'])) if not values: - f_handle.write(ROW_FORMAT.format(child['shortcut'], child['description'], child.name, '')) + write(ROW_FORMAT.format(child['shortcut'], child['description'], child.name, '')) continue - f_handle.write(ROW_FORMAT.format(child['shortcut'], child['description'], child.name, values[0])) + write(ROW_FORMAT.format( + child.get('shortcut', ''), + child.get('description', ''), + child.name, values[0] + )) for i in range(1, len(values)): - f_handle.write(ROW_FORMAT.format('', '', '', values[i])) - f_handle.write('\n') - f_handle.write('\n') - - f_handle.write('\n') + write(ROW_FORMAT.format('', '', '', values[i])) + write('\n') + write('\n') diff --git a/pyproject.toml b/pyproject.toml index c909391..f6864c4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "enocean4ha" -dynamic = [ "version", ] +dynamic = [ "version", "readme"] authors = [ { name="Kimmo Huoman", email="kipenroskaposti@gmail.com" }, ] @@ -13,7 +13,6 @@ maintainers = [ ] description = "EnOcean serial protocol implementation" keywords = [ "EnOcean", "HomeAssistant", ] -readme = "README.md" requires-python = ">=3.9" classifiers = [ "Programming Language :: Python :: 3", @@ -37,6 +36,7 @@ tests = [ "nose2>=0.15.1", ] [tool.setuptools.dynamic] version = { attr = "enocean.__version__" } +readme = { file = [ "README.md", "SUPPORTED_PROFILES.md" ], content-type = "text/markdown" } [tool.setuptools.packages.find] where = [ "." ] From 74c45095c29e1de437cc90764e5f7be8b18e01a7 Mon Sep 17 00:00:00 2001 From: topic2k Date: Sun, 1 Sep 2024 18:02:07 +0200 Subject: [PATCH 23/40] first start with using simple type annotations replaced most %-string formatting with f-strings --- SUPPORTED_PROFILES.md | 2 +- decorators.py | 6 +- enocean/communicators/__init__.py | 2 +- enocean/communicators/communicator.py | 32 +++-- enocean/communicators/serialcommunicator.py | 6 +- enocean/communicators/tcpcommunicator.py | 8 +- enocean/protocol/constants.py | 19 ++- enocean/protocol/eep.py | 73 +++++----- enocean/protocol/packet.py | 140 +++++++++++--------- enocean/utils.py | 26 ++-- generate_supported_profiles.py | 29 ++-- tests/test_protocol_eep.py | 18 +-- tests/test_protocol_temperature_sensors.py | 8 +- 13 files changed, 198 insertions(+), 171 deletions(-) diff --git a/SUPPORTED_PROFILES.md b/SUPPORTED_PROFILES.md index 5b73b58..fe7027b 100644 --- a/SUPPORTED_PROFILES.md +++ b/SUPPORTED_PROFILES.md @@ -1,5 +1,5 @@ # Supported profiles -All profiles (should) correspond to the official [EEP](http://www.enocean-alliance.org/eep/) by EnOcean. +All profiles (should) correspond to the official [EEP](https://www.enocean-alliance.org/eep/) by EnOcean.
RPS Telegram (0xF6) diff --git a/decorators.py b/decorators.py index f03ff95..82c95bd 100644 --- a/decorators.py +++ b/decorators.py @@ -6,11 +6,11 @@ def timing(rounds=1, limit=None): - ''' + """ Wrapper to implement simple timing of tests. Allows running multiple rounds to calculate average time. Limit (in milliseconds) can be set to assert, if (average) duration is too high. - ''' + """ def decorator(method): @functools.wraps(method) def f(): @@ -26,7 +26,7 @@ def f(): # Use milliseconds for duration counter duration = duration * 1e3 - print('Test "%s.%s" took %.06f ms.' % (method.__module__, method.__name__, duration)) + print(f'Test "{method.__module__}.{method.__name__}" took {duration:.06f} ms.') if limit is not None: assert limit > duration, 'Timing failure: %.06f > %.06f' % (duration, limit) diff --git a/enocean/communicators/__init__.py b/enocean/communicators/__init__.py index a0682e7..91a5b90 100644 --- a/enocean/communicators/__init__.py +++ b/enocean/communicators/__init__.py @@ -1,4 +1,4 @@ -''' Provider for different Communicator -classes for EnOcean. ''' +""" Provider for different Communicator -classes for EnOcean. """ from .communicator import Communicator from .serialcommunicator import SerialCommunicator diff --git a/enocean/communicators/communicator.py b/enocean/communicators/communicator.py index 3e5a16d..da8f116 100644 --- a/enocean/communicators/communicator.py +++ b/enocean/communicators/communicator.py @@ -10,13 +10,13 @@ class Communicator(threading.Thread): - ''' + """ Communicator base-class for EnOcean. Not to be used directly, only serves as base class for SerialCommunicator etc. - ''' + """ logger = logging.getLogger('enocean.communicators.Communicator') - def __init__(self, callback=None, teach_in=True): + def __init__(self, callback: callable = None, teach_in: bool = True) -> None: super(Communicator, self).__init__() # Create an event to stop the thread self._stop_flag = threading.Event() @@ -33,8 +33,8 @@ def __init__(self, callback=None, teach_in=True): # TODO: Not sure if we should use CO_WR_LEARNMODE?? self.teach_in = teach_in - def _get_from_send_queue(self): - ''' Get message from send queue, if one exists ''' + def _get_from_send_queue(self) -> Packet | None: + """ Get message from send queue, if one exists """ try: packet = self.transmit.get(block=False) self.logger.info('Sending packet') @@ -44,18 +44,18 @@ def _get_from_send_queue(self): pass return None - def send(self, packet): + def send(self, packet: Packet) -> bool: if not isinstance(packet, Packet): self.logger.error('Object to send must be an instance of Packet') return False self.transmit.put(packet) return True - def stop(self): + def stop(self) -> None: self._stop_flag.set() - def parse(self): - ''' Parses messages and puts them to receive queue ''' + def parse(self) -> None | PARSE_RESULT: + """ Parses messages and puts them to receive queue """ # Loop while we get new messages while True: status, self._buffer, packet = Packet.parse_msg(self._buffer) @@ -79,8 +79,8 @@ def parse(self): self.logger.debug(packet) @property - def base_id(self): - ''' Fetches Base ID from the transmitter, if required. Otherwise returns the currently set Base ID. ''' + def base_id(self) -> None | list[int, int, int, int]: + """ Fetches Base ID from the transmitter, if required. Otherwise returns the currently set Base ID. """ # If base id is already set, return it. if self._base_id is not None: return self._base_id @@ -94,7 +94,11 @@ def base_id(self): try: packet = self.receive.get(block=True, timeout=0.1) # We're only interested in responses to the request in question. - if packet.packet_type == PACKET.RESPONSE and packet.response == RETURN_CODE.OK and len(packet.response_data) == 4: # noqa: E501 + if ( + packet.packet_type == PACKET.RESPONSE + and packet.response == RETURN_CODE.OK + and len(packet.response_data) == 4 + ): # Base ID is set in the response data. self._base_id = packet.response_data # Put packet back to the Queue, so the user can also react to it if required... @@ -108,6 +112,6 @@ def base_id(self): return self._base_id @base_id.setter - def base_id(self, base_id): - ''' Sets the Base ID manually, only for testing purposes. ''' + def base_id(self, base_id: list[int, int, int, int]): + """ Sets the Base ID manually, only for testing purposes. """ self._base_id = base_id diff --git a/enocean/communicators/serialcommunicator.py b/enocean/communicators/serialcommunicator.py index 2954ce5..58849c8 100644 --- a/enocean/communicators/serialcommunicator.py +++ b/enocean/communicators/serialcommunicator.py @@ -9,15 +9,15 @@ class SerialCommunicator(Communicator): - ''' Serial port communicator class for EnOcean radio ''' + """ Serial port communicator class for EnOcean radio """ logger = logging.getLogger('enocean.communicators.SerialCommunicator') - def __init__(self, port='/dev/ttyAMA0', callback=None): + def __init__(self, port: str = '/dev/ttyAMA0', callback: callable = None) -> None: super(SerialCommunicator, self).__init__(callback) # Initialize serial port self.__ser = serial.Serial(port, 57600, timeout=0.1) - def run(self): + def run(self) -> None: self.logger.info('SerialCommunicator started') while not self._stop_flag.is_set(): # If there's messages in transmit queue diff --git a/enocean/communicators/tcpcommunicator.py b/enocean/communicators/tcpcommunicator.py index a8fbdf8..13f80fb 100644 --- a/enocean/communicators/tcpcommunicator.py +++ b/enocean/communicators/tcpcommunicator.py @@ -7,15 +7,15 @@ class TCPCommunicator(Communicator): - ''' Socket communicator class for EnOcean radio ''' + """ Socket communicator class for EnOcean radio """ logger = logging.getLogger('enocean.communicators.TCPCommunicator') - def __init__(self, host='', port=9637): + def __init__(self, host: str = '', port: int = 9637) -> None: super(TCPCommunicator, self).__init__() self.host = host self.port = port - def run(self): + def run(self) -> None: self.logger.info('TCPCommunicator started') sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.bind((self.host, self.port)) @@ -27,7 +27,7 @@ def run(self): (client, addr) = sock.accept() except socket.timeout: continue - self.logger.debug('Client "%s" connected' % (addr)) + self.logger.debug(f'Client "{addr}" connected') client.settimeout(0.5) while True and not self._stop_flag.is_set(): try: diff --git a/enocean/protocol/constants.py b/enocean/protocol/constants.py index 584cb28..17a004f 100644 --- a/enocean/protocol/constants.py +++ b/enocean/protocol/constants.py @@ -27,12 +27,14 @@ class PACKET(IntEnum): # EnOceanSerialProtocol3-1-1.pdf / 33 +# noinspection PyPep8Naming class COMMON_COMMAND(IntEnum): CO_RD_VERSION = 0x03 CO_RD_IDBASE = 0x08 # EnOceanSerialProtocol3.pdf / 18 +# noinspection PyPep8Naming class RETURN_CODE(IntEnum): OK = 0x00 ERROR = 0x01 @@ -42,6 +44,7 @@ class RETURN_CODE(IntEnum): # EnOceanSerialProtocol3.pdf / 20 +# noinspection PyPep8Naming class EVENT_CODE(IntEnum): SA_RECLAIM_NOT_SUCCESFUL = 0x01 SA_CONFIRM_LEARN = 0x02 @@ -51,6 +54,7 @@ class EVENT_CODE(IntEnum): # EnOcean_Equipment_Profiles_EEP_V2.61_public.pdf / 8 +# noinspection PyPep8Naming class RORG(IntEnum): UNDEFINED = 0x00 RPS = 0xF6 @@ -69,6 +73,7 @@ class RORG(IntEnum): # Results for message parsing +# noinspection PyPep8Naming class PARSE_RESULT(IntEnum): OK = 0x00 INCOMPLETE = 0x01 @@ -77,7 +82,7 @@ class PARSE_RESULT(IntEnum): # Data byte indexing # Starts from the end, so works on messages of all length. -class DB0(object): +class DB0: BIT_0 = -1 BIT_1 = -2 BIT_2 = -3 @@ -88,7 +93,7 @@ class DB0(object): BIT_7 = -8 -class DB1(object): +class DB1: BIT_0 = -9 BIT_1 = -10 BIT_2 = -11 @@ -99,7 +104,7 @@ class DB1(object): BIT_7 = -16 -class DB2(object): +class DB2: BIT_0 = -17 BIT_1 = -18 BIT_2 = -19 @@ -110,7 +115,7 @@ class DB2(object): BIT_7 = -24 -class DB3(object): +class DB3: BIT_0 = -25 BIT_1 = -26 BIT_2 = -27 @@ -121,7 +126,7 @@ class DB3(object): BIT_7 = -32 -class DB4(object): +class DB4: BIT_0 = -33 BIT_1 = -34 BIT_2 = -35 @@ -132,7 +137,7 @@ class DB4(object): BIT_7 = -40 -class DB5(object): +class DB5: BIT_0 = -41 BIT_1 = -42 BIT_2 = -43 @@ -143,7 +148,7 @@ class DB5(object): BIT_7 = -48 -class DB6(object): +class DB6: BIT_0 = -49 BIT_1 = -50 BIT_2 = -51 diff --git a/enocean/protocol/eep.py b/enocean/protocol/eep.py index cdc3a70..dc80f2f 100644 --- a/enocean/protocol/eep.py +++ b/enocean/protocol/eep.py @@ -4,15 +4,15 @@ from collections import OrderedDict from importlib.resources import files -from bs4 import BeautifulSoup +from bs4 import BeautifulSoup, Tag from .. import utils -class EEP(object): +class EEP: logger = logging.getLogger('enocean.protocol.eep') - def __init__(self): + def __init__(self) -> None: self.init_ok = False self.telegrams = {} @@ -28,7 +28,7 @@ def __init__(self): self.logger.warning('Cannot load protocol file!') self.init_ok = False - def __xml_to_dict(self): + def __xml_to_dict(self) -> None: self.telegrams = { utils.from_hex_string(telegram['rorg']): { utils.from_hex_string(function['func']): { @@ -41,15 +41,15 @@ def __xml_to_dict(self): } @staticmethod - def _get_raw(source, bitarray): - ''' Get raw data as integer, based on offset and size ''' + def _get_raw(source, bitarray) -> int: + """ Get raw data as integer, based on offset and size """ offset = int(source['offset']) size = int(source['size']) return int(''.join(['1' if digit else '0' for digit in bitarray[offset:offset + size]]), 2) @staticmethod - def _set_raw(target, raw_value, bitarray): - ''' put value into bit array ''' + def _set_raw(target: Tag, raw_value: int, bitarray: list) -> list: + """ put value into bit array """ offset = int(target['offset']) size = int(target['size']) for digit in range(size): @@ -57,13 +57,13 @@ def _set_raw(target, raw_value, bitarray): return bitarray @staticmethod - def _get_rangeitem(source, raw_value): + def _get_rangeitem(source: Tag, raw_value: int) -> Tag: for rangeitem in source.find_all('rangeitem'): if raw_value in range(int(rangeitem.get('start', -1)), int(rangeitem.get('end', -1)) + 1): return rangeitem - def _get_value(self, source, bitarray): - ''' Get value, based on the data in XML ''' + def _get_value(self, source: Tag, bitarray: list) -> dict: + """ Get value, based on the data in XML """ raw_value = self._get_raw(source, bitarray) rng = source.find('range') @@ -83,8 +83,8 @@ def _get_value(self, source, bitarray): } } - def _get_enum(self, source, bitarray): - ''' Get enum value, based on the data in XML ''' + def _get_enum(self, source: Tag, bitarray: list) -> dict: + """ Get enum value, based on the data in XML """ raw_value = self._get_raw(source, bitarray) # Find value description. @@ -99,8 +99,8 @@ def _get_enum(self, source, bitarray): } } - def _get_boolean(self, source, bitarray): - ''' Get boolean value, based on the data in XML ''' + def _get_boolean(self, source: Tag, bitarray: list) -> dict: + """ Get boolean value, based on the data in XML """ raw_value = self._get_raw(source, bitarray) return { source['shortcut']: { @@ -111,8 +111,8 @@ def _get_boolean(self, source, bitarray): } } - def _set_value(self, target, value, bitarray): - ''' set given numeric value to target field in bitarray ''' + def _set_value(self, target: Tag, value: int | float, bitarray: list) -> list: + """ set given numeric value to target field in bitarray """ # derive raw value rng = target.find('range') rng_min = float(rng.find('min').text) @@ -124,8 +124,8 @@ def _set_value(self, target, value, bitarray): # store value in bitfield return self._set_raw(target, int(raw_value), bitarray) - def _set_enum(self, target, value, bitarray): - ''' set given enum value (by string or integer value) to target field in bitarray ''' + def _set_enum(self, target: Tag, value: int | str, bitarray: list) -> list: + """ set given enum value (by string or integer value) to target field in bitarray """ # derive raw value if isinstance(value, int): # check whether this value exists @@ -133,39 +133,41 @@ def _set_enum(self, target, value, bitarray): # set integer values directly raw_value = value else: - raise ValueError('Enum value "%s" not found in EEP.' % (value)) + raise ValueError(f'Enum value "{value}" not found in EEP.') else: value_item = target.find('item', {'description': value}) if value_item is None: - raise ValueError('Enum description for value "%s" not found in EEP.' % (value)) + raise ValueError(f'Enum description for value "{value}" not found in EEP.') raw_value = int(value_item['value']) return self._set_raw(target, raw_value, bitarray) @staticmethod - def _set_boolean(target, data, bitarray): - ''' set given value to target bit in bitarray ''' + def _set_boolean(target: Tag, data: bool, bitarray: list) -> list: + """ set given value to target bit in bitarray """ bitarray[int(target['offset'])] = data return bitarray - def find_profile(self, bitarray, eep_rorg, rorg_func, rorg_type, direction=None, command=None): - ''' Find profile and data description, matching RORG, FUNC and TYPE ''' + def find_profile(self, eep_rorg: int, rorg_func: int, rorg_type: int, direction=None, command=None) -> None | Tag: + """ Find profile and data description, matching RORG, FUNC and TYPE """ if not self.init_ok: self.logger.warning('EEP.xml not loaded!') return None if eep_rorg not in self.telegrams.keys(): - self.logger.warning('Cannot find rorg %s in EEP!', hex(eep_rorg)) + self.logger.warning(f'Cannot find rorg {hex(eep_rorg)} in EEP!') return None if rorg_func not in self.telegrams[eep_rorg].keys(): - self.logger.warning('Cannot find rorg %s func %s in EEP!', hex(eep_rorg), hex(rorg_func)) + self.logger.warning(f'Cannot find rorg {hex(eep_rorg)} func {hex(rorg_func)} in EEP!') return None if rorg_type not in self.telegrams[eep_rorg][rorg_func].keys(): - self.logger.warning('Cannot find rorg %s func %s type %s in EEP!', hex(eep_rorg), hex(rorg_func), hex(rorg_type)) + self.logger.warning( + f'Cannot find rorg {hex(eep_rorg)} func {hex(rorg_func)} type {hex(rorg_type)} in EEP!' + ) return None - profile = self.telegrams[eep_rorg][rorg_func][rorg_type] + profile: Tag = self.telegrams[eep_rorg][rorg_func][rorg_type] if command: # multiple commands can be defined, with the command id always in same location (per RORG-FUNC-TYPE). @@ -184,8 +186,8 @@ def find_profile(self, bitarray, eep_rorg, rorg_func, rorg_type, direction=None, return profile.find('data', recursive=False) return profile.find('data', {'direction': direction}, recursive=False) - def get_values(self, profile, bitarray, status): - ''' Get keys and values from bitarray ''' + def get_values(self, profile: Tag, bitarray: list, status: list) -> tuple: + """ Get keys and values from bitarray """ if not self.init_ok or profile is None: return [], {} @@ -202,12 +204,11 @@ def get_values(self, profile, bitarray, status): pass elif source.name == 'status': output.update(self._get_boolean(source, status)) - # else: - # pass + return output.keys(), output - def set_values(self, profile, data, status, properties): - ''' Update data based on data contained in properties ''' + def set_values(self, profile: Tag, data: list, status: list, properties: dict) -> tuple: + """ Update data based on data contained in properties """ if not self.init_ok or profile is None: return data, status @@ -216,7 +217,7 @@ def set_values(self, profile, data, status, properties): target = profile.find(shortcut=shortcut) if not target: # TODO: Should we raise an error? - self.logger.warning('Cannot find data description for shortcut %s', shortcut) + self.logger.warning(f"Cannot find data description for shortcut '{shortcut}'") continue # update bit_data diff --git a/enocean/protocol/packet.py b/enocean/protocol/packet.py index 0aa0171..f5167ea 100644 --- a/enocean/protocol/packet.py +++ b/enocean/protocol/packet.py @@ -2,6 +2,7 @@ import logging from collections import OrderedDict +from typing import Any from .crc8 import calc from .constants import PACKET, RORG, PARSE_RESULT, DB0, DB2, DB3, DB4, DB6 @@ -10,16 +11,16 @@ class Packet: - ''' + """ Base class for Packet. Mainly used for for packet generation and Packet.parse_msg(buf) for parsing message. parse_msg() returns subclass, if one is defined for the data type. - ''' + """ eep = EEP() logger = logging.getLogger('enocean.protocol.packet') - def __init__(self, packet_type, data=None, optional=None): + def __init__(self, packet_type: PACKET, data: None | list = None, optional: None | list = None) -> None: self.packet_type = packet_type self.rorg = RORG.UNDEFINED self.rorg_func = None @@ -47,35 +48,32 @@ def __init__(self, packet_type, data=None, optional=None): self.parse() - def __str__(self): - return '0x%02X %s %s %s' % ( - self.packet_type, - [hex(o) for o in self.data], - [hex(o) for o in self.optional], - self.parsed) + def __str__(self) -> str: + return (f'0x{self.packet_type:02X} {[hex(o) for o in self.data]}' + f' {[hex(o) for o in self.optional]} {self.parsed}') - def __unicode__(self): + def __unicode__(self) -> str: return self.__str__() - def __eq__(self, other): + def __eq__(self, other) -> bool: return self.packet_type == other.packet_type and self.rorg == other.rorg \ and self.data == other.data and self.optional == other.optional @property - def _bit_data(self): + def _bit_data(self) -> list[bool]: # First and last 5 bits are always defined, so the data we're modifying is between them... # TODO: This is valid for the packets we're currently manipulating. # Needs the redefinition of Packet.data -> Packet.message. # Packet.data would then only have the actual, documented data-bytes. # Packet.message would contain the whole message. - # See discussion in issue #14 + # See discussion in issue https://github.com/kipe/enocean/issues/14 return to_bitarray(self.data[1:len(self.data) - 5], (len(self.data) - 6) * 8) @_bit_data.setter - def _bit_data(self, value): + def _bit_data(self, value: list[bool]) -> None: # The same as getting the data, first and last 5 bits are ommitted, as they are defined... for byte in range(len(self.data) - 6): - self.data[byte+1] = from_bitarray(value[byte * 8:(byte + 1) * 8]) + self.data[byte + 1] = from_bitarray(value[byte * 8:(byte + 1) * 8]) # # COMMENTED OUT, AS NOTHING TOUCHES _bit_optional FOR NOW. # # Thus, this is also untested. @@ -92,22 +90,24 @@ def _bit_data(self, value): # self.data[byte+1] = enocean.utils.from_bitarray(value[byte*8:(byte+1)*8]) @property - def _bit_status(self): + def _bit_status(self) -> list[bool]: return to_bitarray(self.status) @_bit_status.setter - def _bit_status(self, value): + def _bit_status(self, value: list[bool]) -> None: self.status = from_bitarray(value) @staticmethod - def parse_msg(buf): - ''' + def parse_msg(buf: list | bytearray) -> (PARSE_RESULT, list, Any): + # 'Any' in return type should be [None | UTETeachInPacket | ResponsePacket | EventPacket | Packet] + # how to realize that? + """ Parses message from buffer. returns: - PARSE_RESULT - remaining buffer - Packet -object (if message was valid, else None) - ''' + """ # If the buffer doesn't contain 0x55 (start char) # the message isn't needed -> ignore if 0x55 not in buf: @@ -165,11 +165,16 @@ def parse_msg(buf): return PARSE_RESULT.OK, buf, packet @staticmethod - def create(packet_type, rorg, rorg_func, rorg_type, direction=None, command=None, - destination=None, - sender=None, - learn=False, **kwargs): - ''' + def create( + packet_type: PACKET, rorg: RORG, rorg_func: int, rorg_type: int, + direction=None, + command: None | int = None, + destination: None | list = None, + sender: None | list = None, + learn: bool = False, + **kwargs + ): + """ Creates an packet ready for sending. Uses rorg, rorg_func and rorg_type to determine the values set based on EEP. Additional arguments (**kwargs) are used for setting the values. @@ -182,7 +187,7 @@ def create(packet_type, rorg, rorg_func, rorg_type, direction=None, command=None - Require sender to be set? Would force the "correct" sender to be set. - Do we need to set telegram control bits? Might be useful for acting as a repeater? - ''' + """ if packet_type != PACKET.RADIO_ERP1: # At least for now, only support PACKET.RADIO_ERP1. @@ -203,10 +208,10 @@ def create(packet_type, rorg, rorg_func, rorg_type, direction=None, command=None sender = [0xDE, 0xAD, 0xBE, 0xEF] if not isinstance(destination, list) or len(destination) != 4: - raise ValueError('Destination must a list containing 4 (numeric) values.') + raise ValueError('Destination must be a list containing 4 (numeric) values.') if not isinstance(sender, list) or len(sender) != 4: - raise ValueError('Sender must a list containing 4 (numeric) values.') + raise ValueError('Sender must be a list containing 4 (numeric) values.') packet = Packet(packet_type, data=[], optional=[]) packet.rorg = rorg @@ -243,11 +248,12 @@ def create(packet_type, rorg, rorg_func, rorg_type, direction=None, command=None # For example, stuff like RadioPacket.learn should be set. packet = Packet.parse_msg(packet.build())[2] packet.rorg = rorg + # noinspection PyUnresolvedReferences packet.parse_eep(rorg_func, rorg_type, direction, command) return packet - def parse(self): - ''' Parse data from Packet ''' + def parse(self) -> OrderedDict: + """ Parse data from Packet """ # Parse status from messages if self.rorg in [RORG.RPS, RORG.BS1, RORG.BS4]: self.status = self.data[-1] @@ -259,16 +265,17 @@ def parse(self): self.repeater_count = from_bitarray(self._bit_status[4:]) return self.parsed - def select_eep(self, rorg_func, rorg_type, direction=None, command=None): - ''' Set EEP based on FUNC and TYPE ''' + def select_eep(self, rorg_func: int, rorg_type: int, direction=None, command: None | int = None) -> bool: + """ Set EEP based on FUNC and TYPE """ # set EEP profile self.rorg_func = rorg_func self.rorg_type = rorg_type - self._profile = self.eep.find_profile(self._bit_data, self.rorg, rorg_func, rorg_type, direction, command) + self._profile = self.eep.find_profile(self.rorg, rorg_func, rorg_type, direction, command) return self._profile is not None - def parse_eep(self, rorg_func=None, rorg_type=None, direction=None, command=None): - ''' Parse EEP based on FUNC and TYPE ''' + def parse_eep(self, rorg_func: None | int = None, rorg_type: None | int = None, + direction=None, command: None | int = None) -> list: + """ Parse EEP based on FUNC and TYPE """ # set EEP profile, if demanded if rorg_func is not None and rorg_type is not None: self.select_eep(rorg_func, rorg_type, direction, command) @@ -277,13 +284,14 @@ def parse_eep(self, rorg_func=None, rorg_type=None, direction=None, command=None self.parsed.update(values) return list(provides) - def set_eep(self, data): - ''' Update packet data based on EEP. Input data is a dictionary with keys corresponding to the EEP. ''' + def set_eep(self, data: dict) -> None: + """ Update packet data based on EEP. Input data is a dictionary with keys corresponding to the EEP. """ self._bit_data, self._bit_status = self.eep.set_values(self._profile, self._bit_data, self._bit_status, data) - def build(self): - ''' Build Packet for sending to EnOcean controller ''' + def build(self) -> list: + """ Build Packet for sending to EnOcean controller """ data_length = len(self.data) + # noinspection PyListCreation ords = [0x55, (data_length >> 8) & 0xFF, data_length & 0xFF, len(self.optional), int(self.packet_type)] ords.append(calc(ords[1:5])) ords.extend(self.data) @@ -299,33 +307,33 @@ class RadioPacket(Packet): learn = True contains_eep = False - def __str__(self): + def __str__(self) -> str: packet_str = super(RadioPacket, self).__str__() - return '%s->%s (%d dBm): %s' % (self.sender_hex, self.destination_hex, self.dBm, packet_str) + return f'{self.sender_hex}->{self.destination_hex} ({self.dBm} dBm): {packet_str}' @staticmethod - def create(rorg, rorg_func, rorg_type, direction=None, command=None, - destination=None, sender=None, learn=False, **kwargs): + def create(rorg: RORG, rorg_func: int, rorg_type: int, direction=None, command: None | int = None, + destination: None | list = None, sender: None | list = None, learn: bool = False, **kwargs) -> Packet: return Packet.create(PACKET.RADIO_ERP1, rorg, rorg_func, rorg_type, direction, command, destination, sender, learn, **kwargs) @property - def sender_int(self): + def sender_int(self) -> int: return combine_hex(self.sender) @property - def sender_hex(self): + def sender_hex(self) -> str: return to_hex_string(self.sender) @property - def destination_int(self): + def destination_int(self) -> int: return combine_hex(self.destination) @property - def destination_hex(self): + def destination_hex(self) -> str: return to_hex_string(self.destination) - def parse(self): + def parse(self) -> OrderedDict: self.destination = self.optional[1:5] self.dBm = -self.optional[5] self.sender = self.data[-5:-1] @@ -346,8 +354,10 @@ def parse(self): self.rorg_func = from_bitarray(self._bit_data[DB3.BIT_7:DB3.BIT_1]) self.rorg_type = from_bitarray(self._bit_data[DB3.BIT_1:DB2.BIT_2]) self.rorg_manufacturer = from_bitarray(self._bit_data[DB2.BIT_2:DB0.BIT_7]) - self.logger.debug('learn received, EEP detected, RORG: 0x%02X, FUNC: 0x%02X, TYPE: 0x%02X, Manufacturer: 0x%02X' % (self.rorg, self.rorg_func, self.rorg_type, self.rorg_manufacturer)) # noqa: E501 - + self.logger.debug( + f'learn received, EEP detected, RORG: 0x{self.rorg:X}, FUNC: 0x{self.rorg_func:X}, ' + f'TYPE: 0x{self.rorg_type:X}, Manufacturer: 0x{self.rorg_manufacturer:X}' + ) return super(RadioPacket, self).parse() @@ -373,23 +383,24 @@ class UTETeachInPacket(RadioPacket): contains_eep = True @property - def bidirectional(self): + def bidirectional(self) -> bool: return not self.unidirectional @property - def teach_in(self): + def teach_in(self) -> bool: return self.request_type != self.DELETE @property - def delete(self): + def delete(self) -> bool: return self.request_type == self.DELETE - def parse(self): + def parse(self) -> OrderedDict: super(UTETeachInPacket, self).parse() self.unidirectional = not self._bit_data[DB6.BIT_7] self.response_expected = not self._bit_data[DB6.BIT_6] self.request_type = from_bitarray(self._bit_data[DB6.BIT_5:DB6.BIT_3]) - self.rorg_manufacturer = from_bitarray(self._bit_data[DB3.BIT_2:DB2.BIT_7] + self._bit_data[DB4.BIT_7:DB3.BIT_7]) # noqa: E501 + self.rorg_manufacturer = from_bitarray( + self._bit_data[DB3.BIT_2:DB2.BIT_7] + self._bit_data[DB4.BIT_7:DB3.BIT_7]) # noqa: E501 self.channel = self.data[2] self.rorg_type = self.data[5] self.rorg_func = self.data[6] @@ -398,16 +409,21 @@ def parse(self): self.learn = True return self.parsed - def create_response_packet(self, sender_id, response=TEACHIN_ACCEPTED): + def create_response_packet(self, sender_id: list, response: None | list = None) -> RadioPacket: + if response is None: + response = self.TEACHIN_ACCEPTED + # Create data: # - Respond with same RORG (UTE Teach-in) # - Always use bidirectional communication, set response code, set command identifier. # - Databytes 5 to 0 are copied from the original message # - Set sender id and status - data = [self.rorg] + \ - [from_bitarray([True, False] + response + [False, False, False, True])] + \ - self.data[2:8] + \ - sender_id + [0] + data = ( + [self.rorg] + + [from_bitarray([True, False] + response + [False, False, False, True])] + + self.data[2:8] + + sender_id + [0] + ) # Always use 0x03 to indicate sending, attach sender ID, dBm, and security level optional = [0x03] + self.sender + [0xFF, 0x00] @@ -419,7 +435,7 @@ class ResponsePacket(Packet): response = 0 response_data = [] - def parse(self): + def parse(self) -> OrderedDict: self.response = self.data[0] self.response_data = self.data[1:] return super(ResponsePacket, self).parse() @@ -429,7 +445,7 @@ class EventPacket(Packet): event = 0 event_data = [] - def parse(self): + def parse(self) -> OrderedDict: self.event = self.data[0] self.event_data = self.data[1:] return super(EventPacket, self).parse() diff --git a/enocean/utils.py b/enocean/utils.py index e704034..19da0f6 100644 --- a/enocean/utils.py +++ b/enocean/utils.py @@ -1,38 +1,38 @@ # -*- encoding: utf-8 -*- -def get_bit(byte, bit): - ''' Get bit value from byte ''' +def get_bit(byte: int, bit: int) -> int: + """ Get bit value from byte """ return (byte >> bit) & 0x01 -def combine_hex(data): - ''' Combine list of integer values to one big integer ''' +def combine_hex(data: list | bytearray) -> int: + """ Combine list of integer values to one big integer """ output = 0x00 for i, value in enumerate(reversed(data)): output |= (value << i * 8) return output -def to_bitarray(data, width=8): - ''' Convert data (list of integers, bytearray or integer) to bitarray ''' +def to_bitarray(data: list | bytearray, width=8) -> list[bool]: + """ Convert data (list of integers, bytearray or integer) to bitarray """ if isinstance(data, list) or isinstance(data, bytearray): data = combine_hex(data) return [True if digit == '1' else False for digit in bin(data)[2:].zfill(width)] -def from_bitarray(data): - ''' Convert bit array back to integer ''' +def from_bitarray(data: list[bool]) -> int: + """ Convert bit array back to integer """ return int(''.join(['1' if x else '0' for x in data]), 2) -def to_hex_string(data): - ''' Convert list of integers to a hex string, separated by ":" ''' +def to_hex_string(data: list | int) -> str: + """ Convert list of integers to a hex string, separated by ":" """ if isinstance(data, int): - return '%02X' % data - return ':'.join([('%02X' % o) for o in data]) + return f'{data:02X}' + return ':'.join([f'{o:02X}' for o in data]) -def from_hex_string(hex_string): +def from_hex_string(hex_string: str) -> int | list: reval = [int(x, 16) for x in hex_string.split(':')] if len(reval) == 1: return reval[0] diff --git a/generate_supported_profiles.py b/generate_supported_profiles.py index 07413f6..5ac4e85 100755 --- a/generate_supported_profiles.py +++ b/generate_supported_profiles.py @@ -15,7 +15,7 @@ write("# Supported profiles\n") write( "All profiles (should) correspond to the official" - " [EEP](http://www.enocean-alliance.org/eep/) by EnOcean.\n\n" + " [EEP](https://www.enocean-alliance.org/eep/) by EnOcean.\n\n" ) # table of contents @@ -23,11 +23,11 @@ write("
") write(f" {telegram['description']} ({telegram['rorg']}) \n\n") for func in telegram.find_all('profiles'): - # f_handle.write('##### FUNC %s - %s\n' % (func['func'], func['description'])) for profile in func.find_all('profile'): write( f"- [FUNC {func['func']} - TYPE {profile['type']} - {profile['description']}]" - f"({BASE_URL}rorg-{telegram['rorg']}---func-{func['func']}---type-{profile['type']}---{profile['description'].lower().replace(' ', '-')})\n" + f"({BASE_URL}rorg-{telegram['rorg']}---func-{func['func']}---type-{profile['type']}-" + f"--{profile['description'].lower().replace(' ', '-')})\n" ) write("\n
\n\n") @@ -35,25 +35,26 @@ # contents for telegram in eep.soup.find_all('telegram'): - write('### %s (%s)\n\n' % (telegram['description'], telegram['rorg'])) + write(f'### {telegram['description']} ({telegram['rorg']})\n\n') for func in telegram.find_all('profiles'): - # f_handle.write('##### FUNC %s - %s\n' % (func['func'], func['description'])) for profile in func.find_all('profile'): - write('##### RORG %s - FUNC %s - TYPE %s - %s\n\n' % (telegram['rorg'], func['func'], profile['type'], profile['description'])) + write( + f'##### RORG {telegram['rorg']} - FUNC {func['func']}' + f' - TYPE {profile['type']} - {profile['description']}\n\n' + ) for data in profile.find_all('data'): header = [] if data.get('direction'): - header.append('direction: %s' % (data.get('direction'))) + header.append(f'direction: {data.get('direction')}') if data.get('command'): - header.append('command: %s' % (data.get('command'))) - + header.append(f'command: {data.get('command')}') if header: - write('###### %s\n' % ' '.join(header)) + write(f"###### {' '.join(header)}\n") write(ROW_FORMAT.format('shortcut', 'description', 'type', 'values')) - write(ROW_FORMAT.format('--------', '--------------------------------------------------', '--------', '----')) + write(ROW_FORMAT.format('-'*8, '-'*50, '-'*8, '-'*4)) for child in data.children: if child.name is None: continue @@ -64,9 +65,9 @@ continue if item.name == 'rangeitem': - values.append('%s-%s - %s' % (item['start'], item['end'], item['description'])) + values.append(f'{item['start']}-{item['end']} - {item['description']}') elif item.name == 'item': - values.append('%s - %s' % (item['value'], item['description'])) + values.append(f'{item['value']} - {item['description']}') elif item.name == 'range': parent = item.parent @@ -76,7 +77,7 @@ scale_min = float(scale.find('min').text) scale_max = float(scale.find('max').text) - values.append('%s-%s ↔ %s-%s %s' % (range_min, range_max, scale_min, scale_max, parent['unit'])) + values.append(f'{range_min}-{range_max} ↔ {scale_min}-{scale_max} {parent['unit']}') if not values: write(ROW_FORMAT.format(child['shortcut'], child['description'], child.name, '')) continue diff --git a/tests/test_protocol_eep.py b/tests/test_protocol_eep.py index f0f8644..3f08b1d 100644 --- a/tests/test_protocol_eep.py +++ b/tests/test_protocol_eep.py @@ -8,7 +8,7 @@ @timing(1000) def test_temperature(): - ''' Tests RADIO message for EEP -profile 0xA5 0x02 0x05 ''' + """ Tests RADIO message for EEP -profile 0xA5 0x02 0x05 """ status, buf, packet = Packet.parse_msg(bytearray([ 0x55, 0x00, 0x0A, 0x07, 0x01, @@ -34,7 +34,7 @@ def test_temperature(): @timing(1000) def test_magnetic_switch(): - ''' Tests RADIO message for EEP -profile 0xD5 0x00 0x01 ''' + """ Tests RADIO message for EEP -profile 0xD5 0x00 0x01 """ status, buf, packet = Packet.parse_msg(bytearray([ 0x55, 0x00, 0x07, 0x07, 0x01, @@ -244,16 +244,16 @@ def test_fails(): eep = EEP() # Mock initialization failure eep.init_ok = False - assert eep.find_profile(packet._bit_data, 0xD5, 0x00, 0x01) is None + assert eep.find_profile(0xD5, 0x00, 0x01) is None # TODO: Needs better test. A much better. assert eep.set_values(profile=None, data=[True], status=[False, False], properties={'CV': False}) eep.init_ok = True - profile = eep.find_profile(packet._bit_data, 0xD5, 0x00, 0x01) + profile = eep.find_profile(0xD5, 0x00, 0x01) assert eep.set_values(profile, packet._bit_data, packet.status, {'ASD': 1}) - assert eep.find_profile(packet._bit_data, 0xFF, 0x00, 0x01) is None - assert eep.find_profile(packet._bit_data, 0xD5, 0xFF, 0x01) is None - assert eep.find_profile(packet._bit_data, 0xD5, 0x00, 0xFF) is None + assert eep.find_profile(0xFF, 0x00, 0x01) is None + assert eep.find_profile(0xD5, 0xFF, 0x01) is None + assert eep.find_profile(0xD5, 0x00, 0xFF) is None status, buf, packet = Packet.parse_msg(bytearray([ 0x55, @@ -263,5 +263,5 @@ def test_fails(): 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x40, 0x00, 0xBF ])) - assert eep.find_profile(packet._bit_data, 0xD2, 0x01, 0x01) is not None - assert eep.find_profile(packet._bit_data, 0xD2, 0x01, 0x01, command=-1) is None + assert eep.find_profile(0xD2, 0x01, 0x01) is not None + assert eep.find_profile(0xD2, 0x01, 0x01, command=-1) is None diff --git a/tests/test_protocol_temperature_sensors.py b/tests/test_protocol_temperature_sensors.py index 9ae2919..70991d9 100644 --- a/tests/test_protocol_temperature_sensors.py +++ b/tests/test_protocol_temperature_sensors.py @@ -12,7 +12,7 @@ def test_first_range(): for i in range(len(values)): minimum = float(i * 10 + offset) maximum = minimum + 40 - profile = eep.find_profile([], 0xA5, 0x02, values[i]) + profile = eep.find_profile(0xA5, 0x02, values[i]) assert minimum == float(profile.find('value', {'shortcut': 'TMP'}).find('scale').find('min').text) assert maximum == float(profile.find('value', {'shortcut': 'TMP'}).find('scale').find('max').text) @@ -24,17 +24,17 @@ def test_second_range(): for i in range(len(values)): minimum = float(i * 10 + offset) maximum = minimum + 80 - profile = eep.find_profile([], 0xA5, 0x02, values[i]) + profile = eep.find_profile(0xA5, 0x02, values[i]) assert minimum == float(profile.find('value', {'shortcut': 'TMP'}).find('scale').find('min').text) assert maximum == float(profile.find('value', {'shortcut': 'TMP'}).find('scale').find('max').text) def test_rest(): - profile = eep.find_profile([], 0xA5, 0x02, 0x20) + profile = eep.find_profile(0xA5, 0x02, 0x20) assert -10 == float(profile.find('value', {'shortcut': 'TMP'}).find('scale').find('min').text) assert +41.2 == float(profile.find('value', {'shortcut': 'TMP'}).find('scale').find('max').text) - profile = eep.find_profile([], 0xA5, 0x02, 0x30) + profile = eep.find_profile(0xA5, 0x02, 0x30) assert -40 == float(profile.find('value', {'shortcut': 'TMP'}).find('scale').find('min').text) assert +62.3 == float(profile.find('value', {'shortcut': 'TMP'}).find('scale').find('max').text) From d4028255264c71229401e308f7ffe5a5bb1152e4 Mon Sep 17 00:00:00 2001 From: topic2k Date: Sun, 1 Sep 2024 18:02:57 +0200 Subject: [PATCH 24/40] update version --- enocean/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/enocean/__init__.py b/enocean/__init__.py index e23d576..a7393a0 100644 --- a/enocean/__init__.py +++ b/enocean/__init__.py @@ -1,3 +1,3 @@ # -*- encoding: utf-8 -*- -__version__ = '1.1.1' +__version__ = '1.2.1' From a6730bc37fff23c7704bb039657fc4623fec8d29 Mon Sep 17 00:00:00 2001 From: topic2k Date: Sun, 1 Sep 2024 18:10:22 +0200 Subject: [PATCH 25/40] fix f-string quotation marks --- generate_supported_profiles.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/generate_supported_profiles.py b/generate_supported_profiles.py index 5ac4e85..353feb5 100755 --- a/generate_supported_profiles.py +++ b/generate_supported_profiles.py @@ -35,21 +35,21 @@ # contents for telegram in eep.soup.find_all('telegram'): - write(f'### {telegram['description']} ({telegram['rorg']})\n\n') + write(f"### {telegram['description']} ({telegram['rorg']})\n\n") for func in telegram.find_all('profiles'): for profile in func.find_all('profile'): write( - f'##### RORG {telegram['rorg']} - FUNC {func['func']}' - f' - TYPE {profile['type']} - {profile['description']}\n\n' + f"##### RORG {telegram['rorg']} - FUNC {func['func']}" + f" - TYPE {profile['type']} - {profile['description']}\n\n" ) for data in profile.find_all('data'): header = [] if data.get('direction'): - header.append(f'direction: {data.get('direction')}') + header.append(f"direction: {data.get('direction')}") if data.get('command'): - header.append(f'command: {data.get('command')}') + header.append(f"command: {data.get('command')}") if header: write(f"###### {' '.join(header)}\n") @@ -65,9 +65,9 @@ continue if item.name == 'rangeitem': - values.append(f'{item['start']}-{item['end']} - {item['description']}') + values.append(f"{item['start']}-{item['end']} - {item['description']}") elif item.name == 'item': - values.append(f'{item['value']} - {item['description']}') + values.append(f"{item['value']} - {item['description']}") elif item.name == 'range': parent = item.parent @@ -77,7 +77,7 @@ scale_min = float(scale.find('min').text) scale_max = float(scale.find('max').text) - values.append(f'{range_min}-{range_max} ↔ {scale_min}-{scale_max} {parent['unit']}') + values.append(f"{range_min}-{range_max} ↔ {scale_min}-{scale_max} {parent['unit']}") if not values: write(ROW_FORMAT.format(child['shortcut'], child['description'], child.name, '')) continue From 2ea81483ba0cdac5fa61d639bb7857ecb11b6730 Mon Sep 17 00:00:00 2001 From: topic2k Date: Sun, 1 Sep 2024 18:26:44 +0200 Subject: [PATCH 26/40] fix TypeErrors --- .github/workflows/lint_and_test.yml | 1 + enocean/communicators/communicator.py | 7 ++++--- enocean/protocol/eep.py | 8 +++++--- enocean/protocol/packet.py | 12 ++++++------ enocean/utils.py | 10 ++++++---- 5 files changed, 22 insertions(+), 16 deletions(-) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index eb7e66d..a92b370 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -14,6 +14,7 @@ jobs: runs-on: ubuntu-latest strategy: + fail-fast: false matrix: python-version: ['3.9', '3.10','3.11', '3.12', '3.13.0-rc.1'] diff --git a/enocean/communicators/communicator.py b/enocean/communicators/communicator.py index da8f116..63975dc 100644 --- a/enocean/communicators/communicator.py +++ b/enocean/communicators/communicator.py @@ -4,6 +4,7 @@ import logging import queue import threading +from typing import Union from ..protocol.constants import COMMON_COMMAND, PACKET, PARSE_RESULT, RETURN_CODE from ..protocol.packet import Packet, UTETeachInPacket @@ -33,7 +34,7 @@ def __init__(self, callback: callable = None, teach_in: bool = True) -> None: # TODO: Not sure if we should use CO_WR_LEARNMODE?? self.teach_in = teach_in - def _get_from_send_queue(self) -> Packet | None: + def _get_from_send_queue(self) -> Union[Packet, None]: """ Get message from send queue, if one exists """ try: packet = self.transmit.get(block=False) @@ -54,7 +55,7 @@ def send(self, packet: Packet) -> bool: def stop(self) -> None: self._stop_flag.set() - def parse(self) -> None | PARSE_RESULT: + def parse(self) -> Union[None, PARSE_RESULT]: """ Parses messages and puts them to receive queue """ # Loop while we get new messages while True: @@ -79,7 +80,7 @@ def parse(self) -> None | PARSE_RESULT: self.logger.debug(packet) @property - def base_id(self) -> None | list[int, int, int, int]: + def base_id(self) -> Union[None, list[int, int, int, int]]: """ Fetches Base ID from the transmitter, if required. Otherwise returns the currently set Base ID. """ # If base id is already set, return it. if self._base_id is not None: diff --git a/enocean/protocol/eep.py b/enocean/protocol/eep.py index dc80f2f..27107bb 100644 --- a/enocean/protocol/eep.py +++ b/enocean/protocol/eep.py @@ -3,6 +3,7 @@ import logging from collections import OrderedDict from importlib.resources import files +from typing import Union from bs4 import BeautifulSoup, Tag @@ -111,7 +112,7 @@ def _get_boolean(self, source: Tag, bitarray: list) -> dict: } } - def _set_value(self, target: Tag, value: int | float, bitarray: list) -> list: + def _set_value(self, target: Tag, value: Union[int, float], bitarray: list) -> list: """ set given numeric value to target field in bitarray """ # derive raw value rng = target.find('range') @@ -124,7 +125,7 @@ def _set_value(self, target: Tag, value: int | float, bitarray: list) -> list: # store value in bitfield return self._set_raw(target, int(raw_value), bitarray) - def _set_enum(self, target: Tag, value: int | str, bitarray: list) -> list: + def _set_enum(self, target: Tag, value: Union[int, str], bitarray: list) -> list: """ set given enum value (by string or integer value) to target field in bitarray """ # derive raw value if isinstance(value, int): @@ -147,7 +148,8 @@ def _set_boolean(target: Tag, data: bool, bitarray: list) -> list: bitarray[int(target['offset'])] = data return bitarray - def find_profile(self, eep_rorg: int, rorg_func: int, rorg_type: int, direction=None, command=None) -> None | Tag: + def find_profile(self, eep_rorg: int, rorg_func: int, rorg_type: int, + direction=None, command=None) -> Union[None, Tag]: """ Find profile and data description, matching RORG, FUNC and TYPE """ if not self.init_ok: self.logger.warning('EEP.xml not loaded!') diff --git a/enocean/protocol/packet.py b/enocean/protocol/packet.py index f5167ea..c001831 100644 --- a/enocean/protocol/packet.py +++ b/enocean/protocol/packet.py @@ -2,7 +2,7 @@ import logging from collections import OrderedDict -from typing import Any +from typing import Any, Union from .crc8 import calc from .constants import PACKET, RORG, PARSE_RESULT, DB0, DB2, DB3, DB4, DB6 @@ -20,7 +20,7 @@ class Packet: eep = EEP() logger = logging.getLogger('enocean.protocol.packet') - def __init__(self, packet_type: PACKET, data: None | list = None, optional: None | list = None) -> None: + def __init__(self, packet_type: PACKET, data: Union[None, list] = None, optional: Union[None, list] = None) -> None: self.packet_type = packet_type self.rorg = RORG.UNDEFINED self.rorg_func = None @@ -98,7 +98,7 @@ def _bit_status(self, value: list[bool]) -> None: self.status = from_bitarray(value) @staticmethod - def parse_msg(buf: list | bytearray) -> (PARSE_RESULT, list, Any): + def parse_msg(buf: Union[list, bytearray]) -> (PARSE_RESULT, list, Any): # 'Any' in return type should be [None | UTETeachInPacket | ResponsePacket | EventPacket | Packet] # how to realize that? """ @@ -168,9 +168,9 @@ def parse_msg(buf: list | bytearray) -> (PARSE_RESULT, list, Any): def create( packet_type: PACKET, rorg: RORG, rorg_func: int, rorg_type: int, direction=None, - command: None | int = None, - destination: None | list = None, - sender: None | list = None, + command: Union[None, int] = None, + destination: Union[None, list] = None, + sender: Union[None, list] = None, learn: bool = False, **kwargs ): diff --git a/enocean/utils.py b/enocean/utils.py index 19da0f6..e4edfb1 100644 --- a/enocean/utils.py +++ b/enocean/utils.py @@ -1,11 +1,13 @@ # -*- encoding: utf-8 -*- +from typing import Union + def get_bit(byte: int, bit: int) -> int: """ Get bit value from byte """ return (byte >> bit) & 0x01 -def combine_hex(data: list | bytearray) -> int: +def combine_hex(data: Union[list, bytearray]) -> int: """ Combine list of integer values to one big integer """ output = 0x00 for i, value in enumerate(reversed(data)): @@ -13,7 +15,7 @@ def combine_hex(data: list | bytearray) -> int: return output -def to_bitarray(data: list | bytearray, width=8) -> list[bool]: +def to_bitarray(data: Union[list, bytearray], width=8) -> list[bool]: """ Convert data (list of integers, bytearray or integer) to bitarray """ if isinstance(data, list) or isinstance(data, bytearray): data = combine_hex(data) @@ -25,14 +27,14 @@ def from_bitarray(data: list[bool]) -> int: return int(''.join(['1' if x else '0' for x in data]), 2) -def to_hex_string(data: list | int) -> str: +def to_hex_string(data: Union[list, int]) -> str: """ Convert list of integers to a hex string, separated by ":" """ if isinstance(data, int): return f'{data:02X}' return ':'.join([f'{o:02X}' for o in data]) -def from_hex_string(hex_string: str) -> int | list: +def from_hex_string(hex_string: str) -> Union[int, list]: reval = [int(x, 16) for x in hex_string.split(':')] if len(reval) == 1: return reval[0] From 5e8764e3a05139a08e975bccd56d8c292af2a60d Mon Sep 17 00:00:00 2001 From: topic2k Date: Sun, 1 Sep 2024 18:30:46 +0200 Subject: [PATCH 27/40] fix TypeErrors --- enocean/protocol/packet.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/enocean/protocol/packet.py b/enocean/protocol/packet.py index c001831..3c6d96b 100644 --- a/enocean/protocol/packet.py +++ b/enocean/protocol/packet.py @@ -99,7 +99,7 @@ def _bit_status(self, value: list[bool]) -> None: @staticmethod def parse_msg(buf: Union[list, bytearray]) -> (PARSE_RESULT, list, Any): - # 'Any' in return type should be [None | UTETeachInPacket | ResponsePacket | EventPacket | Packet] + # 'Any' in return type should be Union[None | UTETeachInPacket | ResponsePacket | EventPacket | Packet] # how to realize that? """ Parses message from buffer. @@ -265,7 +265,7 @@ def parse(self) -> OrderedDict: self.repeater_count = from_bitarray(self._bit_status[4:]) return self.parsed - def select_eep(self, rorg_func: int, rorg_type: int, direction=None, command: None | int = None) -> bool: + def select_eep(self, rorg_func: int, rorg_type: int, direction=None, command: Union[None, int] = None) -> bool: """ Set EEP based on FUNC and TYPE """ # set EEP profile self.rorg_func = rorg_func @@ -273,8 +273,8 @@ def select_eep(self, rorg_func: int, rorg_type: int, direction=None, command: No self._profile = self.eep.find_profile(self.rorg, rorg_func, rorg_type, direction, command) return self._profile is not None - def parse_eep(self, rorg_func: None | int = None, rorg_type: None | int = None, - direction=None, command: None | int = None) -> list: + def parse_eep(self, rorg_func: Union[None, int] = None, rorg_type: Union[None, int] = None, + direction=None, command: Union[None, int] = None) -> list: """ Parse EEP based on FUNC and TYPE """ # set EEP profile, if demanded if rorg_func is not None and rorg_type is not None: @@ -312,8 +312,9 @@ def __str__(self) -> str: return f'{self.sender_hex}->{self.destination_hex} ({self.dBm} dBm): {packet_str}' @staticmethod - def create(rorg: RORG, rorg_func: int, rorg_type: int, direction=None, command: None | int = None, - destination: None | list = None, sender: None | list = None, learn: bool = False, **kwargs) -> Packet: + def create(rorg: RORG, rorg_func: int, rorg_type: int, direction=None, + command: Union[None, int] = None, destination: Union[None, list] = None, + sender: Union[None, list] = None, learn: bool = False, **kwargs) -> Packet: return Packet.create(PACKET.RADIO_ERP1, rorg, rorg_func, rorg_type, direction, command, destination, sender, learn, **kwargs) @@ -409,7 +410,7 @@ def parse(self) -> OrderedDict: self.learn = True return self.parsed - def create_response_packet(self, sender_id: list, response: None | list = None) -> RadioPacket: + def create_response_packet(self, sender_id: list, response: Union[None, list] = None) -> RadioPacket: if response is None: response = self.TEACHIN_ACCEPTED From b6e4d28381d0b1b05d63fa0d6614b9ed529cea5d Mon Sep 17 00:00:00 2001 From: topic2k Date: Sun, 1 Sep 2024 19:39:15 +0200 Subject: [PATCH 28/40] fix EEP.xml --- SUPPORTED_PROFILES.md | 7 +++++++ enocean/protocol/EEP.xml | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/SUPPORTED_PROFILES.md b/SUPPORTED_PROFILES.md index fe7027b..e25b59b 100644 --- a/SUPPORTED_PROFILES.md +++ b/SUPPORTED_PROFILES.md @@ -54,6 +54,7 @@ All profiles (should) correspond to the official [EEP](https://www.enocean-allia - [FUNC 0x08 - TYPE 0x01 - Range 0lx to 510lx, 0°C to +51°C and Occupancy Button](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x08---type-0x01---range-0lx-to-510lx,-0°c-to-+51°c-and-occupancy-button) - [FUNC 0x09 - TYPE 0x04 - CO2 Sensor](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x09---type-0x04---co2-sensor) - [FUNC 0x09 - TYPE 0x05 - VOC Sensor](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x09---type-0x05---voc-sensor) +- [FUNC 0x09 - TYPE 0x09 - Gas Sensor](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x09---type-0x09---gas-sensor) - [FUNC 0x10 - TYPE 0x03 - Temperature Sensor and Set Point](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x10---type-0x03---temperature-sensor-and-set-point) - [FUNC 0x10 - TYPE 0x05 - Temperature Sensor, Set Point and Occupancy Control](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x10---type-0x05---temperature-sensor,-set-point-and-occupancy-control) - [FUNC 0x10 - TYPE 0x06 - Temperature Sensor, Set Point and Day/Night Control](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x10---type-0x06---temperature-sensor,-set-point-and-day/night-control) @@ -438,6 +439,12 @@ All profiles (should) correspond to the official [EEP](https://www.enocean-allia | | | |26 - Diethyl ether | | | | |255 - ozone | +##### RORG 0xA5 - FUNC 0x09 - TYPE 0x09 - Gas Sensor + +|shortcut|description |type |values | +|--------|--------------------------------------------------|--------|---- | +|CO2 |CO2 Measurement |value |0.0-255.0 ↔ 0.0-2000.0 ppm | + ##### RORG 0xA5 - FUNC 0x10 - TYPE 0x03 - Temperature Sensor and Set Point diff --git a/enocean/protocol/EEP.xml b/enocean/protocol/EEP.xml index d2c304e..82d7d29 100644 --- a/enocean/protocol/EEP.xml +++ b/enocean/protocol/EEP.xml @@ -775,7 +775,7 @@ - + 0 255 From a9e5924f30437da2fdad24674561d7c4a225e61d Mon Sep 17 00:00:00 2001 From: topic2k Date: Mon, 2 Sep 2024 15:45:44 +0200 Subject: [PATCH 29/40] prepare release --- .github/workflows/python-publish.yml | 7 ++++--- enocean/__init__.py | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index cab7d30..7b8e5b1 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -13,7 +13,8 @@ jobs: runs-on: ubuntu-latest environment: name: testpypi - url: https://test.pypi.org/p/enocean4ha + # url: https://test.pypi.org/p/enocean4ha + url: https://pypi.org/p/enocean4ha permissions: id-token: write # IMPORTANT: this permission is mandatory for trusted publishing steps: @@ -28,5 +29,5 @@ jobs: python -m build - name: Publish package distributions to PyPI uses: pypa/gh-action-pypi-publish@release/v1 - with: - repository-url: https://test.pypi.org/legacy/ + # with: + # repository-url: https://test.pypi.org/legacy/ diff --git a/enocean/__init__.py b/enocean/__init__.py index a7393a0..45eebd7 100644 --- a/enocean/__init__.py +++ b/enocean/__init__.py @@ -1,3 +1,3 @@ # -*- encoding: utf-8 -*- -__version__ = '1.2.1' +__version__ = '0.7.0' From 86897ac019669994c9fe89123005d28fbb45aa66 Mon Sep 17 00:00:00 2001 From: topic2k Date: Mon, 2 Sep 2024 15:48:23 +0200 Subject: [PATCH 30/40] rename branches --- .github/workflows/lint_and_test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index a92b370..2884ccf 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -5,9 +5,9 @@ name: Linting and testing on: push: - branches: [ topix ] + branches: [ main ] pull_request: - branches: [ topix ] + branches: [ main ] jobs: test: From 35afb6e015e4f7ba92a75d190c046bff8fe409ba Mon Sep 17 00:00:00 2001 From: topic2k Date: Mon, 2 Sep 2024 15:53:02 +0200 Subject: [PATCH 31/40] prepare release --- enocean/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/enocean/__init__.py b/enocean/__init__.py index 45eebd7..68f1f32 100644 --- a/enocean/__init__.py +++ b/enocean/__init__.py @@ -1,3 +1,3 @@ # -*- encoding: utf-8 -*- -__version__ = '0.7.0' +__version__ = '0.70.0' From 2975f28ce8bd21d90dd2b2e222db55230e3e769a Mon Sep 17 00:00:00 2001 From: topic2k Date: Mon, 2 Sep 2024 15:55:20 +0200 Subject: [PATCH 32/40] rename workflow environment --- .github/workflows/python-publish.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 7b8e5b1..1356184 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -12,9 +12,10 @@ jobs: name: Upload release to PyPI runs-on: ubuntu-latest environment: - name: testpypi - # url: https://test.pypi.org/p/enocean4ha + name: pypi url: https://pypi.org/p/enocean4ha + # name: testpypi + # url: https://test.pypi.org/p/enocean4ha permissions: id-token: write # IMPORTANT: this permission is mandatory for trusted publishing steps: From 2cc16fb45fd8b10edb3fee7456c65b170fe8ce37 Mon Sep 17 00:00:00 2001 From: topic2k Date: Mon, 2 Sep 2024 16:29:59 +0200 Subject: [PATCH 33/40] add verbosity to pypi upload --- .github/workflows/python-publish.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 1356184..cdf6606 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -30,5 +30,6 @@ jobs: python -m build - name: Publish package distributions to PyPI uses: pypa/gh-action-pypi-publish@release/v1 - # with: + with: + verbose: true # repository-url: https://test.pypi.org/legacy/ From f90ed61a1af510475c1075cb08827b4b89afc076 Mon Sep 17 00:00:00 2001 From: topic2k Date: Mon, 2 Sep 2024 16:44:59 +0200 Subject: [PATCH 34/40] upload to testpypi --- .github/workflows/python-publish.yml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index cdf6606..2103950 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -9,13 +9,14 @@ on: jobs: testpypi-publish: - name: Upload release to PyPI + # name: Upload release to PyPI + name: Upload release to TestPyPI runs-on: ubuntu-latest environment: - name: pypi - url: https://pypi.org/p/enocean4ha - # name: testpypi - # url: https://test.pypi.org/p/enocean4ha + # name: pypi + # url: https://pypi.org/p/enocean4ha + name: testpypi + url: https://test.pypi.org/p/enocean4ha permissions: id-token: write # IMPORTANT: this permission is mandatory for trusted publishing steps: @@ -32,4 +33,4 @@ jobs: uses: pypa/gh-action-pypi-publish@release/v1 with: verbose: true - # repository-url: https://test.pypi.org/legacy/ + repository-url: https://test.pypi.org/legacy/ From 157511a71920dd9091a233cd293e9546f49a8354 Mon Sep 17 00:00:00 2001 From: topic2k Date: Mon, 2 Sep 2024 16:52:28 +0200 Subject: [PATCH 35/40] try again to upload to pypi --- .github/workflows/python-publish.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 2103950..5402bda 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -9,14 +9,14 @@ on: jobs: testpypi-publish: - # name: Upload release to PyPI - name: Upload release to TestPyPI + name: Upload release to PyPI + # name: Upload release to TestPyPI runs-on: ubuntu-latest environment: - # name: pypi - # url: https://pypi.org/p/enocean4ha - name: testpypi - url: https://test.pypi.org/p/enocean4ha + name: pypi + url: https://pypi.org/p/enocean4ha + # name: testpypi + # url: https://test.pypi.org/p/enocean4ha permissions: id-token: write # IMPORTANT: this permission is mandatory for trusted publishing steps: @@ -31,6 +31,6 @@ jobs: python -m build - name: Publish package distributions to PyPI uses: pypa/gh-action-pypi-publish@release/v1 - with: - verbose: true - repository-url: https://test.pypi.org/legacy/ + # with: + # verbose: true + # repository-url: https://test.pypi.org/legacy/ From 64f1e3f1a63451beab69841f42ff63be36cee1ec Mon Sep 17 00:00:00 2001 From: topic2k Date: Mon, 2 Sep 2024 16:56:14 +0200 Subject: [PATCH 36/40] Update badges in README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 27ecc9b..7c02bb4 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # Python EnOcean # -[![Linting And Testing Status](https://github.com/topic2k/enocean4ha/actions/workflows/lint_and_test.yml/badge.svg?branch=topix)](https://github.com/topic2k/enocean4ha/actions/workflows/lint_and_test.yml) -[![Coverage Status](https://coveralls.io/repos/github/topic2k/enocean4ha/badge.svg?branch=topix)](https://coveralls.io/github/topic2k/enocean4ha?branch=topix) -[![PyPi](https://img.shields.io/pypi/v/wxpython?logo=pypi&logoColor=959DA5)](https://test.pypi.org/project/enocean4ha/) +[![Linting And Testing Status](https://github.com/topic2k/enocean4ha/actions/workflows/lint_and_test.yml/badge.svg?branch=main)](https://github.com/topic2k/enocean4ha/actions/workflows/lint_and_test.yml) +[![Coverage Status](https://coveralls.io/repos/github/topic2k/enocean4ha/badge.svg?branch=main)](https://coveralls.io/github/topic2k/enocean4ha?branch=main) +[![PyPi](https://img.shields.io/pypi/v/enocean4ha?logo=pypi&logoColor=959DA5)](https://pypi.org/project/enocean4ha/) A Python library for reading and controlling [EnOcean](http://www.enocean.com/) devices. From 2fadc6ce6db29ee9789b282064320c925ad9b684 Mon Sep 17 00:00:00 2001 From: topic2k Date: Sun, 22 Sep 2024 16:49:53 +0200 Subject: [PATCH 37/40] Added some EEPs and changed/added some logging (#4) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit added: - EEP A5-20-06: HVAC Components,Harvesting-powered actuator with local temperature offset control - EEP D2-01-0E: Electronic switch with Local Control - EEP D2-01-12: Electronic switch with Local Control - EEP A5-04-02: Temperature and Humidity Sensor - Range -20°C to +60°C and 0% to 100% - EEP A5-04-04: Temperature and Humidity Sensor - Range -40°C to +120°C 12bit-measurement and 0% to 100% - EEP A5-08-02: Light, Temperature and Occupancy Sensor - Range 0lx to 1020lx, 0°C to +51°C and Occupancy Button - EEP A5-08-03: Light, Temperature and Occupancy Sensor - Range 0lx to 1020lx, 0°C to +51°C and Occupancy Button --- .gitignore | 1 + SUPPORTED_PROFILES.md | 269 ++++++++++ enocean/__init__.py | 2 +- enocean/communicators/communicator.py | 19 +- enocean/communicators/serialcommunicator.py | 14 +- enocean/communicators/tcpcommunicator.py | 16 +- enocean/protocol/EEP.xml | 553 +++++++++++++++++++- enocean/protocol/constants.py | 2 + enocean/protocol/eep.py | 2 +- generate_supported_profiles.py | 12 +- pyproject.toml | 2 +- 11 files changed, 859 insertions(+), 33 deletions(-) diff --git a/.gitignore b/.gitignore index 2bb1c65..17110e2 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ dist pip-selfcheck.json .vscode/ +.idea/ diff --git a/SUPPORTED_PROFILES.md b/SUPPORTED_PROFILES.md index e25b59b..f92aaf0 100644 --- a/SUPPORTED_PROFILES.md +++ b/SUPPORTED_PROFILES.md @@ -46,12 +46,16 @@ All profiles (should) correspond to the official [EEP](https://www.enocean-allia - [FUNC 0x02 - TYPE 0x20 - 10 Bit Temperature Sensor Range -10°C to +41.2°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x20---10-bit-temperature-sensor-range--10°c-to-+41.2°c) - [FUNC 0x02 - TYPE 0x30 - 10 Bit Temperature Sensor Range -40°C to +62.3°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x30---10-bit-temperature-sensor-range--40°c-to-+62.3°c) - [FUNC 0x04 - TYPE 0x01 - Range 0°C to +40°C and 0% to 100%](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x04---type-0x01---range-0°c-to-+40°c-and-0%-to-100%) +- [FUNC 0x04 - TYPE 0x02 - Range -20°C to +60°C and 0% to 100%](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x04---type-0x02---range--20°c-to-+60°c-and-0%-to-100%) - [FUNC 0x04 - TYPE 0x03 - Range -20°C to +60°C 10bit-measurement and 0% to 100%](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x04---type-0x03---range--20°c-to-+60°c-10bit-measurement-and-0%-to-100%) +- [FUNC 0x04 - TYPE 0x04 - Range -40°C to +120°C 12bit-measurement and 0% to 100%](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x04---type-0x04---range--40°c-to-+120°c-12bit-measurement-and-0%-to-100%) - [FUNC 0x06 - TYPE 0x01 - Range 300lx to 60.000lx](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x06---type-0x01---range-300lx-to-60.000lx) - [FUNC 0x06 - TYPE 0x02 - Range 0lx to 1.020lx](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x06---type-0x02---range-0lx-to-1.020lx) - [FUNC 0x07 - TYPE 0x01 - Occupancy with Supply voltage monitor](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x07---type-0x01---occupancy-with-supply-voltage-monitor) - [FUNC 0x07 - TYPE 0x03 - Occupancy with Supply voltage monitor and 10-bit illumination measurement](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x07---type-0x03---occupancy-with-supply-voltage-monitor-and-10-bit-illumination-measurement) - [FUNC 0x08 - TYPE 0x01 - Range 0lx to 510lx, 0°C to +51°C and Occupancy Button](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x08---type-0x01---range-0lx-to-510lx,-0°c-to-+51°c-and-occupancy-button) +- [FUNC 0x08 - TYPE 0x02 - Range 0lx to 1020lx, 0°C to +51°C and Occupancy Button](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x08---type-0x02---range-0lx-to-1020lx,-0°c-to-+51°c-and-occupancy-button) +- [FUNC 0x08 - TYPE 0x03 - Range 0lx to 1020lx, 0°C to +51°C and Occupancy Button](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x08---type-0x03---range-0lx-to-1020lx,-0°c-to-+51°c-and-occupancy-button) - [FUNC 0x09 - TYPE 0x04 - CO2 Sensor](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x09---type-0x04---co2-sensor) - [FUNC 0x09 - TYPE 0x05 - VOC Sensor](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x09---type-0x05---voc-sensor) - [FUNC 0x09 - TYPE 0x09 - Gas Sensor](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x09---type-0x09---gas-sensor) @@ -65,6 +69,7 @@ All profiles (should) correspond to the official [EEP](https://www.enocean-allia - [FUNC 0x13 - TYPE 0x01 - Weather Station](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x13---type-0x01---weather-station) - [FUNC 0x14 - TYPE 0x01 - Single Input Contact (Window/Door), Supply voltage monitor](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x14---type-0x01---single-input-contact-(window/door),-supply-voltage-monitor) - [FUNC 0x20 - TYPE 0x01 - Battery Powered Actuator (BI-DIR)](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x20---type-0x01---battery-powered-actuator-(bi-dir)) +- [FUNC 0x20 - TYPE 0x06 - HVAC Components,Harvesting-powered actuator with local temperature offset control](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x20---type-0x06---hvac-components,harvesting-powered-actuator-with-local-temperature-offset-control) - [FUNC 0x12 - TYPE 0x01 - Electricity](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x12---type-0x01---electricity) - [FUNC 0x30 - TYPE 0x03 - Digital Inputs, Wake and Temperature](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x30---type-0x03---digital-inputs,-wake-and-temperature) - [FUNC 0x38 - TYPE 0x08 - Gateway](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x38---type-0x08---gateway) @@ -74,7 +79,9 @@ All profiles (should) correspond to the official [EEP](https://www.enocean-allia
VLD Telegram (0xD2) - [FUNC 0x01 - TYPE 0x01 - Electronic switch with Local Control](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xD2---func-0x01---type-0x01---electronic-switch-with-local-control) +- [FUNC 0x01 - TYPE 0x0E - Electronic switch with Local Control](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xD2---func-0x01---type-0x0E---electronic-switch-with-local-control) - [FUNC 0x01 - TYPE 0x0F - Electronic switch with Local Control](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xD2---func-0x01---type-0x0F---electronic-switch-with-local-control) +- [FUNC 0x01 - TYPE 0x12 - Electronic switch with Local Control](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xD2---func-0x01---type-0x12---electronic-switch-with-local-control) - [FUNC 0x05 - TYPE 0x00 - Type 0x00](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xD2---func-0x05---type-0x00---type-0x00) - [FUNC 0x14 - TYPE 0x41 - Indoor -Temperature, Humidity XYZ Acceleration, Illumination Sensor](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xD2---func-0x14---type-0x41---indoor--temperature,-humidity-xyz-acceleration,-illumination-sensor) @@ -336,6 +343,15 @@ All profiles (should) correspond to the official [EEP](https://www.enocean-allia |TSN |Availability of the Temperature Sensor |enum |0 - not available | | | | |1 - available | +##### RORG 0xA5 - FUNC 0x04 - TYPE 0x02 - Range -20°C to +60°C and 0% to 100% + +|shortcut|description |type |values | +|--------|--------------------------------------------------|--------|---- | +|HUM |Rel. Humidity (linear) |value |0.0-250.0 ↔ 0.0-100.0 % | +|TMP |Temperature (linear) |value |0.0-250.0 ↔ -20.0-60.0 °C | +|TSN |Availability of the Temperature Sensor |enum |0 - not available | +| | | |1 - available | + ##### RORG 0xA5 - FUNC 0x04 - TYPE 0x03 - Range -20°C to +60°C 10bit-measurement and 0% to 100% |shortcut|description |type |values | @@ -345,6 +361,13 @@ All profiles (should) correspond to the official [EEP](https://www.enocean-allia |TTP |Telegram Type |enum |0 - Heartbeat | | | | |1 - Event triggered | +##### RORG 0xA5 - FUNC 0x04 - TYPE 0x04 - Range -40°C to +120°C 12bit-measurement and 0% to 100% + +|shortcut|description |type |values | +|--------|--------------------------------------------------|--------|---- | +|HUM |Rel. Humidity (linear) |value |0.0-199.0 ↔ 0.0-100.0 % | +|TMP |Temperature (linear) |value |0.0-1599.0 ↔ -40.0-120.0 °C | + ##### RORG 0xA5 - FUNC 0x06 - TYPE 0x01 - Range 300lx to 60.000lx @@ -397,6 +420,30 @@ All profiles (should) correspond to the official [EEP](https://www.enocean-allia |OCC |Occupancy Button |enum |0 - Button pressed | | | | |1 - Button released | +##### RORG 0xA5 - FUNC 0x08 - TYPE 0x02 - Range 0lx to 1020lx, 0°C to +51°C and Occupancy Button + +|shortcut|description |type |values | +|--------|--------------------------------------------------|--------|---- | +|SVC |Supply voltage (linear) |value |0.0-255.0 ↔ 0.0-5.1 V | +|ILL |Illumination (linear) |value |0.0-255.0 ↔ 0.0-1020.0 lx | +|TMP |Temperature (linear) |value |0.0-255.0 ↔ 0.0-51.0 °C | +|PIRS |PIR Status |enum |0 - PIR on | +| | | |1 - PIR off | +|OCC |Occupancy Button |enum |0 - Button pressed | +| | | |1 - Button released | + +##### RORG 0xA5 - FUNC 0x08 - TYPE 0x03 - Range 0lx to 1020lx, 0°C to +51°C and Occupancy Button + +|shortcut|description |type |values | +|--------|--------------------------------------------------|--------|---- | +|SVC |Supply voltage (linear) |value |0.0-255.0 ↔ 0.0-5.1 V | +|ILL |Illumination (linear) |value |0.0-255.0 ↔ 0.0-1530.0 lx | +|TMP |Temperature (linear) |value |0.0-255.0 ↔ -30.0-50.0 °C | +|PIRS |PIR Status |enum |0 - PIR on | +| | | |1 - PIR off | +|OCC |Occupancy Button |enum |0 - Button pressed | +| | | |1 - Button released | + ##### RORG 0xA5 - FUNC 0x09 - TYPE 0x04 - CO2 Sensor @@ -670,6 +717,66 @@ All profiles (should) correspond to the official [EEP](https://www.enocean-allia |RCU |Select function |enum |0 - RCU | | | | |1 - service on | +##### RORG 0xA5 - FUNC 0x20 - TYPE 0x06 - HVAC Components,Harvesting-powered actuator with local temperature offset control + +###### direction: 1 +|shortcut|description |type |values | +|--------|--------------------------------------------------|--------|---- | +|CV |Current Valve |value |0.0-100.0 ↔ 0.0-100.0 % | +|LOM |Local Offset Mode |enum |0 - LO is relative - Offsettemperature | +| | | |1 - LO is absolute - Absoluttemperature | +|LO |Local Offset |enum |0-80 - Temperature setpoint °C +/- local offset °C | +| | | |0x0 - Local Offset 0C | +| | | |1 - Local Offset 1C | +| | | |2 - Local Offset 2C | +| | | |3 - Local Offset 3C | +| | | |4 - Local Offset 4C | +| | | |5 - Local Offset 5C | +| | | |123 - Local Offset -5C | +| | | |124 - Local Offset -4C | +| | | |125 - Local Offset -3C | +| | | |126 - Local Offset -2C | +| | | |127 - Local Offset -1C | +|TMP |Temperature |enum |0-160 - Local Ambient or Feed temperature (Selected by Direction 2, DB1.1)| +|TSL |Tempertature Selection |enum |0 - Ambient Sensor Temperature | +| | | |1 - Feed Sensor Temperature | +|ENIE |Energy Input Enabled |enum |0 - Not Harvesting | +| | | |1 - Harvesting active | +|ES |Energy Storage |enum |0 - Low - almost discharged | +| | | |1 - Sufficently charged | +|DWO |Window open detection |enum |0 - NO window open detected | +| | | |1 - Window open detected | +|RCE |Radio Com Error |enum |0 - Radio communication is stable | +| | | |1 - 6 or more consecutive communication erros have occured | +|RSS |Radio Signal strength |enum |0 - Radio signal is strong | +| | | |1 - Radio signal is weak under -77dBm | +|ACO |Actuator obstructed |enum |0 - Actuator working correctly | +| | | |1 - Actuator blocked | + +###### direction: 2 +|shortcut|description |type |values | +|--------|--------------------------------------------------|--------|---- | +|SP |Setpoint |enum | | +|TMP |Room temperature from control unit |value |0.0-160.0 ↔ 0.0-40.0 % | +|REF |Reference run |enum |0 - Normal operation | +| | | |1 - Reference run | +|RFC |RF Communication intervall |enum |0 - Auto | +| | | |1 - 2 minutes | +| | | |2 - 5 minutes | +| | | |3 - 10 minutes | +| | | |4 - 20 minutes | +| | | |5 - 30 minutes | +| | | |6 - 60 minutes | +| | | |7 - 120 minutes | +|SB |Initiate summer mode Bit |enum |0 - Normal operation | +| | | |1 - Summer mode with 8hours radio duty cycle | +|SPS |Set point selection |enum |0 - Valve position mode | +| | | |1 - Temperature setpoint | +|TSL |Temperature Selection |enum |0 - Request ambient temperature | +| | | |1 - Request feedtemperature | +|SBY |Standbye |enum |0 - Normal operation | +| | | |1 - Enter standbye | + ##### RORG 0xA5 - FUNC 0x12 - TYPE 0x01 - Electricity @@ -775,6 +882,74 @@ All profiles (should) correspond to the official [EEP](https://www.enocean-allia | | | |101-126 - Not used | | | | |127 - output value not valid / not set | +##### RORG 0xD2 - FUNC 0x01 - TYPE 0x0E - Electronic switch with Local Control + +###### command: 4 +|shortcut|description |type |values | +|--------|--------------------------------------------------|--------|---- | +|PF |Power Failure |enum |0 - Power Failure Detection disabled/not supported | +| | | |1 - Power Failure Detection enabled | +|PFD |Power Failure Detection |enum |0 - Power Failure Detection not detected/not supported/disabled | +| | | |1 - Power Failure Detection Detected | +|CMD |Command indentifier |enum |0-13 - Command ID {value} | +|OC |Over current switch off |enum |0 - Over current switch off: ready / not supported | +| | | |1 - Over current switch off: executed | +|EL |Error level |enum |0 - Error level 0: hardware OK | +| | | |1 - Error level 1: hardware warning | +| | | |2 - Error level 2: hardware failure | +| | | |3 - Error level not supported | +|IO |I/O channel |enum |0-29 - Output channel {value} (to load) | +| | | |30 - Not applicable, do not use | +| | | |31 - Input channel (from mains supply) | +|LC |Local control |enum |0 - Local control disabled / not supported | +| | | |1 - Local control enabled | +|OV |Output value |enum |0 - Output value 0% or OFF | +| | | |1-100 - Output value {value}% or ON | +| | | |101-126 - Not used | +| | | |127 - output value not valid / not set | + +###### command: 1 +|shortcut|description |type |values | +|--------|--------------------------------------------------|--------|---- | +|CMD |Command indentifier |enum |0-13 - Command ID {value} | +|DV |Dim value |enum |0 - Switch to new output value | +| | | |1 - Dim to new output level - dim timer 1 | +| | | |2 - Dim to new output level - dim timer 2 | +| | | |3 - Dim to new output level - dim timer 3 | +| | | |4 - Stop dimming | +|IO |I/O channel |enum |0-29 - Output channel {value} (to load) | +| | | |30 - All output channels supported by the device | +| | | |31 - Input channel (from mains supply) | +|OV |Output value |enum |0 - Output value 0% or OFF | +| | | |1-100 - Output value {value}% or ON | +| | | |101-126 - Not used | +| | | |127 - output value not valid / not set | + +###### command: 6 +|shortcut|description |type |values | +|--------|--------------------------------------------------|--------|---- | +|CMD |Command indentifier |enum |0-13 - Command ID {value} | +|qu |Measurement to query |enum |0 - Query energy | +| | | |1 - Query power | +|IO |I/O channel |enum |0-29 - Output channel {value} (to load) | +| | | |30 - All output channels supported by the device | +| | | |31 - Input channel (from mains supply) | + +###### command: 7 +|shortcut|description |type |values | +|--------|--------------------------------------------------|--------|---- | +|CMD |Command indentifier |enum |0-13 - Command ID {value} | +|UN |Unit |enum |0 - Ws | +| | | |1 - Wh | +| | | |2 - kWh | +| | | |3 - W | +| | | |4 - kW | +| | | |5-7 - Not used | +|IO |I/O channel |enum |0-29 - Output channel {value} (to load) | +| | | |30 - Not applicable, do not use | +| | | |31 - Input channel (from mains supply) | +|MV |Measurement value |value |1.0-4294967295.0 ↔ 1.0-4294967295.0 None | + ##### RORG 0xD2 - FUNC 0x01 - TYPE 0x0F - Electronic switch with Local Control ###### command: 4 @@ -818,6 +993,100 @@ All profiles (should) correspond to the official [EEP](https://www.enocean-allia | | | |101-126 - Not used | | | | |127 - output value not valid / not set | +###### command: 6 +|shortcut|description |type |values | +|--------|--------------------------------------------------|--------|---- | +|CMD |Command indentifier |enum |0-13 - Command ID {value} | +|qu |Measurement to query |enum |0 - Query energy | +| | | |1 - Query power | +|IO |I/O channel |enum |0-29 - Output channel {value} (to load) | +| | | |30 - All output channels supported by the device | +| | | |31 - Input channel (from mains supply) | + +###### command: 7 +|shortcut|description |type |values | +|--------|--------------------------------------------------|--------|---- | +|CMD |Command indentifier |enum |0-13 - Command ID {value} | +|UN |Unit |enum |0 - Ws | +| | | |1 - Wh | +| | | |2 - kWh | +| | | |3 - W | +| | | |4 - kW | +| | | |5-7 - Not used | +|IO |I/O channel |enum |0-29 - Output channel {value} (to load) | +| | | |30 - Not applicable, do not use | +| | | |31 - Input channel (from mains supply) | +|MV |Measurement value |value |1.0-4294967295.0 ↔ 1.0-4294967295.0 None | + +##### RORG 0xD2 - FUNC 0x01 - TYPE 0x12 - Electronic switch with Local Control + +###### command: 4 +|shortcut|description |type |values | +|--------|--------------------------------------------------|--------|---- | +|PF |Power Failure |enum |0 - Power Failure Detection disabled/not supported | +| | | |1 - Power Failure Detection enabled | +|PFD |Power Failure Detection |enum |0 - Power Failure Detection not detected/not supported/disabled | +| | | |1 - Power Failure Detection Detected | +|CMD |Command indentifier |enum |0-13 - Command ID {value} | +|OC |Over current switch off |enum |0 - Over current switch off: ready / not supported | +| | | |1 - Over current switch off: executed | +|EL |Error level |enum |0 - Error level 0: hardware OK | +| | | |1 - Error level 1: hardware warning | +| | | |2 - Error level 2: hardware failure | +| | | |3 - Error level not supported | +|IO |I/O channel |enum |0-29 - Output channel {value} (to load) | +| | | |30 - Not applicable, do not use | +| | | |31 - Input channel (from mains supply) | +|LC |Local control |enum |0 - Local control disabled / not supported | +| | | |1 - Local control enabled | +|OV |Output value |enum |0 - Output value 0% or OFF | +| | | |1-100 - Output value {value}% or ON | +| | | |101-126 - Not used | +| | | |127 - output value not valid / not set | + +###### command: 1 +|shortcut|description |type |values | +|--------|--------------------------------------------------|--------|---- | +|CMD |Command indentifier |enum |0-13 - Command ID {value} | +|DV |Dim value |enum |0 - Switch to new output value | +| | | |1 - Dim to new output level - dim timer 1 | +| | | |2 - Dim to new output level - dim timer 2 | +| | | |3 - Dim to new output level - dim timer 3 | +| | | |4 - Stop dimming | +|IO |I/O channel |enum |0-29 - Output channel {value} (to load) | +| | | |30 - All output channels supported by the device | +| | | |31 - Input channel (from mains supply) | +| | | |32-127 - Output channel {value} (to load) | +|OV |Output value |enum |0 - Output value 0% or OFF | +| | | |1-100 - Output value {value}% or ON | +| | | |101-126 - Not used | +| | | |127 - output value not valid / not set | + +###### command: 6 +|shortcut|description |type |values | +|--------|--------------------------------------------------|--------|---- | +|CMD |Command indentifier |enum |0-13 - Command ID {value} | +|qu |Measurement to query |enum |0 - Query energy | +| | | |1 - Query power | +|IO |I/O channel |enum |0-29 - Output channel {value} (to load) | +| | | |30 - All output channels supported by the device | +| | | |31 - Input channel (from mains supply) | + +###### command: 7 +|shortcut|description |type |values | +|--------|--------------------------------------------------|--------|---- | +|CMD |Command indentifier |enum |0-13 - Command ID {value} | +|UN |Unit |enum |0 - Ws | +| | | |1 - Wh | +| | | |2 - kWh | +| | | |3 - W | +| | | |4 - kW | +| | | |5-7 - Not used | +|IO |I/O channel |enum |0-29 - Output channel {value} (to load) | +| | | |30 - Not applicable, do not use | +| | | |31 - Input channel (from mains supply) | +|MV |Measurement value |value |1.0-4294967295.0 ↔ 1.0-4294967295.0 None | + ##### RORG 0xD2 - FUNC 0x05 - TYPE 0x00 - Type 0x00 diff --git a/enocean/__init__.py b/enocean/__init__.py index 68f1f32..ea28939 100644 --- a/enocean/__init__.py +++ b/enocean/__init__.py @@ -1,3 +1,3 @@ # -*- encoding: utf-8 -*- -__version__ = '0.70.0' +__version__ = '0.71.0' diff --git a/enocean/communicators/communicator.py b/enocean/communicators/communicator.py index 83210d1..1014c1c 100644 --- a/enocean/communicators/communicator.py +++ b/enocean/communicators/communicator.py @@ -9,16 +9,18 @@ from ..protocol.constants import COMMON_COMMAND, PACKET, PARSE_RESULT, RETURN_CODE from ..protocol.packet import Packet, UTETeachInPacket +LOGGER = logging.getLogger('enocean.communicators.Communicator') + class Communicator(threading.Thread): """ Communicator base-class for EnOcean. Not to be used directly, only serves as base class for SerialCommunicator etc. """ - logger = logging.getLogger('enocean.communicators.Communicator') - def __init__(self, callback: callable = None, teach_in: bool = True) -> None: - super(Communicator, self).__init__() + def __init__(self, callback: callable = None, teach_in: bool = True, loglevel=logging.NOTSET) -> None: + super().__init__() + LOGGER.setLevel(loglevel) # Create an event to stop the thread self._stop_flag = threading.Event() # Input buffer @@ -38,16 +40,15 @@ def _get_from_send_queue(self) -> Union[Packet, None]: """ Get message from send queue, if one exists """ try: packet = self.transmit.get(block=False) - self.logger.info('Sending packet') - self.logger.debug(packet) return packet except queue.Empty: pass return None def send(self, packet: Packet) -> bool: + LOGGER.debug(f'sending: {packet}') if not isinstance(packet, Packet): - self.logger.error('Object to send must be an instance of Packet') + LOGGER.error('Object to send must be an instance of Packet') return False self.transmit.put(packet) return True @@ -70,14 +71,14 @@ def parse(self) -> Union[None, PARSE_RESULT]: if isinstance(packet, UTETeachInPacket) and self.teach_in: response_packet = packet.create_response_packet(self.base_id) - self.logger.info('Sending response to UTE teach-in.') + LOGGER.info('Sending response to UTE teach-in.') self.send(response_packet) + LOGGER.debug(f"received: {packet}") if self.__callback is None: self.receive.put(packet) else: self.__callback(packet) - self.logger.debug(packet) @property # getter def callback(self): @@ -89,7 +90,7 @@ def callback(self, callback): @property def base_id(self) -> Union[None, list[int, int, int, int]]: - """ Fetches Base ID from the transmitter, if required. Otherwise returns the currently set Base ID. """ + """ Fetches Base ID from the transmitter, if required. Otherwise, returns the currently set Base ID. """ # If base id is already set, return it. if self._base_id is not None: return self._base_id diff --git a/enocean/communicators/serialcommunicator.py b/enocean/communicators/serialcommunicator.py index b6d27ce..6a1829d 100644 --- a/enocean/communicators/serialcommunicator.py +++ b/enocean/communicators/serialcommunicator.py @@ -7,18 +7,20 @@ from .communicator import Communicator +LOGGER = logging.getLogger('enocean.communicators.SerialCommunicator') + class SerialCommunicator(Communicator): """ Serial port communicator class for EnOcean radio """ - logger = logging.getLogger('enocean.communicators.SerialCommunicator') - def __init__(self, port: str = '/dev/ttyAMA0', callback: callable = None) -> None: - super(SerialCommunicator, self).__init__(callback) + def __init__(self, port: str = '/dev/ttyAMA0', callback: callable = None, loglevel=logging.NOTSET) -> None: + super().__init__(callback, loglevel=loglevel) + LOGGER.setLevel(loglevel) # Initialize serial port self.__ser = serial.Serial(port, 57600, timeout=0.1) def run(self) -> None: - self.logger.info('SerialCommunicator started') + LOGGER.info('SerialCommunicator started') while not self._stop_flag.is_set(): # If there's messages in transmit queue # send them @@ -35,11 +37,11 @@ def run(self) -> None: try: self._buffer.extend(bytearray(self.__ser.read(16))) except serial.SerialException: - self.logger.error('Serial port exception! (device disconnected or multiple access on port?)') + LOGGER.error('Serial port exception! (device disconnected or multiple access on port?)') self.stop() continue self.parse() time.sleep(0) self.__ser.close() - self.logger.info('SerialCommunicator stopped') + LOGGER.info('SerialCommunicator stopped') diff --git a/enocean/communicators/tcpcommunicator.py b/enocean/communicators/tcpcommunicator.py index 13f80fb..fecc846 100644 --- a/enocean/communicators/tcpcommunicator.py +++ b/enocean/communicators/tcpcommunicator.py @@ -5,18 +5,20 @@ from .communicator import Communicator +LOGGER = logging.getLogger('enocean.communicators.TCPCommunicator') + class TCPCommunicator(Communicator): """ Socket communicator class for EnOcean radio """ - logger = logging.getLogger('enocean.communicators.TCPCommunicator') - def __init__(self, host: str = '', port: int = 9637) -> None: - super(TCPCommunicator, self).__init__() + def __init__(self, host: str = '', port: int = 9637, loglevel=logging.NOTSET) -> None: + super().__init__(loglevel=loglevel) + LOGGER.setLevel(loglevel) self.host = host self.port = port def run(self) -> None: - self.logger.info('TCPCommunicator started') + LOGGER.info('TCPCommunicator started') sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.bind((self.host, self.port)) sock.listen(5) @@ -27,7 +29,7 @@ def run(self) -> None: (client, addr) = sock.accept() except socket.timeout: continue - self.logger.debug(f'Client "{addr}" connected') + LOGGER.debug(f'Client "{addr}" connected') client.settimeout(0.5) while True and not self._stop_flag.is_set(): try: @@ -39,6 +41,6 @@ def run(self) -> None: self._buffer.extend(bytearray(data)) self.parse() client.close() - self.logger.debug('Client disconnected') + LOGGER.debug('Client disconnected') sock.close() - self.logger.info('TCPCommunicator stopped') + LOGGER.info('TCPCommunicator stopped') diff --git a/enocean/protocol/EEP.xml b/enocean/protocol/EEP.xml index 82d7d29..c909268 100644 --- a/enocean/protocol/EEP.xml +++ b/enocean/protocol/EEP.xml @@ -496,6 +496,34 @@ + + + + + 0 + 250 + + + 0 + 100 + + + + + 0 + 250 + + + -20 + 60 + + + + + + + + @@ -524,6 +552,30 @@ + + + + + 0 + 199 + + + 0 + 100 + + + + + 0 + 1599 + + + -40 + +120 + + + + @@ -652,7 +704,7 @@ - + @@ -694,6 +746,90 @@ + + + + + 0 + 255 + + + 0 + 5.100000 + + + + + 0 + 255 + + + 0 + 1020 + + + + + 0 + 255 + + + 0 + 51 + + + + + + + + + + + + + + + + + 0 + 255 + + + 0 + 5.100000 + + + + + 0 + 255 + + + 0 + 1530 + + + + + 0 + 255 + + + -30 + 50 + + + + + + + + + + + + @@ -813,7 +949,7 @@ - + @@ -841,7 +977,7 @@ - + @@ -869,7 +1005,7 @@ - + @@ -907,7 +1043,7 @@ - + @@ -1436,6 +1572,133 @@ + + + + + 0 + 100 + + + 0 + 100 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 100 + + + 0 + 100 + + + + + 0 + 80 + + + 0 + 40 + + + + + + 0 + 160 + + + 0 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1663,6 +1926,125 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 4294967295 + + + 1 + 4294967295 + + + @@ -1740,6 +2122,167 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 4294967295 + + + 1 + 4294967295 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 4294967295 + + + 1 + 4294967295 + + diff --git a/enocean/protocol/constants.py b/enocean/protocol/constants.py index 17a004f..fc78352 100644 --- a/enocean/protocol/constants.py +++ b/enocean/protocol/constants.py @@ -69,6 +69,8 @@ class RORG(IntEnum): SYS_EX = 0xC5 SEC = 0x30 SEC_ENCAPS = 0x31 + SEC_MAN = 0x34 + SIGNAL = 0xD0 UTE = 0xD4 diff --git a/enocean/protocol/eep.py b/enocean/protocol/eep.py index 27107bb..a74d8f7 100644 --- a/enocean/protocol/eep.py +++ b/enocean/protocol/eep.py @@ -78,7 +78,7 @@ def _get_value(self, source: Tag, bitarray: list) -> dict: return { source['shortcut']: { 'description': source.get('description'), - 'unit': source['unit'], + 'unit': source.get('unit'), 'value': (scl_max - scl_min) / (rng_max - rng_min) * (raw_value - rng_min) + scl_min, 'raw_value': raw_value, } diff --git a/generate_supported_profiles.py b/generate_supported_profiles.py index 353feb5..962802b 100755 --- a/generate_supported_profiles.py +++ b/generate_supported_profiles.py @@ -74,10 +74,16 @@ range_min = float(item.find('min').text) range_max = float(item.find('max').text) scale = parent.find('scale') - scale_min = float(scale.find('min').text) - scale_max = float(scale.find('max').text) + if scale: + scale_min = float(scale.find('min').text) + scale_max = float(scale.find('max').text) + else: + scale_min = '' + scale_max = '' + unit = item.get('unit') or parent.get('unit') + + values.append(f"{range_min}-{range_max} ↔ {scale_min}-{scale_max} {unit}") - values.append(f"{range_min}-{range_max} ↔ {scale_min}-{scale_max} {parent['unit']}") if not values: write(ROW_FORMAT.format(child['shortcut'], child['description'], child.name, '')) continue diff --git a/pyproject.toml b/pyproject.toml index f6864c4..0dc143e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ authors = [ { name="Kimmo Huoman", email="kipenroskaposti@gmail.com" }, ] maintainers = [ - { name="topic2k", email="topic2k@atlogger.de" }, + { name="Torsten Pieper", email="topic2k@atlogger.de" }, ] description = "EnOcean serial protocol implementation" keywords = [ "EnOcean", "HomeAssistant", ] From be468dc0bc33adee072a0f22bd7d5431d01bd305 Mon Sep 17 00:00:00 2001 From: Henning Kerstan Date: Thu, 9 Oct 2025 22:12:52 +0200 Subject: [PATCH 38/40] fix missing import/wrong name --- enocean/communicators/communicator.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/enocean/communicators/communicator.py b/enocean/communicators/communicator.py index f73f3bd..5927467 100644 --- a/enocean/communicators/communicator.py +++ b/enocean/communicators/communicator.py @@ -8,6 +8,7 @@ from ..protocol.constants import COMMON_COMMAND, PACKET, PARSE_RESULT, RETURN_CODE from ..protocol.packet import Packet, UTETeachInPacket +from ..protocol.version_info import VersionInfo LOGGER = logging.getLogger('enocean.communicators.Communicator') @@ -100,7 +101,7 @@ def base_id(self) -> Union[None, list[int, int, int, int]]: start = datetime.datetime.now() # Send COMMON_COMMAND 0x08, CO_RD_IDBASE request to the module - self.send(Packet(PACKET.COMMON_COMMAND, data=[COMMON_COMMAND_CODE.CO_RD_IDBASE.value])) + self.send(Packet(PACKET.COMMON_COMMAND, data=[COMMON_COMMAND.CO_RD_IDBASE.value])) # wait at most 1 second for the response while True: @@ -152,7 +153,7 @@ def version_info(self): start = datetime.datetime.now() # Send COMMON_COMMAND 0x03, CO_RD_VERSION request to the module - self.send(Packet(PACKET.COMMON_COMMAND, data=[COMMON_COMMAND_CODE.CO_RD_VERSION.value])) + self.send(Packet(PACKET.COMMON_COMMAND, data=[COMMON_COMMAND.CO_RD_VERSION.value])) # wait at most 1 second for the response while True: From a6d397fe82fd931900b4005995abb0a336b7f131 Mon Sep 17 00:00:00 2001 From: Henning Kerstan Date: Thu, 9 Oct 2025 22:21:10 +0200 Subject: [PATCH 39/40] fix LOGGER --- enocean/communicators/communicator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/enocean/communicators/communicator.py b/enocean/communicators/communicator.py index 5927467..74da4f7 100644 --- a/enocean/communicators/communicator.py +++ b/enocean/communicators/communicator.py @@ -159,7 +159,7 @@ def version_info(self): while True: seconds_elapsed = (datetime.datetime.now() - start).total_seconds() if seconds_elapsed > 1: - self.logger.error("Could not obtain version info from module within 1 second (timeout).") + LOGGER.warning("Could not obtain version info from module within 1 second (timeout).") break try: From dec54b29c0b90e38f82e57c7010d1a62c84d40c6 Mon Sep 17 00:00:00 2001 From: Henning Kerstan Date: Thu, 9 Oct 2025 22:23:57 +0200 Subject: [PATCH 40/40] fix unneccessary warnings when getting chip_id / base_id --- enocean/communicators/communicator.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/enocean/communicators/communicator.py b/enocean/communicators/communicator.py index 74da4f7..1d3951f 100644 --- a/enocean/communicators/communicator.py +++ b/enocean/communicators/communicator.py @@ -101,7 +101,7 @@ def base_id(self) -> Union[None, list[int, int, int, int]]: start = datetime.datetime.now() # Send COMMON_COMMAND 0x08, CO_RD_IDBASE request to the module - self.send(Packet(PACKET.COMMON_COMMAND, data=[COMMON_COMMAND.CO_RD_IDBASE.value])) + self.send(Packet(PACKET.COMMON_COMMAND, data=[COMMON_COMMAND.CO_RD_IDBASE.value], optional=[])) # wait at most 1 second for the response while True: @@ -153,7 +153,7 @@ def version_info(self): start = datetime.datetime.now() # Send COMMON_COMMAND 0x03, CO_RD_VERSION request to the module - self.send(Packet(PACKET.COMMON_COMMAND, data=[COMMON_COMMAND.CO_RD_VERSION.value])) + self.send(Packet(PACKET.COMMON_COMMAND, data=[COMMON_COMMAND.CO_RD_VERSION.value], optional=[])) # wait at most 1 second for the response while True: