diff --git a/CustomLogger.py b/CustomLogger.py index 4a7aaaf..b897c3a 100644 --- a/CustomLogger.py +++ b/CustomLogger.py @@ -63,13 +63,6 @@ class IndentFormatter(logging.Formatter): del rec.indent; del rec.function return out -class MessageProcessorFilter(logging.Filter): - def filter(self, record): - # Suppress INFO level messages from MessageProcessor.py - if record.levelno == logging.INFO and record.pathname.endswith("MessageProcessor.py"): - return False - return True - # The following code sets up a custom logger with indentation support. # It creates a custom formatter, a logger instance, and a stream handler. # The custom formatter is set to the handler, which is then added to the logger. @@ -91,15 +84,3 @@ def setDebugMode(): """ logger.setLevel(logging.DEBUG) logger.debug("Debug mode is on...") - -def setSilent(): - """ - Sets the logger to silent mode, where only messages at the ERROR level or higher are displayed. - If the current logging level is not 'DEBUG', this function will log an informational message - indicating that silent mode is being activated and then set the logging level to ERROR. - """ - if logger.level != logging.DEBUG: - logger.info("Silent Mode is turning on, only Messages at Level WARNING or higher are displayed") - #logger.setLevel(logging.ERROR) - # Add the filter to suppress INFO level messages from MessageProcessor.py - logger.addFilter(MessageProcessorFilter()) \ No newline at end of file diff --git a/EHSConfig.py b/EHSConfig.py index db96ae2..993ffd7 100644 --- a/EHSConfig.py +++ b/EHSConfig.py @@ -26,6 +26,7 @@ class EHSConfig(): GENERAL = None SERIAL = None NASA_REPO = None + LOGGING = {} def __new__(cls, *args, **kwargs): """ @@ -77,6 +78,10 @@ class EHSConfig(): self.MQTT = config.get('mqtt') self.GENERAL = config.get('general') self.SERIAL = config.get('serial') + if 'logging' in config: + self.LOGGING = config.get('logging') + else: + self.LOGGING = {} logger.debug(f"Configuration loaded: {config}") self.validate() @@ -97,9 +102,6 @@ class EHSConfig(): self.NASA_REPO = yaml.safe_load(file) else: raise ConfigException(argument=self.GENERAL['nasaRepositoryFile'], message="NASA Respository File is missing") - - if 'silentMode' not in self.GENERAL: - self.GENERAL['silentMode'] = True if 'protocolFile' not in self.GENERAL: self.GENERAL['protocolFile'] = None @@ -132,4 +134,24 @@ class EHSConfig(): raise ConfigException(argument=self.SERIAL['device'], message="mqtt user parameter is missing") if 'password' not in self.MQTT and 'user' in self.MQTT: - raise ConfigException(argument=self.SERIAL['device'], message="mqtt password parameter is missing") \ No newline at end of file + raise ConfigException(argument=self.SERIAL['device'], message="mqtt password parameter is missing") + + if 'messageNotFound' not in self.LOGGING: + self.LOGGING['messageNotFound'] = False + + if 'messageNotFound' not in self.LOGGING: + self.LOGGING['messageNotFound'] = False + + if 'deviceAdded' not in self.LOGGING: + self.LOGGING['deviceAdded'] = True + + if 'packetNotFromIndoorOutdoor' not in self.LOGGING: + self.LOGGING['packetNotFromIndoorOutdoor'] = False + + if 'proccessedMessage' not in self.LOGGING: + self.LOGGING['proccessedMessage'] = False + + logger.info(f"Logging Config:") + for key, value in self.LOGGING.items(): + logger.info(f" {key}: {value}") + \ No newline at end of file diff --git a/EHSExceptions.py b/EHSExceptions.py index b0f7e96..608f2c5 100644 --- a/EHSExceptions.py +++ b/EHSExceptions.py @@ -46,8 +46,8 @@ class ArgumentException(EHSException): def __str__(self): return f'{self.argument} -> {self.message}' - -class MessageCapacityStructureWarning(EHSException): + +class SkipInvalidPacketException(EHSException): """Exception raised for invalid message types. Attributes: diff --git a/MQTTClient.py b/MQTTClient.py index c421ad6..8ed2446 100644 --- a/MQTTClient.py +++ b/MQTTClient.py @@ -13,25 +13,9 @@ from EHSConfig import EHSConfig class MQTTClient: """ - MQTTClient is a singleton class that manages the connection to an MQTT broker and handles - publishing and subscribing to topics. It is designed to work with Home Assistant for - auto-discovery of devices and sensors. - - Attributes: - _instance (MQTTClient): The single instance of the MQTTClient class. - STOP (asyncio.Event): Event to signal stopping the MQTT client. - DEVICE_ID (str): The device ID used for MQTT topics. - config (EHSConfig): Configuration object for the MQTT client. - args (EHSArguments): Arguments object for the MQTT client. - broker (str): URL of the MQTT broker. - port (int): Port of the MQTT broker. - client_id (str): Client ID for the MQTT client. - client (gmqtt.Client): MQTT client instance. - topicPrefix (str): Prefix for MQTT topics. - homeAssistantAutoDiscoverTopic (str): Topic for Home Assistant auto-discovery. - useCamelCaseTopicNames (bool): Flag to use camel case for topic names. - known_topics (list): List to keep track of known topics. - known_devices_topic (str): Dedicated topic for storing known topics. + MQTTClient is a singleton class that manages the connection and communication with an MQTT broker. + It handles the initialization, connection, subscription, and message publishing for the MQTT client. + The class also supports Home Assistant auto-discovery and maintains a list of known devices. """ _instance = None STOP = asyncio.Event() @@ -40,17 +24,16 @@ class MQTTClient: def __new__(cls, *args, **kwargs): """ - Create a new instance of the MQTTClient class if one does not already exist. - This method ensures that the MQTTClient class follows the Singleton design pattern, - meaning only one instance of the class can exist at any given time. If an instance - already exists, it returns the existing instance. Otherwise, it creates a new instance - and sets the _initialized attribute to False. + Create a new instance of the class if one does not already exist. + This method ensures that only one instance of the class is created (singleton pattern). + If an instance already exists, it returns the existing instance. + Otherwise, it creates a new instance, marks it as uninitialized, and returns it. Args: - cls (type): The class being instantiated. + cls: The class being instantiated. *args: Variable length argument list. **kwargs: Arbitrary keyword arguments. Returns: - MQTTClient: The single instance of the MQTTClient class. + An instance of the class. """ if not cls._instance: @@ -60,26 +43,25 @@ class MQTTClient: def __init__(self): """ - Initialize the MQTTClient instance. - This constructor initializes the MQTT client with the configuration - provided by the EHSConfig and EHSArguments classes. It sets up the - MQTT broker connection details, client ID, and authentication credentials - if provided. It also assigns callback functions for various MQTT events - such as connect, disconnect, message, and subscribe. Additionally, it - initializes the topic prefix, Home Assistant auto-discover topic, and - topic naming convention. + Initializes the MQTTClient instance. + This constructor sets up the MQTT client with the necessary configuration + parameters, including broker URL, port, client ID, and authentication credentials. + It also assigns callback functions for various MQTT events such as connect, + disconnect, message, and subscribe. Additionally, it initializes topic-related + settings and a list to keep track of known topics. Attributes: - config (EHSConfig): Configuration object for the MQTT client. - args (EHSArguments): Argument parser object for the MQTT client. + config (EHSConfig): Configuration object for MQTT settings. + args (EHSArguments): Argument parser object. broker (str): URL of the MQTT broker. port (int): Port number of the MQTT broker. client_id (str): Client ID for the MQTT connection. - client (gmqtt.Client): gmqtt client instance. + client (gmqtt.Client): MQTT client instance. topicPrefix (str): Prefix for MQTT topics. homeAssistantAutoDiscoverTopic (str): Topic for Home Assistant auto-discovery. useCamelCaseTopicNames (bool): Flag to use camel case for topic names. + initialized (bool): Flag indicating if the client has been initialized. known_topics (list): List to keep track of known topics. - known_devices_topic (str): Dedicated topic for storing known topics. + known_devices_topic (str): Topic for storing known devices. """ if self._initialized: @@ -108,17 +90,15 @@ class MQTTClient: async def connect(self): """ Asynchronously connects to the MQTT broker and optionally clears the known devices topic. - This method logs the connection attempt, connects to the MQTT broker using the specified - broker address and port, and sets the keepalive interval. If the CLEAN_KNOWN_DEVICES - argument is set to True, it publishes an empty message to the known devices topic to clear it. + This function logs the connection attempt, connects to the MQTT broker using the specified + broker address and port, and sets the keepalive interval. If the CLEAN_KNOWN_DEVICES argument + is set, it publishes an empty message to the known devices topic to clear it. Args: None Returns: None - Raises: - Any exceptions raised by the underlying MQTT client library during connection. """ - + logger.info("[MQTT] Connecting to broker...") await self.client.connect(self.broker, self.port, keepalive=60, version=gmqtt.constants.MQTTv311) @@ -128,15 +108,13 @@ class MQTTClient: def subscribe_known_topics(self): """ - Subscribes the MQTT client to known topics. - This method subscribes the MQTT client to two specific topics: - 1. A topic for known devices, constructed using the topic prefix and known devices topic. - 2. A status topic for Home Assistant auto-discovery. - The subscription QoS (Quality of Service) level for both topics is set to 1. + Subscribe to predefined MQTT topics. + This method subscribes the MQTT client to a set of known topics, which include: + - A topic for known devices, constructed using the topic prefix and known devices topic. + - A status topic for Home Assistant auto-discovery. + The subscription is done with a QoS level of 1 for both topics. Logging: - Logs an informational message indicating that the client is subscribing to known devices topic. - Raises: - Any exceptions raised by the gmqtt.Subscription or self.client.subscribe methods. + - Logs an info message indicating the subscription to known devices topic. """ logger.info("Subscribe to known devices topic") @@ -158,7 +136,7 @@ class MQTTClient: Returns: None """ - + logger.debug('SUBSCRIBED') def on_message(self, client, topic, payload, qos, properties): @@ -169,49 +147,46 @@ class MQTTClient: topic (str): The topic that the message was received on. payload (bytes): The message payload. qos (int): The quality of service level of the message. - properties (paho.mqtt.properties.Properties): The properties of the message. - Behavior: - - If the topic matches the known devices topic, updates the known devices set with the retained message. - - If the topic matches the Home Assistant auto-discover status topic, logs the status message and clears the known devices topic. + properties (paho.mqtt.properties.Properties): The properties associated with the message. + This function performs the following actions: + - If the topic matches the known devices topic, it updates the known topics list with the retained message. + - If the topic matches the Home Assistant auto-discover status topic, it logs the status message and, if the payload indicates that Home Assistant is online, it clears the known devices topic. """ if self.known_devices_topic in topic: # Update the known devices set with the retained message self.known_topics = list(filter(None, [x.strip() for x in payload.decode().split(",")])) + if properties['retain'] == True: + if self.config.LOGGING['deviceAdded']: + logger.info(f"Loaded devices from known devices Topic:") + + for idx, devname in enumerate(self.known_topics, start=1): + logger.info(f"Device no. {idx:<3}: {devname} ") + else: + logger.debug(f"Loaded devices from known devices Topic:") + for idx, devname in enumerate(self.known_topics): + logger.debug(f"Device added no. {idx:<3}: {devname} ") - if f"{self.homeAssistantAutoDiscoverTopic}/status" == topic: logger.info(f"HASS Status Messages {topic} received: {payload.decode()}") if payload.decode() == "online": self._publish(f"{self.topicPrefix.replace('/', '')}/{self.known_devices_topic}", " ", retain=True) - logger.info("Known Devices Topic have been cleared") - - def refresh_known_devices(self, devname): - """ - Refreshes the list of known devices by publishing the updated list to the MQTT topic. - Args: - devname (str): The name of the device to be refreshed. - Returns: - None - """ - - self._publish(f"{self.topicPrefix.replace('/', '')}/{self.known_devices_topic}", ",".join(self.known_topics), retain=True) + logger.info("Known Devices Topic have been cleared") + self.clear_hass() + logger.info("All configuration from HASS has been resetet") def on_connect(self, client, flags, rc, properties): """ Callback function for when the client receives a CONNACK response from the server. - Args: - client (paho.mqtt.client.Client): The client instance for this callback. - flags (dict): Response flags sent by the broker. - rc (int): The connection result. - properties (paho.mqtt.properties.Properties): The properties associated with the connection. - Returns: - None - Logs: - - Info: When connected successfully with result code 0. - - Error: When failed to connect with a non-zero result code. + Parameters: + client (paho.mqtt.client.Client): The client instance for this callback. + flags (dict): Response flags sent by the broker. + rc (int): The connection result. + properties (paho.mqtt.properties.Properties): The properties associated with the connection. + If the connection is successful (rc == 0), logs a success message and subscribes to known topics if any. + Otherwise, logs an error message with the return code. """ - + if rc == 0: logger.info(f"Connected to MQTT with result code {rc}") if len(self.homeAssistantAutoDiscoverTopic) > 0: @@ -222,14 +197,13 @@ class MQTTClient: def on_disconnect(self, client, packet, exc=None): """ Callback function that is called when the client disconnects from the MQTT broker. + This function logs the disconnection event and attempts to reconnect the client + in case of an unexpected disconnection. It will keep trying to reconnect every + 5 seconds until successful. Args: client (paho.mqtt.client.Client): The MQTT client instance that disconnected. - packet (paho.mqtt.client.MQTTMessage): The MQTT message packet received during disconnection. - exc (Exception, optional): The exception that caused the disconnection, if any. Defaults to None. - Logs: - Logs an info message indicating disconnection. - Logs a warning message indicating an unexpected disconnection and attempts to reconnect. - Logs an error message if reconnection fails and retries every 5 seconds. + packet (paho.mqtt.packet.Packet): The disconnect packet. + exc (Exception, optional): The exception that caused the disconnection, if any. """ logger.info(f"Disconnected with result code ") @@ -244,11 +218,11 @@ class MQTTClient: def _publish(self, topic, payload, qos=0, retain=False): """ - Publish a message to a specified MQTT topic. + Publishes a message to a specified MQTT topic. Args: topic (str): The MQTT topic to publish to. payload (str): The message payload to publish. - qos (int, optional): The Quality of Service level for message delivery. Defaults to 0. + qos (int, optional): The Quality of Service level for the message. Defaults to 0. retain (bool, optional): If True, the message will be retained by the broker. Defaults to False. Returns: None @@ -257,33 +231,51 @@ class MQTTClient: logger.debug(f"MQTT Publish Topic: {topic} payload: {payload}") self.client.publish(f"{topic}", payload, qos, retain) #time.sleep(0.1) + + def refresh_known_devices(self, devname): + """ + Refreshes the list of known devices by publishing the current known topics to the MQTT broker. + Args: + devname (str): The name of the device to refresh. + This function constructs a topic string by replacing '/' with an empty string in the topicPrefix, + then concatenates it with the known_devices_topic. It publishes the known topics as a comma-separated + string to this constructed topic with the retain flag set to True. + """ + self.known_topics.append(devname) + if self.config.LOGGING['deviceAdded']: + logger.info(f"Device added no. {len(self.known_topics):<3}: {devname} ") + else: + logger.debug(f"Device added no. {len(self.known_topics):<3}: {devname} ") + self._publish(f"{self.topicPrefix.replace('/', '')}/{self.known_devices_topic}", ",".join(self.known_topics), retain=True) def publish_message(self, name, value): """ Publishes a message to an MQTT topic. - This method normalizes the given name, determines the appropriate MQTT topic, - and publishes the provided value to that topic. If Home Assistant auto-discovery - is enabled, it will also handle the auto-discovery configuration. + This function normalizes the given name, determines the appropriate MQTT topic, + and publishes the provided value to that topic. It also handles Home Assistant + auto-discovery if configured. Args: name (str): The name of the sensor or device. value (int, float, bool, str): The value to be published. If the value is a float, - it will be rounded to two decimal places. + it will be rounded to two decimal places. Raises: - KeyError: If the name is not found in the NASA_REPO configuration. + ValueError: If the value type is not supported for publishing. """ newname = f"{self._normalize_name(name)}" if len(self.homeAssistantAutoDiscoverTopic) > 0: + + if name not in self.known_topics: + self.auto_discover_hass(name) + self.refresh_known_devices(name) + sensor_type = "sensor" if 'enum' in self.config.NASA_REPO[name]: enum = [*self.config.NASA_REPO[name]['enum'].values()] if all([en.lower() in ['on', 'off'] for en in enum]): sensor_type = "binary_sensor" topicname = f"{self.config.MQTT['homeAssistantAutoDiscoverTopic']}/{sensor_type}/{self.DEVICE_ID}_{newname.lower()}/state" - if name not in self.known_topics: - self.auto_discover_hass(topicname, name, newname, sensor_type) - else: topicname = f"{self.topicPrefix.replace('/', '')}/{newname}" @@ -292,78 +284,24 @@ class MQTTClient: self._publish(topicname, value, qos=2, retain=False) - def auto_discover_hass(self, topicname, nameraw, namenorm, sensor_type): - """ - Automatically discovers and configures Home Assistant entities for the MQTT client. - This function creates and publishes a configuration payload for Home Assistant's MQTT discovery. - It supports both sensor and binary sensor types, and sets appropriate attributes based on the - provided sensor type and unit of measurement. - Args: - topicname (str): The MQTT topic name. - nameraw (str): The raw name of the sensor. - namenorm (str): The normalized name of the sensor. - sensor_type (str): The type of the sensor (e.g., "sensor" or "binary_sensor"). - Returns: - None + def clear_hass(self): """ + clears all entities/components fpr the HomeAssistant Device + """ + entities = {} + for nasa in self.config.NASA_REPO: + namenorm = self._normalize_name(nasa) + sensor_type = self._get_sensor_type(nasa) + entities[namenorm] = {"platform": sensor_type} - entity = { namenorm: { - "name": f"{namenorm}", - "object_id": f"{self.DEVICE_ID}_{namenorm.lower()}", - "unique_id": f"{self.DEVICE_ID}_{nameraw.lower()}", - "platform": sensor_type, - "value_template": "{{ value }}", - "state_topic": f"{self.config.MQTT['homeAssistantAutoDiscoverTopic']}/{sensor_type}/{self.DEVICE_ID}_{namenorm.lower()}/state", - } - } - - if sensor_type == "sensor": - if len(self.config.NASA_REPO[nameraw]['unit']) > 0: - entity[namenorm]['unit_of_measurement'] = self.config.NASA_REPO[nameraw]['unit'] - if entity[namenorm]['unit_of_measurement'] == "\u00b0C": - entity[namenorm]['device_class'] = "temperature" - elif entity[namenorm]['unit_of_measurement'] == '%': - entity[namenorm]['state_class'] = "measurement" - elif entity[namenorm]['unit_of_measurement'] == 'kW': - entity[namenorm]['device_class'] = "power" - elif entity[namenorm]['unit_of_measurement'] == 'rpm': - entity[namenorm]['state_class'] = "measurement" - elif entity[namenorm]['unit_of_measurement'] == 'bar': - entity[namenorm]['device_class'] = "pressure" - elif entity[namenorm]['unit_of_measurement'] == 'HP': - entity[namenorm]['device_class'] = "power" - elif entity[namenorm]['unit_of_measurement'] == 'hz': - entity[namenorm]['device_class'] = "frequency" - else: - entity[namenorm]['device_class'] = None - else: - entity[namenorm]['payload_on'] = "ON" - entity[namenorm]['payload_off'] = "OFF" - - if 'state_class' in self.config.NASA_REPO[nameraw]: - entity[namenorm]['state_class'] = self.config.NASA_REPO[nameraw]['state_class'] - - if 'device_class' in self.config.NASA_REPO[nameraw]: - entity[namenorm]['device_class'] = self.config.NASA_REPO[nameraw]['device_class'] - - device = { - "device": { - "identifiers": self.DEVICE_ID, - "name": "Samsung EHS", - "manufacturer": "Samsung", - "model": "Mono HQ Quiet", - "sw_version": "1.0.0" - }, - "origin": { - "name": "EHS-Sentinel", - "support_url": "https://github.com/echoDaveD/EHS-Sentinel" - }, - "components": entity, + "device": self._get_device(), + "origin": self._get_origin(), + "components": entities, "qos": 2 } - logger.debug(f"Auto Discovery HomeAssistant Message: ") + logger.debug(f"Auto Discovery HomeAssistant Clear Message: ") logger.debug(f"{device}") self._publish(f"{self.config.MQTT['homeAssistantAutoDiscoverTopic']}/device/{self.DEVICE_ID}/config", @@ -371,9 +309,99 @@ class MQTTClient: qos=2, retain=True) - self.known_topics.append(nameraw) - self.refresh_known_devices(nameraw) + def auto_discover_hass(self, name): + """ + Automatically discovers and configures Home Assistant entities based on the NASA_REPO configuration. + This function iterates through the NASA_REPO configuration to create and configure entities for Home Assistant. + It determines the type of sensor (binary_sensor or sensor) based on the configuration and sets various attributes + such as unit of measurement, device class, state class, and payloads for binary sensors. It then constructs a device + configuration payload and publishes it to the Home Assistant MQTT discovery topic. + The function performs the following steps: + 1. Iterates through the NASA_REPO configuration. + 2. Normalizes the name of each NASA_REPO entry. + 3. Determines the sensor type (binary_sensor or sensor) based on the 'enum' values. + 4. Configures the entity attributes such as unit of measurement, device class, state class, and payloads. + 5. Constructs a device configuration payload. + 6. Publishes the device configuration to the Home Assistant MQTT discovery topic. + Attributes: + entities (dict): A dictionary to store the configured entities. + device (dict): A dictionary to store the device configuration payload. + Logs: + Logs the constructed device configuration payload for debugging purposes. + Publishes: + Publishes the device configuration payload to the Home Assistant MQTT discovery topic with QoS 2 and retain flag set to True. + """ + entity = {} + namenorm = self._normalize_name(name) + sensor_type = self._get_sensor_type(name) + entity = { + "name": f"{namenorm}","" + "object_id": f"{self.DEVICE_ID}_{namenorm.lower()}", + "unique_id": f"{self.DEVICE_ID}_{name.lower()}", + "platform": sensor_type, + "value_template": "{{ value }}", + #"value_template": "{{ value if value | length > 0 else 'unavailable' }}", + "state_topic": f"{self.config.MQTT['homeAssistantAutoDiscoverTopic']}/{sensor_type}/{self.DEVICE_ID}_{namenorm.lower()}/state", + } + if sensor_type == "sensor": + if len(self.config.NASA_REPO[name]['unit']) > 0: + entity['unit_of_measurement'] = self.config.NASA_REPO[name]['unit'] + if entity['unit_of_measurement'] == "\u00b0C": + entity['device_class'] = "temperature" + elif entity['unit_of_measurement'] == '%': + entity['state_class'] = "measurement" + elif entity['unit_of_measurement'] == 'kW': + entity['device_class'] = "power" + elif entity['unit_of_measurement'] == 'rpm': + entity['state_class'] = "measurement" + elif entity['unit_of_measurement'] == 'bar': + entity['device_class'] = "pressure" + elif entity['unit_of_measurement'] == 'HP': + entity['device_class'] = "power" + elif entity['unit_of_measurement'] == 'hz': + entity['device_class'] = "frequency" + else: + entity['device_class'] = None + else: + entity['payload_on'] = "ON" + entity['payload_off'] = "OFF" + + if 'state_class' in self.config.NASA_REPO[name]: + entity['state_class'] = self.config.NASA_REPO[name]['state_class'] + + if 'device_class' in self.config.NASA_REPO[name]: + entity['device_class'] = self.config.NASA_REPO[name]['device_class'] + + device = { + "device": self._get_device(), + "origin": self._get_origin(), + "qos": 2 + } + device.update(entity) + + logger.debug(f"Auto Discovery HomeAssistant Message: ") + logger.debug(f"{device}") + + self._publish(f"{self.config.MQTT['homeAssistantAutoDiscoverTopic']}/{sensor_type}/{self.DEVICE_ID}_{name.lower()}/config", + json.dumps(device, ensure_ascii=False), + qos=2, + retain=True) + + def _get_device(self): + return { + "identifiers": self.DEVICE_ID, + "name": "Samsung EHS", + "manufacturer": "Samsung", + "model": "Mono HQ Quiet", + "sw_version": "1.0.0" + } + + def _get_origin(self): + return { + "name": "EHS-Sentinel", + "support_url": "https://github.com/echoDaveD/EHS-Sentinel" + } def _normalize_name(self, name): """ @@ -405,3 +433,19 @@ class MQTTClient: tmpname = name return tmpname + + def _get_sensor_type(self, name): + """ + return the sensor type of given measurement + Args: + name (str): The name of the measurement. + Returns: + str: The sensor type: sensor or binary_sensor. + """ + sensor_type = "sensor" + if 'enum' in self.config.NASA_REPO[name]: + enum = [*self.config.NASA_REPO[name]['enum'].values()] + if all([en.lower() in ['on', 'off'] for en in enum]): + sensor_type = "binary_sensor" + + return sensor_type diff --git a/MessageProcessor.py b/MessageProcessor.py index abbca5c..17f83df 100644 --- a/MessageProcessor.py +++ b/MessageProcessor.py @@ -2,7 +2,7 @@ import asyncio import logging import traceback import yaml -from CustomLogger import logger, setSilent +from CustomLogger import logger from EHSArguments import EHSArguments from EHSConfig import EHSConfig from EHSExceptions import MessageWarningException @@ -53,7 +53,6 @@ class MessageProcessor: if self._initialized: return self._initialized = True - logger.debug("init MessageProcessor") self.config = EHSConfig() self.args = EHSArguments() self.mqtt = MQTTClient() @@ -79,7 +78,10 @@ class MessageProcessor: raise MessageWarningException(argument=f"{msg.packet_payload}/{[hex(x) for x in msg.packet_payload]}", message=f"Value of {hexmsg} couldn't be determinate, skip Message {e}") self.protocolMessage(msg, msgname, msgvalue) else: - logger.debug(f"Message not Found in NASA repository: {hexmsg:<6} Type: {msg.packet_message_type} Payload: {msg.packet_payload}") + if self.config.LOGGING['messageNotFound']: + logger.info(f"Message not Found in NASA repository: {hexmsg:<6} Type: {msg.packet_message_type} Payload: {msg.packet_payload}") + else: + logger.debug(f"Message not Found in NASA repository: {hexmsg:<6} Type: {msg.packet_message_type} Payload: {msg.packet_payload}") def protocolMessage(self, msg: NASAMessage, msgname, msgvalue): """ @@ -98,7 +100,10 @@ class MessageProcessor: - Calculates and processes derived values for specific message names. """ - logger.info(f"Message number: {hex(msg.packet_message):<6} {msgname:<50} Type: {msg.packet_message_type} Payload: {msgvalue}") + if self.config.LOGGING['proccessedMessage']: + logger.info(f"Message number: {hex(msg.packet_message):<6} {msgname:<50} Type: {msg.packet_message_type} Payload: {msgvalue}") + else: + logger.debug(f"Message number: {hex(msg.packet_message):<6} {msgname:<50} Type: {msg.packet_message_type} Payload: {msgvalue}") if self.config.GENERAL['protocolFile'] is not None: with open(self.config.GENERAL['protocolFile'], "a") as protWriter: @@ -110,14 +115,18 @@ class MessageProcessor: if msgname in ['NASA_OUTDOOR_TW2_TEMP', 'NASA_OUTDOOR_TW1_TEMP', 'VAR_IN_FLOW_SENSOR_CALC']: if all(k in self.NASA_VAL_STORE for k in ['NASA_OUTDOOR_TW2_TEMP', 'NASA_OUTDOOR_TW1_TEMP', 'VAR_IN_FLOW_SENSOR_CALC']): - self.protocolMessage(NASAMessage(packet_message=0x9999, packet_message_type=1), - "NASA_EHSSENTINEL_HEAT_OUTPUT", - round( - ( - (self.NASA_VAL_STORE['NASA_OUTDOOR_TW2_TEMP'] - self.NASA_VAL_STORE['NASA_OUTDOOR_TW1_TEMP']) * - (self.NASA_VAL_STORE['VAR_IN_FLOW_SENSOR_CALC']/60) - * 4190 - ), 4)) + value = round( + abs( + (self.NASA_VAL_STORE['NASA_OUTDOOR_TW2_TEMP'] - self.NASA_VAL_STORE['NASA_OUTDOOR_TW1_TEMP']) * + (self.NASA_VAL_STORE['VAR_IN_FLOW_SENSOR_CALC']/60) + * 4190 + ) , 4 + ) + if (value < 15000 and value > 0): # only if heater output between 0 und 15000 W + self.protocolMessage(NASAMessage(packet_message=0x9999, packet_message_type=1), + "NASA_EHSSENTINEL_HEAT_OUTPUT", + value + ) if msgname in ('NASA_EHSSENTINEL_HEAT_OUTPUT', 'NASA_OUTDOOR_CONTROL_WATTMETER_ALL_UNIT'): if all(k in self.NASA_VAL_STORE for k in ['NASA_EHSSENTINEL_HEAT_OUTPUT', 'NASA_OUTDOOR_CONTROL_WATTMETER_ALL_UNIT']): @@ -125,6 +134,13 @@ class MessageProcessor: self.protocolMessage(NASAMessage(packet_message=0x9998, packet_message_type=1), "NASA_EHSSENTINEL_COP", round((self.NASA_VAL_STORE['NASA_EHSSENTINEL_HEAT_OUTPUT'] / self.NASA_VAL_STORE['NASA_OUTDOOR_CONTROL_WATTMETER_ALL_UNIT']/1000.), 3)) + + if msgname in ('NASA_OUTDOOR_CONTROL_WATTMETER_ALL_UNIT_ACCUM', 'LVAR_IN_TOTAL_GENERATED_POWER'): + if all(k in self.NASA_VAL_STORE for k in ['NASA_OUTDOOR_CONTROL_WATTMETER_ALL_UNIT_ACCUM', 'LVAR_IN_TOTAL_GENERATED_POWER']): + if (self.NASA_VAL_STORE['NASA_OUTDOOR_CONTROL_WATTMETER_ALL_UNIT_ACCUM'] > 0): + self.protocolMessage(NASAMessage(packet_message=0x9997, packet_message_type=1), + "NASA_EHSSENTINEL_TOTAL_COP", + round(self.NASA_VAL_STORE['LVAR_IN_TOTAL_GENERATED_POWER'] / self.NASA_VAL_STORE['NASA_OUTDOOR_CONTROL_WATTMETER_ALL_UNIT_ACCUM'], 3)) def search_nasa_table(self, address): """ @@ -157,7 +173,7 @@ class MessageProcessor: try: value = eval(arithmetic) except Exception as e: - logger.warning(f"Arithmetic Function couldn't been applied, using raw value: arithmetic = {arithmetic} {e}") + logger.warning(f"Arithmetic Function couldn't been applied for Message {msgname}, using raw value: arithmetic = {arithmetic} {e}") value = packed_value else: value = packed_value diff --git a/NASAMessage.py b/NASAMessage.py index e74af64..f2781fc 100644 --- a/NASAMessage.py +++ b/NASAMessage.py @@ -48,12 +48,53 @@ class NASAMessage: self.packet_message_type: int = packet_message_type self.packet_payload: bytes = bytes([int(hex(x), 16) for x in packet_payload]) + + def set_packet_message(self, value: int): + self.packet_message = value + + def set_packet_message_type(self, value: int): + self.packet_message_type = value + + def set_packet_payload(self, value: list): + self.packet_payload = bytes([int(hex(x), 16) for x in value]) + + def to_raw(self) -> bytearray: + + message_number_reconstructed = (self.packet_message_type << 9) | (self.packet_message & 0x1FF) + + # Extract the original bytes from message_number + msg_rest_0 = (self.packet_message >> 8) & 0xFF # Upper 8 bits + msg_rest_1 = self.packet_message & 0xFF # Lower 8 bits + msgpayload = int.from_bytes(self.packet_payload, byteorder='big', signed=True) + if self.packet_message_type == 0: + return [ + msg_rest_0, + msg_rest_1, + msgpayload & 0xFF + ] + elif self.packet_message_type == 1: + return [ + msg_rest_0, + msg_rest_1, + (msgpayload >> 8) & 0xFF, + msgpayload & 0xFF + ] + elif self.packet_message_type == 2: + return [ + msg_rest_0, + msg_rest_1, + (msgpayload >> 24) & 0xFF, + (msgpayload >> 16) & 0xFF, + (msgpayload >> 8) & 0xFF, + msgpayload & 0xFF + ] + def __str__(self): return ( f"NASAMessage(\n" - f" packet_message={self.packet_message} ({hex(self.packet_message)}),\n" + f" packet_message={self.packet_message} ({hex(self.packet_message)}) ({[x for x in bytearray(self.packet_message.to_bytes(2))]}),\n" f" packet_message_type={self.packet_message_type} ({hex(self.packet_message_type)}),\n" - f" packet_payload={self.packet_payload} ({self.packet_payload.hex()})\n" + f" packet_payload={self.packet_payload} ({self.packet_payload.hex()}) ({[int(x) for x in self.packet_payload]})\n" f")" ) diff --git a/NASAPacket.py b/NASAPacket.py index f0d8ad8..274fb82 100644 --- a/NASAPacket.py +++ b/NASAPacket.py @@ -1,6 +1,8 @@ from enum import Enum from NASAMessage import NASAMessage -from EHSExceptions import MessageCapacityStructureWarning +from EHSExceptions import SkipInvalidPacketException +import binascii +import struct class AddressClassEnum(Enum): """ @@ -210,13 +212,21 @@ class NASAPacket: self._packet_raw = packet if len(packet) < 14: raise ValueError("Data too short to be a valid NASAPacket") + + crc_checkusm=binascii.crc_hqx(bytearray(packet[3:-3]), 0) self.packet_start = packet[0] self.packet_size = ((packet[1] << 8) | packet[2]) - self.packet_source_address_class = AddressClassEnum(packet[3]) + try: + self.packet_source_address_class = AddressClassEnum(packet[3]) + except ValueError as e: + raise SkipInvalidPacketException(f"Source Adress Class out of enum {packet[3]}") self.packet_source_channel = packet[4] self.packet_source_address = packet[5] - self.packet_dest_address_class = AddressClassEnum(packet[6]) + try: + self.packet_dest_address_class = AddressClassEnum(packet[6]) + except ValueError as e: + raise SkipInvalidPacketException(f"Destination Adress Class out of enum {packet[6]}") self.packet_dest_channel = packet[7] self.packet_dest_address = packet[8] self.packet_information = (int(packet[9]) & 128) >> 7 == 1 @@ -226,10 +236,13 @@ class NASAPacket: self.packet_data_type = DataType(int(packet[10]) & 15) self.packet_number = packet[11] self.packet_capacity = packet[12] - self.packet_crc16 = ((packet[-3] << 8) | packet[-2]) + 2 + self.packet_crc16 = ((packet[-3] << 8) | packet[-2]) # + 2 self.packet_end = packet[-1] self.packet_messages = self._extract_messages(0, self.packet_capacity, packet[13:-3], []) + if crc_checkusm != self.packet_crc16: + raise SkipInvalidPacketException(f"Checksum for package could not be validated. Calculated: {crc_checkusm} in packet: {self.packet_crc16}: packet:{self}") + def _extract_messages(self, depth: int, capacity: int, msg_rest: bytearray, return_list: list): """ Recursively extracts messages from a bytearray and appends them to a list. @@ -260,7 +273,7 @@ class NASAPacket: elif message_type == 3: payload_size = len(msg_rest) if capacity != 1: - raise MessageCapacityStructureWarning("Message with structure type must have capacity of 1.") + raise SkipInvalidPacketException("Message with structure type must have capacity of 1.") else: raise ValueError(f"Mssage type unknown: {message_type}") @@ -303,6 +316,84 @@ class NASAPacket: def __repr__(self): return self.__str__() + + # Setter methods + def set_packet_source_address_class(self, value: AddressClassEnum): + self.packet_source_address_class = value + + def set_packet_source_channel(self, value: int): + self.packet_source_channel = value + + def set_packet_source_address(self, value: int): + self.packet_source_address = value + + def set_packet_dest_address_class(self, value: AddressClassEnum): + self.packet_dest_address_class = value + + def set_packet_dest_channel(self, value: int): + self.packet_dest_channel = value + + def set_packet_dest_address(self, value: int): + self.packet_dest_address = value + + def set_packet_information(self, value: bool): + self.packet_information = value + + def set_packet_version(self, value: int): + self.packet_version = value + + def set_packet_retry_count(self, value: int): + self.packet_retry_count = value + + def set_packet_type(self, value: PacketType): + self.packet_type = value + + def set_packet_data_type(self, value: DataType): + self.packet_data_type = value + + def set_packet_number(self, value: int): + self.packet_number = value + + def set_packet_messages(self, value: list[NASAMessage]): + self.packet_messages = value + + def to_raw(self) -> bytearray: + """ + Converts the NASAPacket object back to its raw byte representation. + Returns: + bytearray: The raw byte representation of the packet. + """ + self.packet_start = 50 + self.packet_end = 52 + + + packet = bytearray() + packet.append(int(self.packet_start)) + packet.append(0) + packet.append(0) + packet.append(self.packet_source_address_class.value) + packet.append(self.packet_source_channel) + packet.append(self.packet_source_address) + packet.append(self.packet_dest_address_class.value) + packet.append(self.packet_dest_channel) + packet.append(self.packet_dest_address) + packet.append((self.packet_information << 7) | (self.packet_version << 5) | (self.packet_retry_count << 3)) + packet.append((self.packet_type.value << 4) | self.packet_data_type.value) + packet.append(self.packet_number) + packet.append(len(self.packet_messages)) + # Add messages to the packet + for msg in self.packet_messages: + for msg_pack in msg.to_raw(): + packet.append(msg_pack) + self.packet_capacity = len(self.packet_messages) + self.packet_size = len(packet)+2+2 + packet[1] = (self.packet_size >> 8) & 0xFF + packet[2] = self.packet_size & 0xFF + self.packet_crc16=binascii.crc_hqx(packet[3:], 0) + final_packet = struct.pack(">BH", packet[0], len(packet[1:])+2) + packet[3:] + struct.pack(">HB", self.packet_crc16, 0x34) + print([x for x in final_packet]) + return final_packet + # Example usage: # packet = NASAPacket() # packet.parse(bytearray([0x01, 0x02, 0x03, 0x04, 0x05, 0x06])) diff --git a/README.md b/README.md index 0cd48dd..233e3bd 100644 --- a/README.md +++ b/README.md @@ -169,11 +169,20 @@ The `config.yml` file contains configuration settings for the EHS-Sentinel proje - **nasaRepositoryFile**: Path to the NASA repository file. - Default: `data/NasaRepository.yml` -- **silentMode**: Boolean flag to enable or disable silent mode. In Silent Mode only Logmessages above WARNING are printed out (for production use to not spam your systemlog) - - Default: `True` - **protocolFile**: Path to the protocol file. (not set in Sample config.yml) - Example: `prot.csv` +### Logging Settings + +- **deviceAdded**: Set to true will log when new device is added to known Devices (and discover to HASS). + - Default: `True` +- **messageNotFound**: Set to true will log when a received message was not found in NasaRepository + - Default: `False` +- **packetNotFromIndoorOutdoor**: Set to true will log when a message not from Indoor/Outdoor unit was received + - Default: `False` +- **proccessedMessage**: set to true, prints out a summary of which massage was processed and its value + - Default: `False` + ### Serial Connection Settings - **device**: The serial device URL. @@ -205,8 +214,12 @@ The `config.yml` file contains configuration settings for the EHS-Sentinel proje ```yaml general: nasaRepositoryFile: data/NasaRepository.yml - silentMode: False protocolFile: prot.csv +logging: + deviceAdded: True + messageNotFound: False + packetNotFromIndoorOutdoor: False + proccessedMessage: False serial: device: /dev/ttyUSB0 baudrate: 9600 @@ -254,5 +267,120 @@ if you want to see how many uniquie Messages have been collected in the Dumpfile # Changelog +### v0.2.0 - 2025-02-22 +- improved MQTT Auto Discovery Config Messages +- NASAPacket and NASAMessage are now bidirectional, can decode and encode Packets +- Improved data quality + - Added crc16 Checksum check for any Packet to reduce incorrect value changes + - Only Packets from outdoor/Indoor Units are processed +- Folloiwng warnings moved to SkipInvalidPacketException and from warning to debug log level to reduce log entries + - Source Adress Class out of enum + - Destination Adress Class out of enum + - Checksum for package could not be validatet calculated + - Message with structure type must have capacity of 1. +- removed silentMNode config property (new logging section is replacing its functionality but more granular) +- added new logging config property to allow to turn on/off additional info log entries + - deviceAdded set to true (default) will log when new device is added to known Devices (and discover to HASS) + - messageNotFound set to true (false is default) will log when a received message was not found in NasaRepository + - packetNotFromIndoorOutdoor set to true (false is default) will log when a message not from Indoor/Outdoor unit was received + - proccessedMessage set to true(false is default) prints out a summary of which massage was processed and its value +- Added new Measurements + - 0x4423 LVAR_IN_MINUTES_SINCE_INSTALLATION + - 0x4424 LVAR_IN_MINUTES_ACTIVE + - 0x4426 LVAR_IN_GENERATED_POWER_LAST_MINUTE + - 0x4427 LVAR_IN_TOTAL_GENERATED_POWER + - 0x0997 NASA_EHSSENTINEL_TOTAL_COP = LVAR_IN_TOTAL_GENERATED_POWER / NASA_OUTDOOR_CONTROL_WATTMETER_ALL_UNIT_ACCUM +- NASA Repository, measurements enums completed + - ENUM_IN_FSV_3041: enum edited + - ENUM_IN_FSV_3071: enum edited + - ENUM_IN_FSV_4021: enum edited + - ENUM_IN_FSV_4041: enum edited + - ENUM_IN_FSV_4051: enum edited + - ENUM_IN_FSV_4053: enum edited + - ENUM_IN_FSV_5022: enum edited + - ENUM_IN_FSV_5042: enum edited + - ENUM_IN_FSV_5081: enum edited + - ENUM_IN_FSV_5091: enum edited + - ENUM_IN_FSV_2093: enum edited + - ENUM_IN_FSV_2094: enum edited + - VAR_IN_FSV_2011: desc edited + - VAR_IN_FSV_2012: desc edited + - VAR_IN_FSV_2021: desc edited + - VAR_IN_FSV_2022: desc edited + - VAR_IN_FSV_2031: desc edited + - VAR_IN_FSV_2032: desc edited + - ENUM_IN_FSV_2041: desc edited + - VAR_IN_FSV_2051: desc edited + - VAR_IN_FSV_2052: desc edited + - VAR_IN_FSV_2061: desc edited + - VAR_IN_FSV_2062: desc edited + - VAR_IN_FSV_2071: desc edited + - VAR_IN_FSV_2072: desc edited + - ENUM_IN_FSV_2093: desc edited + - VAR_IN_FSV_3021: desc edited + - VAR_IN_FSV_3022: desc edited + - VAR_IN_FSV_3023: desc edited + - VAR_IN_FSV_3024: desc edited + - VAR_IN_FSV_3025: desc edited + - VAR_IN_FSV_3026: desc edited + - VAR_IN_FSV_3032: desc edited + - VAR_IN_FSV_3033: desc edited + - VAR_IN_FSV_3041: desc edited + - VAR_IN_FSV_3042: desc edited + - VAR_IN_FSV_3043: desc edited + - VAR_IN_FSV_3044: desc edited + - VAR_IN_FSV_3045: desc edited + - VAR_IN_FSV_3046: desc edited + - ENUM_IN_FSV_3051: desc edited + - ENUM_IN_FSV_3052: desc edited + - ENUM_IN_FSV_3071: desc edited + - ENUM_IN_FSV_3081: desc edited + - ENUM_IN_FSV_3082: desc edited + - ENUM_IN_FSV_3083: desc edited + - VAR_IN_FSV_4011: desc edited + - VAR_IN_FSV_4012: desc edited + - VAR_IN_FSV_4013: desc edited + - VAR_IN_FSV_4021: desc edited + - VAR_IN_FSV_4022: desc edited + - VAR_IN_FSV_4023: desc edited + - VAR_IN_FSV_4024: desc edited + - VAR_IN_FSV_4025: desc edited + - VAR_IN_FSV_4031: desc edited + - VAR_IN_FSV_4032: desc edited + - VAR_IN_FSV_4033: desc edited + - VAR_IN_FSV_4041: desc edited + - VAR_IN_FSV_4042: desc edited + - VAR_IN_FSV_4043: desc edited + - VAR_IN_FSV_4044: desc edited + - VAR_IN_FSV_4045: desc edited + - VAR_IN_FSV_4046: desc edited + - VAR_IN_FSV_4051: desc edited + - VAR_IN_FSV_4052: desc edited + - VAR_IN_FSV_4053: desc edited + - VAR_IN_FSV_4061: desc edited + - VAR_IN_FSV_5011: desc edited + - VAR_IN_FSV_5012: desc edited + - VAR_IN_FSV_5013: desc edited + - VAR_IN_FSV_5014: desc edited + - VAR_IN_FSV_5015: desc edited + - VAR_IN_FSV_5016: desc edited + - VAR_IN_FSV_5017: desc edited + - VAR_IN_FSV_5018: desc edited + - VAR_IN_FSV_5019: desc edited + - VAR_IN_FSV_5021: desc edited + - VAR_IN_FSV_5023: desc edited + - ENUM_IN_FSV_5022: desc edited + - ENUM_IN_FSV_5041: desc edited + - ENUM_IN_FSV_5042: desc edited + - ENUM_IN_FSV_5043: desc edited + - ENUM_IN_FSV_5051: desc edited + - VAR_IN_FSV_5083: desc edited + - VAR_IN_FSV_5082: desc edited + - ENUM_IN_FSV_5081: desc edited + - ENUM_IN_FSV_5091: desc edited + - ENUM_IN_FSV_5094: desc edited + - VAR_IN_FSV_5092: desc edited + - VAR_IN_FSV_5093: desc edited + ### v0.1.0Beta - 2025-02-08 - Initial Commit \ No newline at end of file diff --git a/data/NasaRepository.yml b/data/NasaRepository.yml index 6556fdb..bbde411 100644 --- a/data/NasaRepository.yml +++ b/data/NasaRepository.yml @@ -203,8 +203,9 @@ enum: ENUM_IN_FSV_2041: address: '0x4093' arithmetic: '' - description: FSV Water Law Type Heating + description: Heating - Heating WL Selection WL Type enum: + 0x00: 'Unknown' 0x01: 'Floor' 0x02: 'FCU' remarks: 1 Floor, 2 FCU @@ -214,8 +215,9 @@ ENUM_IN_FSV_2041: ENUM_IN_FSV_2081: address: '0x4094' arithmetic: '' - description: FSV Water Law Type Cooling + description: Cooling - Cooling WL Selection WL Type enum: + 0x00: 'Unknown' 0x01: 'Floor' 0x02: 'FCU' remarks: 1 Floor, 2 FCU @@ -225,12 +227,12 @@ ENUM_IN_FSV_2081: ENUM_IN_FSV_2093: address: '0x4127' arithmetic: '' - description: '' + description: Remote Controller - Remote Controller Room Temp. Control enum: - 0x01: 'one' - 0x02: 'two' - 0x03: 'three' - 0x04: 'four' + 0x01: 'Off (1min delay) by Room Sensor' + 0x02: 'Off (1min delay) by Room Sensor or WL' + 0x03: 'On' + 0x04: 'Repeat 3min On/7min Off by Room Sensor or WL' remarks: Min = 1 Max = 4 signed: '' type: ENUM @@ -245,6 +247,7 @@ ENUM_IN_FSV_2094: 0x02: 'two' 0x03: 'three' 0x04: 'four' + 0xFF: 'off' remarks: values 0="No" up to 4="4" signed: '' type: ENUM @@ -252,10 +255,10 @@ ENUM_IN_FSV_2094: ENUM_IN_FSV_3041: address: '0x4099' arithmetic: '' - description: '' + description: DHW - Disinfection Application enum: - 0x00: 'No' - 0x01: 'Yes' + 0x00: 'Off' + 0x01: 'on' remarks: 0 No, 1 Yes signed: '' type: ENUM @@ -263,7 +266,7 @@ ENUM_IN_FSV_3041: ENUM_IN_FSV_3042: address: '0x409A' arithmetic: '' - description: '' + description: DHW - Disinfection Interval enum: 0x00: 'Sunday' 0x01: 'Monday' @@ -280,7 +283,7 @@ ENUM_IN_FSV_3042: ENUM_IN_FSV_3051: address: '0x409B' arithmetic: '' - description: '' + description: DHW - Forced DHW Operation Timer OFF Function enum: 0x00: 'No' 0x01: 'Yes' @@ -291,7 +294,10 @@ ENUM_IN_FSV_3051: ENUM_IN_FSV_3071: address: '0x409D' arithmetic: '' - description: '' + description: DHW - Direction of 3Way Valve DHW Tank + enum: + 0x00: 'Room' + 0x01: 'Tank' remarks: '' signed: '' type: ENUM @@ -299,7 +305,7 @@ ENUM_IN_FSV_3071: ENUM_IN_FSV_4011: address: '0x409E' arithmetic: '' - description: '' + description: Heating - Heat Pump Heating/ DHW Priority enum: 0x00: 'DHW' 0x01: 'Heating' @@ -310,11 +316,11 @@ ENUM_IN_FSV_4011: ENUM_IN_FSV_4021: address: '0x409F' arithmetic: '' - description: '' + description: Heating - Backup Heater Application enum: - 0x00: 'zero' - 0x01: 'one' - 0x02: 'two' + 0x00: 'No' + 0x01: '2 Step BUH1+BUH2' + 0x02: '1 Step BUH2' remarks: '' signed: '' type: ENUM @@ -322,7 +328,7 @@ ENUM_IN_FSV_4021: ENUM_IN_FSV_4022: address: '0x40A0' arithmetic: '' - description: '' + description: Heating - Backup Heater BUH/BSH Priority enum: 0x00: 'BUH_BSH_Both' 0x01: 'BUH' @@ -334,7 +340,7 @@ ENUM_IN_FSV_4022: ENUM_IN_FSV_4023: address: '0x40A1' arithmetic: '' - description: '' + description: Heating - Backup Heater Cold Weather Compensation enum: 0x00: 'No' 0x01: 'Yes' @@ -345,7 +351,7 @@ ENUM_IN_FSV_4023: ENUM_IN_FSV_4031: address: '0x40A2' arithmetic: '' - description: '' + description: Heating - Backup Boiler Application enum: 0x00: 'No' 0x01: 'Yes' @@ -356,7 +362,7 @@ ENUM_IN_FSV_4031: ENUM_IN_FSV_4032: address: '0x40A3' arithmetic: '' - description: '' + description: Heating - Backup Boiler Priority enum: 0x00: 'No' 0x01: 'Yes' @@ -367,11 +373,11 @@ ENUM_IN_FSV_4032: ENUM_IN_FSV_4041: address: '0x40C0' arithmetic: '' - description: '' + description: Heating - Mixing Valve Application enum: 0x00: 'No' - 0x01: 'one' - 0x02: 'two' + 0x01: 'FSV_TARGET_DELTA' + 0x02: 'WL' remarks: '' signed: '' type: ENUM @@ -379,7 +385,13 @@ ENUM_IN_FSV_4041: ENUM_IN_FSV_4044: address: '0x40C1' arithmetic: '' - description: '' + description: Heating - Mixing Valve Control Factor + enum: + 0x01: '1' + 0x02: '2' + 0x03: '3' + 0x04: '4' + 0x05: '5' remarks: '' signed: '' type: ENUM @@ -387,10 +399,11 @@ ENUM_IN_FSV_4044: ENUM_IN_FSV_4051: address: '0x40C2' arithmetic: '' - description: '' + description: Heating - Inverter Pump Application enum: 0x00: 'No' - 0x01: 'Yes' + 0x01: '100%' + 0x02: '70%' remarks: '' signed: '' type: ENUM @@ -398,10 +411,11 @@ ENUM_IN_FSV_4051: ENUM_IN_FSV_4053: address: '0x40C3' arithmetic: '' - description: '' + description: Heating - Inverter Pump Control Factor enum: - 0x00: 'No' - 0x01: 'Yes' + 0x01: '1' + 0x02: '2' + 0x03: '3' remarks: '' signed: '' type: ENUM @@ -409,10 +423,10 @@ ENUM_IN_FSV_4053: ENUM_IN_FSV_4061: address: '0x411A' arithmetic: '' - description: '' + description: Heating - Zone Control Application enum: - 0x00: 'zero' - 0x01: 'one' + 0x00: 'No' + 0x01: 'Yes' remarks: '' signed: '' type: ENUM @@ -420,10 +434,10 @@ ENUM_IN_FSV_4061: ENUM_IN_FSV_5022: address: '0x4128' arithmetic: '' - description: '' + description: DHW Saving - DHW Saving Mode enum: - 0x00: 'min' - 0x01: 'max' + 0x00: 'ON' + 0x01: 'OFF' remarks: Min = 0 Max = 1 signed: '' type: ENUM @@ -442,7 +456,7 @@ ENUM_IN_FSV_5033: ENUM_IN_FSV_5041: address: '0x40A4' arithmetic: '' - description: '' + description: Power Peak Control - Application enum: 0x00: 'No' 0x01: 'Yes' @@ -453,12 +467,12 @@ ENUM_IN_FSV_5041: ENUM_IN_FSV_5042: address: '0x40A5' arithmetic: '' - description: '' + description: Power Peak Control - Select Forced Off Parts enum: - 0x00: 'All' - 0x01: 'one' - 0x02: 'two' - 0x03: 'three' + 0x00: 'COMP_PER_BUH_OFF_BSH_PER' + 0x01: 'COMP_PER_BUH_OFF_BSH_OFF' + 0x02: 'COMP_OFF_BUH_OFF_BSH_PER' + 0x03: 'COMP_OFF_BUH_OFF_BSH_OFF' remarks: '' signed: '' type: ENUM @@ -466,7 +480,7 @@ ENUM_IN_FSV_5042: ENUM_IN_FSV_5043: address: '0x40A6' arithmetic: '' - description: '' + description: Power Peak Control - Using Input Voltage enum: 0x00: 'Low' 0x01: 'High' @@ -477,10 +491,10 @@ ENUM_IN_FSV_5043: ENUM_IN_FSV_5051: address: '0x40A7' arithmetic: '' - description: '' + description: Frequency Ratio Control enum: - 0x00: 'No' - 0x01: 'Yes' + 0x00: 'Disable' + 0x01: 'Use' remarks: '' signed: '' type: ENUM @@ -499,10 +513,10 @@ ENUM_IN_FSV_5061: ENUM_IN_FSV_5081: address: '0x411B' arithmetic: '' - description: '' + description: PV Control - Application enum: - 0x00: 'zero' - 0x01: 'one' + 0x00: 'No' + 0x01: 'Yes' remarks: '' signed: '' type: ENUM @@ -510,10 +524,10 @@ ENUM_IN_FSV_5081: ENUM_IN_FSV_5091: address: '0x411C' arithmetic: '' - description: '' + description: Smart Grid Control - Application enum: - 0x00: 'zero' - 0x01: 'one' + 0x00: 'No' + 0x01: 'Yes' remarks: '' signed: '' type: ENUM @@ -521,10 +535,10 @@ ENUM_IN_FSV_5091: ENUM_IN_FSV_5094: address: '0x411D' arithmetic: '' - description: '' + description: Smart Grid Control - DHW Mode enum: - 0x00: 'zero' - 0x01: 'one' + 0x00: '55° by HP' + 0x01: '70° by HP and BSH' remarks: '' signed: '' type: ENUM @@ -1227,6 +1241,45 @@ LVAR_IN_ETO_HEAT_CONTROL_DATA: signed: 'false' type: LVAR unit: '' +LVAR_IN_MINUTES_SINCE_INSTALLATION: + address: '0x4423' + arithmetic: '' + description: Minutes since installation + remarks: + state_class: total_increasing + device_class: duration + signed: 'false' + type: LVAR + unit: 'min' +LVAR_IN_MINUTES_ACTIVE: + address: '0x4424' + arithmetic: '' + description: Minutes active + remarks: + state_class: total_increasing + device_class: duration + signed: 'false' + type: LVAR + unit: 'min' +LVAR_IN_GENERATED_POWER_LAST_MINUTE: + address: '0x4426' + arithmetic: value / 1000 + description: Generated power last minute + remarks: value is kWh, so do div 1000 + device_class: energy + signed: 'false' + type: LVAR + unit: kWh +LVAR_IN_TOTAL_GENERATED_POWER: + address: '0x4427' + arithmetic: value / 1000 + description: Total generated power + remarks: value is kWh, so do div 1000 + state_class: total_increasing + device_class: energy + signed: 'false' + type: LVAR + unit: kWh LVAR_OUT_AUTO_INSPECT_RESULT0: address: '0x840B' arithmetic: '' @@ -2752,7 +2805,7 @@ NASA_OUTDOOR_COMP2_TARGET_HZ: remarks: '' signed: 'false' type: VAR - uunit: 'Hz' + unit: 'Hz' NASA_OUTDOOR_COMP3_ORDER_HZ: address: '0x82C0' arithmetic: '' @@ -3295,12 +3348,12 @@ NASA_OUTDOOR_HOTGAS2: unit: '' NASA_OUTDOOR_HP: address: '0x8287' - arithmetic: '' - description: Outdoor unit horsepower + arithmetic: 'value * 0.7457' + description: Outdoor unit horsepower (in kw) remarks: unknown UNIT "HP" signed: 'false' type: VAR - unit: HP + unit: kw NASA_OUTDOOR_HREEV: address: '0x822F' arithmetic: '' @@ -4820,7 +4873,7 @@ VAR_IN_FLOW_SENSOR_VOLTAGE: VAR_IN_FSV_2011: address: '0x4254' arithmetic: value / 10 - description: Water Law Auto heating ambient temperature - Max. + description: Heating - Outdoor Temp. for WL Max. remarks: '' signed: 'true' type: VAR @@ -4828,7 +4881,7 @@ VAR_IN_FSV_2011: VAR_IN_FSV_2012: address: '0x4255' arithmetic: value / 10 - description: '' + description: Heating - Outdoor Temp. for WL Min. remarks: '' signed: 'true' type: VAR @@ -4836,7 +4889,7 @@ VAR_IN_FSV_2012: VAR_IN_FSV_2021: address: '0x4256' arithmetic: value / 10 - description: Water Law (WL1-Floor) Temperature auto heating - Max. + description: Heating - Water out Temp. UFH Max. remarks: '' signed: 'true' type: VAR @@ -4844,7 +4897,7 @@ VAR_IN_FSV_2021: VAR_IN_FSV_2022: address: '0x4257' arithmetic: value / 10 - description: '' + description: Heating - Water out Temp. UFH Min. remarks: '' signed: 'true' type: VAR @@ -4852,7 +4905,7 @@ VAR_IN_FSV_2022: VAR_IN_FSV_2031: address: '0x4258' arithmetic: value / 10 - description: Water Law (WL2-FCU) Temperature auto heating - Max. + description: Heating - Water out Temp. FCU Max. remarks: '' signed: 'true' type: VAR @@ -4860,7 +4913,7 @@ VAR_IN_FSV_2031: VAR_IN_FSV_2032: address: '0x4259' arithmetic: value / 10 - description: '' + description: Heating - Water out Temp. FCU Min. remarks: '' signed: 'true' type: VAR @@ -4868,7 +4921,7 @@ VAR_IN_FSV_2032: VAR_IN_FSV_2051: address: '0x425A' arithmetic: value / 10 - description: '' + description: Cooling - Outdoor Temp. for WL Max. remarks: '' signed: 'true' type: VAR @@ -4876,7 +4929,7 @@ VAR_IN_FSV_2051: VAR_IN_FSV_2052: address: '0x425B' arithmetic: value / 10 - description: '' + description: Cooling - Outdoor Temp. for WL Min. remarks: '' signed: 'true' type: VAR @@ -4884,7 +4937,7 @@ VAR_IN_FSV_2052: VAR_IN_FSV_2061: address: '0x425C' arithmetic: value / 10 - description: '' + description: Cooling - Water out Temp. UFH Max. remarks: '' signed: 'true' type: VAR @@ -4892,7 +4945,7 @@ VAR_IN_FSV_2061: VAR_IN_FSV_2062: address: '0x425D' arithmetic: value / 10 - description: '' + description: Cooling - Water out Temp. UFH Min. remarks: '' signed: 'true' type: VAR @@ -4900,7 +4953,7 @@ VAR_IN_FSV_2062: VAR_IN_FSV_2071: address: '0x425E' arithmetic: value / 10 - description: '' + description: Cooling - Water out Temp. FCU Max. remarks: '' signed: 'true' type: VAR @@ -4908,7 +4961,7 @@ VAR_IN_FSV_2071: VAR_IN_FSV_2072: address: '0x425F' arithmetic: value / 10 - description: '' + description: Cooling - Water out Temp. FCU Min. remarks: '' signed: 'true' type: VAR @@ -4916,7 +4969,7 @@ VAR_IN_FSV_2072: VAR_IN_FSV_3021: address: '0x4260' arithmetic: value / 10 - description: DHW Heating mode - Max. + description: DHW - Heat Pump Max. temp. remarks: '' signed: 'true' type: VAR @@ -4924,7 +4977,7 @@ VAR_IN_FSV_3021: VAR_IN_FSV_3022: address: '0x4261' arithmetic: value / 10 - description: '' + description: DHW - Heat Pump Stop remarks: '' signed: 'true' type: VAR @@ -4932,7 +4985,7 @@ VAR_IN_FSV_3022: VAR_IN_FSV_3023: address: '0x4262' arithmetic: value / 10 - description: DHW Heating mode - Start + description: DHW - Heat Pump Start remarks: '' signed: 'true' type: VAR @@ -4940,7 +4993,7 @@ VAR_IN_FSV_3023: VAR_IN_FSV_3024: address: '0x4263' arithmetic: '' - description: '' + description: DHW - Heat Pump Min. Space heating operation time remarks: '' signed: 'false' type: VAR @@ -4948,7 +5001,7 @@ VAR_IN_FSV_3024: VAR_IN_FSV_3025: address: '0x4264' arithmetic: '' - description: DHW Heating mode - DHW operation time + description: DHW - Heat Pump Max. DHW operation time remarks: '' signed: 'false' type: VAR @@ -4956,7 +5009,7 @@ VAR_IN_FSV_3025: VAR_IN_FSV_3026: address: '0x4265' arithmetic: '' - description: '' + description: DHW - Heat Pump Max. Space heating operation time remarks: '' signed: 'false' type: VAR @@ -4964,7 +5017,7 @@ VAR_IN_FSV_3026: VAR_IN_FSV_3032: address: '0x4266' arithmetic: '' - description: DHW Booster heater - Delayed time + description: DHW - Booster Heat Delay Time remarks: '' signed: 'false' type: VAR @@ -4972,7 +5025,7 @@ VAR_IN_FSV_3032: VAR_IN_FSV_3033: address: '0x4267' arithmetic: value / 10 - description: '' + description: DHW - Booster Heat Overshoot remarks: '' signed: 'true' type: VAR @@ -4988,7 +5041,7 @@ VAR_IN_FSV_3034: VAR_IN_FSV_3043: address: '0x4269' arithmetic: '' - description: '' + description: DHW - Disinfection Start Time remarks: '' signed: 'false' type: VAR @@ -4996,7 +5049,7 @@ VAR_IN_FSV_3043: VAR_IN_FSV_3044: address: '0x426A' arithmetic: value / 10 - description: Desinfection - Target temp. + description: DHW - Disinfection Target Temp. remarks: '' signed: 'true' type: VAR @@ -5004,7 +5057,7 @@ VAR_IN_FSV_3044: VAR_IN_FSV_3045: address: '0x426B' arithmetic: '' - description: '' + description: DHW - Disinfection Duration remarks: '' signed: 'true' type: VAR @@ -5012,7 +5065,7 @@ VAR_IN_FSV_3045: VAR_IN_FSV_3046: address: '0x42CE' arithmetic: value / 60 - description: DHW Desinfection - Max. operation time + description: DHW - Disinfection Max time remarks: NASA Value is [minutes], not [hours] signed: 'false' type: VAR @@ -5020,7 +5073,7 @@ VAR_IN_FSV_3046: VAR_IN_FSV_3052: address: '0x426C' arithmetic: value / 0.1 - description: '' + description: DHW - Forced DHW Operation Time Duration remarks: '' signed: 'true' type: VAR @@ -5028,7 +5081,7 @@ VAR_IN_FSV_3052: VAR_IN_FSV_3081: address: '0x42ED' arithmetic: '' - description: '' + description: DHW - Energy Metering BUH 1 step capacity remarks: '' signed: 'true' type: VAR @@ -5036,7 +5089,7 @@ VAR_IN_FSV_3081: VAR_IN_FSV_3082: address: '0x42EE' arithmetic: '' - description: '' + description: DHW - Energy Metering BUH 2 step capacity remarks: '' signed: 'true' type: VAR @@ -5044,7 +5097,7 @@ VAR_IN_FSV_3082: VAR_IN_FSV_3083: address: '0x42EF' arithmetic: '' - description: '' + description: DHW - Energy Metering BSH capacity remarks: '' signed: 'true' type: VAR @@ -5052,7 +5105,7 @@ VAR_IN_FSV_3083: VAR_IN_FSV_4012: address: '0x426D' arithmetic: value / 10 - description: '' + description: Heating - Heat Pump Outdoor Temp. for Priority remarks: '' signed: 'true' type: VAR @@ -5060,7 +5113,7 @@ VAR_IN_FSV_4012: VAR_IN_FSV_4013: address: '0x426E' arithmetic: value / 10 - description: Heating mode - Heating Off + description: Heating - Heat Pump Outdoor Heat OFF remarks: '' signed: 'true' type: VAR @@ -5076,7 +5129,7 @@ VAR_IN_FSV_4014: VAR_IN_FSV_4024: address: '0x4270' arithmetic: value / 10 - description: '' + description: Heating - Backup Heater Threshold Temp. remarks: '' signed: 'true' type: VAR @@ -5084,7 +5137,7 @@ VAR_IN_FSV_4024: VAR_IN_FSV_4025: address: '0x4271' arithmetic: value / 10 - description: '' + description: Heating - Backup Heater Defrost Backup Temp. remarks: '' signed: 'true' type: VAR @@ -5092,7 +5145,7 @@ VAR_IN_FSV_4025: VAR_IN_FSV_4033: address: '0x4272' arithmetic: value / 10 - description: '' + description: Heating - Backup Boiler Threshold Temp. remarks: '' signed: 'true' type: VAR @@ -5100,7 +5153,7 @@ VAR_IN_FSV_4033: VAR_IN_FSV_4042: address: '0x4286' arithmetic: value / 10 - description: '' + description: Heating - Mixing Valve Target Delta Heating remarks: '' signed: 'false' type: VAR @@ -5108,7 +5161,7 @@ VAR_IN_FSV_4042: VAR_IN_FSV_4043: address: '0x4287' arithmetic: value / 10 - description: '' + description: Heating - Mixing Valve Target Delta Cooling remarks: '' signed: 'false' type: VAR @@ -5116,7 +5169,7 @@ VAR_IN_FSV_4043: VAR_IN_FSV_4045: address: '0x4288' arithmetic: '' - description: '' + description: Heating - Mixing Valve Control Interval remarks: '' signed: 'false' type: VAR @@ -5124,7 +5177,7 @@ VAR_IN_FSV_4045: VAR_IN_FSV_4046: address: '0x4289' arithmetic: value / 0.1 - description: '' + description: Heating - Mixing Valve Running Time remarks: '' signed: 'false' type: VAR @@ -5132,7 +5185,7 @@ VAR_IN_FSV_4046: VAR_IN_FSV_4052: address: '0x428A' arithmetic: value / 10 - description: '' + description: Heating - Inverter Pump Target Delta remarks: '' signed: 'false' type: VAR @@ -5140,7 +5193,7 @@ VAR_IN_FSV_4052: VAR_IN_FSV_5011: address: '0x4273' arithmetic: value / 10 - description: '' + description: Outing Mode - Water Out Temp. for Cooling remarks: '' signed: 'true' type: VAR @@ -5148,7 +5201,7 @@ VAR_IN_FSV_5011: VAR_IN_FSV_5012: address: '0x4274' arithmetic: value / 10 - description: Outing mode - Room Temperature of cooling Mode + description: Outing mode - Room Temp. for Cooling remarks: '' signed: 'true' type: VAR @@ -5156,7 +5209,7 @@ VAR_IN_FSV_5012: VAR_IN_FSV_5013: address: '0x4275' arithmetic: value / 10 - description: '' + description: Outing mode - Water Out Temp. for Heating remarks: '' signed: 'true' type: VAR @@ -5164,7 +5217,7 @@ VAR_IN_FSV_5013: VAR_IN_FSV_5014: address: '0x4276' arithmetic: value / 10 - description: Outing mode- Indoor heating temperature + description: Outing mode - Room Temp. for Heating remarks: '' signed: 'true' type: VAR @@ -5172,7 +5225,7 @@ VAR_IN_FSV_5014: VAR_IN_FSV_5015: address: '0x4277' arithmetic: value / 10 - description: '' + description: Outing mode - Auto Cooling WL1 Temp. remarks: '' signed: 'true' type: VAR @@ -5180,7 +5233,7 @@ VAR_IN_FSV_5015: VAR_IN_FSV_5016: address: '0x4278' arithmetic: value / 10 - description: '' + description: Outing mode - Auto Cooling WL2 Temp. remarks: '' signed: 'true' type: VAR @@ -5188,7 +5241,7 @@ VAR_IN_FSV_5016: VAR_IN_FSV_5017: address: '0x4279' arithmetic: value / 10 - description: '' + description: Outing mode - Auto Heating WL1 Temp remarks: '' signed: 'true' type: VAR @@ -5196,7 +5249,7 @@ VAR_IN_FSV_5017: VAR_IN_FSV_5018: address: '0x427A' arithmetic: value / 10 - description: Outing mode - Temperature of auto heating WL2 water + description: Outing mode - Auto Heating WL2 Temp remarks: '' signed: 'true' type: VAR @@ -5204,7 +5257,7 @@ VAR_IN_FSV_5018: VAR_IN_FSV_5019: address: '0x427B' arithmetic: value / 10 - description: '' + description: Outing mode - Target Tank Temp remarks: '' signed: 'true' type: VAR @@ -5212,7 +5265,7 @@ VAR_IN_FSV_5019: VAR_IN_FSV_5021: address: '0x427C' arithmetic: value / 10 - description: Economic DHW mode - Temperature of hot water Tank + description: DHW Saving - DHW Saving Temp. remarks: '' signed: 'true' type: VAR @@ -5220,7 +5273,7 @@ VAR_IN_FSV_5021: VAR_IN_FSV_5023: address: '0x42F0' arithmetic: value / 10 - description: '' + description: DHW Saving - DHW Saving Thermo on Temp. remarks: '' signed: 'true' type: VAR @@ -5244,7 +5297,7 @@ VAR_IN_FSV_5032: VAR_IN_FSV_5082: address: '0x42DB' arithmetic: value / 10 - description: '' + description: PV Control - Setting Temp. Shift Value (Cool) remarks: '' signed: 'false' type: VAR @@ -5252,7 +5305,7 @@ VAR_IN_FSV_5082: VAR_IN_FSV_5083: address: '0x42DC' arithmetic: value / 10 - description: '' + description: PV Control - Setting Temp. Shift Value (Heat) remarks: '' signed: 'false' type: VAR @@ -5260,7 +5313,7 @@ VAR_IN_FSV_5083: VAR_IN_FSV_5092: address: '0x42DD' arithmetic: value / 10 - description: '' + description: Smart Grid Control - Setting Temp. Shift Value (Heat) remarks: '' signed: 'false' type: VAR @@ -5268,7 +5321,7 @@ VAR_IN_FSV_5092: VAR_IN_FSV_5093: address: '0x42DE' arithmetic: value / 10 - description: '' + description: Smart Grid Control - Setting Temp. Shift Value (DHW) remarks: '' signed: 'false' type: VAR @@ -5793,6 +5846,15 @@ VAR_OUT_SENSOR_TOTAL_SUCTION: signed: 'true' type: VAR unit: "\u00b0C" +NASA_EHSSENTINEL_TOTAL_COP: + address: '0x9997' + arithmetic: '' + description: Total COP of lifetime + remarks: Custom Measurment + state_class: measurement + signed: 'true' + type: VAR + unit: '' NASA_EHSSENTINEL_COP: address: '0x9998' arithmetic: '' @@ -5810,4 +5872,4 @@ NASA_EHSSENTINEL_HEAT_OUTPUT: state_class: measurement signed: 'true' type: VAR - unit: "W" + unit: "W" \ No newline at end of file diff --git a/data/config.yml b/data/config.yml index 6834216..26d4a0a 100644 --- a/data/config.yml +++ b/data/config.yml @@ -1,6 +1,10 @@ general: nasaRepositoryFile: data/NasaRepository.yml - silentMode: True +logging: + deviceAdded: True + messageNotFound: False + packetNotFromIndoorOutdoor: False + proccessedMessage: False serial: device: /dev/ttyUSB0 baudrate: 9600 diff --git a/data/sampleDump_small.txt b/data/sampleDump_small.txt index eb88a7f..a10b747 100644 --- a/data/sampleDump_small.txt +++ b/data/sampleDump_small.txt @@ -1,2 +1,2 @@ -[50, 0, 60, 16, 0, 0, 176, 0, 255, 192, 20, 27, 13, 2, 2, 255, 255, 4, 16, 0, 0, 0, 0, 4, 27, 0, 32, 255, 255, 128, 0, 0, 128, 5, 255, 128, 23, 0, 128, 25, 0, 128, 26, 0, 128, 33, 1, 128, 50, 1, 128, 51, 2, 128, 60, 0, 128, 69, 0, 63, 195, 52] -[50, 0, 62, 16, 0, 0, 176, 0, 255, 192, 20, 28, 16, 128, 99, 1, 128, 102, 0, 128, 119, 0, 128, 120, 2, 128, 121, 0, 128, 122, 0, 128, 123, 0, 128, 124, 0, 128, 125, 0, 128, 126, 0, 128, 127, 255, 128, 131, 0, 128, 142, 255, 128, 169, 0, 128, 175, 0, 128, 177, 0, 145, 101, 52] +['0x32', '0x0', '0x3e', '0x10', '0x0', '0x0', '0xb0', '0x0', '0xff', '0xc0', '0x14', '0xc7', '0xc', '0x82', '0x18', '0xff', '0xdb', '0x82', '0x1a', '0xff', '0xd5', '0x82', '0x1e', '0x0', '0x8b', '0x82', '0x24', '0x0', '0xd6', '0x82', '0x25', '0x1', '0x5c', '0x82', '0x26', '0x0', '0x1b', '0x82', '0x29', '0x0', '0xe9', '0x82', '0x2e', '0x0', '0x50', '0x82', '0x31', '0x0', '0x64', '0x82', '0x34'] +['0x32', '0x0', '0x12', '0x10', '0x0', '0x0', '0xb0', '0x0', '0xff', '0xc0', '0x14', '0x5a', '0x1', '0x82', '0x1c', '0x1', '0x38', '0x1f', '0x34'] \ No newline at end of file diff --git a/helpertils/TestPacketGenerator.py b/helpertils/TestPacketGenerator.py new file mode 100644 index 0000000..76d5c46 --- /dev/null +++ b/helpertils/TestPacketGenerator.py @@ -0,0 +1,230 @@ +import json +import NASAPacket +import NASAMessage + + +encode_raw = "[50, 0, 17, 16, 0, 0, 176, 0, 255, 192, 20, 143, 1, 128, 49, 0, 157, 7, 52]" +encode_raw = "[50, 0, 60, 16, 0, 0, 176, 0, 255, 192, 20, 196, 13, 2, 2, 255, 255, 4, 16, 0, 0, 0, 0, 4, 27, 0, 32, 255, 255, 128, 0, 0, 128, 5, 255, 128, 23, 0, 128, 25, 0, 128, 26, 0, 128, 33, 1, 128, 50, 255, 128, 51, 0, 128, 60, 0, 128, 69, 0, 240, 94, 52]" +encode_raw = "[50, 0, 56, 98, 0, 144, 178, 0, 32, 192, 17, 3, 11, 64, 147, 0, 64, 148, 0, 66, 115, 0, 0, 66, 116, 0, 0, 66, 117, 0, 0, 66, 118, 0, 0, 66, 119, 0, 0, 66, 120, 0, 0, 66, 121, 0, 0, 66, 122, 0, 0, 66, 123, 0, 0, 221, 200, 52]" +encode_raw = "[50, 0, 56, 98, 0, 144, 178, 0, 32, 192, 17, 240, 11, 64, 147, 0, 64, 148, 0, 66, 115, 0, 0, 66, 116, 0, 0, 66, 117, 0, 0, 66, 118, 0, 0, 66, 119, 0, 0, 66, 120, 0, 0, 66, 121, 0, 0, 66, 122, 0, 0, 66, 123, 0, 0, 76, 33, 52]" +#encode_raw ="[50, 0, 48, 98, 0, 144, 178, 0, 32, 192, 17, 240, 11, 64, 147, 0, 64, 148, 0, 66, 115, 0, 0, 66, 116, 0, 66, 117, 0, 66, 118, 0, 66, 119, 0, 66, 120, 0, 66, 121, 0, 66, 122, 0, 66, 123, 0, 7, 180, 52]" +try: + encode_bytearray = json.loads(encode_raw.strip()) # for [12, 234, 456 ,67] +except: + encode_tmp = encode_raw.strip().replace("'", "").replace("[", "").replace("]", "").split(", ") # for ['0x1', '0x2' ..] + encode_bytearray = [int(value, 16) for value in encode_tmp] + + +print(f"encode raw: {bytearray(encode_bytearray)}") +print(f"encode bytearray: {encode_bytearray}") +print(f"encode bytearray length: {len(encode_bytearray)}") + +encoded_nasa = NASAPacket.NASAPacket() +encoded_nasa.parse(encode_bytearray) + +print(f"encode NASA Object: {encoded_nasa}") + +# time to reverse that thing! +decoded_nasa = NASAPacket.NASAPacket() +decoded_nasa.set_packet_source_address_class(NASAPacket.AddressClassEnum.Outdoor) +decoded_nasa.set_packet_source_channel(0) +decoded_nasa.set_packet_source_address(0) +decoded_nasa.set_packet_dest_address_class(NASAPacket.AddressClassEnum.BroadcastSelfLayer) +decoded_nasa.set_packet_dest_channel(0) +decoded_nasa.set_packet_dest_address(255) +decoded_nasa.set_packet_information(True) +decoded_nasa.set_packet_version(2) +decoded_nasa.set_packet_retry_count(0) +decoded_nasa.set_packet_type(NASAPacket.PacketType.Normal) +decoded_nasa.set_packet_data_type(NASAPacket.DataType.Notification) +decoded_nasa.set_packet_number(143) +tmp_msg = NASAMessage.NASAMessage() +tmp_msg.set_packet_message(0x8031) +tmp_msg.set_packet_message_type(0) +tmp_msg.set_packet_payload([0]) +decoded_nasa.set_packet_messages([tmp_msg]) + +decoded_nasa = NASAPacket.NASAPacket() +decoded_nasa.set_packet_source_address_class(NASAPacket.AddressClassEnum.Outdoor) +decoded_nasa.set_packet_source_channel(0) +decoded_nasa.set_packet_source_address(0) +decoded_nasa.set_packet_dest_address_class(NASAPacket.AddressClassEnum.BroadcastSelfLayer) +decoded_nasa.set_packet_dest_channel(0) +decoded_nasa.set_packet_dest_address(255) +decoded_nasa.set_packet_information(True) +decoded_nasa.set_packet_version(2) +decoded_nasa.set_packet_retry_count(0) +decoded_nasa.set_packet_type(NASAPacket.PacketType.Normal) +decoded_nasa.set_packet_data_type(NASAPacket.DataType.Notification) +decoded_nasa.set_packet_number(196) +lst = [] +tmp_msg = NASAMessage.NASAMessage() +tmp_msg.set_packet_message(0x0202) +tmp_msg.set_packet_message_type(1) +tmp_msg.set_packet_payload([255, 255]) +lst.append(tmp_msg) +tmp_msg = NASAMessage.NASAMessage() +tmp_msg.set_packet_message(0x0410) +tmp_msg.set_packet_message_type(2) +tmp_msg.set_packet_payload([0, 0, 0, 0]) +lst.append(tmp_msg) +tmp_msg = NASAMessage.NASAMessage() +tmp_msg.set_packet_message(0x41b) +tmp_msg.set_packet_message_type(2) +tmp_msg.set_packet_payload([0, 32, 255, 255]) +lst.append(tmp_msg) +tmp_msg = NASAMessage.NASAMessage() +tmp_msg.set_packet_message(0x8000) +tmp_msg.set_packet_message_type(0) +tmp_msg.set_packet_payload([0]) +lst.append(tmp_msg) +tmp_msg = NASAMessage.NASAMessage() +tmp_msg.set_packet_message(0x8005) +tmp_msg.set_packet_message_type(0) +tmp_msg.set_packet_payload([255]) +lst.append(tmp_msg) +tmp_msg = NASAMessage.NASAMessage() +tmp_msg.set_packet_message(0x8017) +tmp_msg.set_packet_message_type(0) +tmp_msg.set_packet_payload([0]) +lst.append(tmp_msg) +tmp_msg = NASAMessage.NASAMessage() +tmp_msg.set_packet_message(0x8019) +tmp_msg.set_packet_message_type(0) +tmp_msg.set_packet_payload([0]) +lst.append(tmp_msg) +tmp_msg = NASAMessage.NASAMessage() +tmp_msg.set_packet_message(0x801a) +tmp_msg.set_packet_message_type(0) +tmp_msg.set_packet_payload([0]) +lst.append(tmp_msg) +tmp_msg = NASAMessage.NASAMessage() +tmp_msg.set_packet_message(0x8021) +tmp_msg.set_packet_message_type(0) +tmp_msg.set_packet_payload([1]) +lst.append(tmp_msg) +tmp_msg = NASAMessage.NASAMessage() +tmp_msg.set_packet_message(0x8032) +tmp_msg.set_packet_message_type(0) +tmp_msg.set_packet_payload([255]) +lst.append(tmp_msg) +tmp_msg = NASAMessage.NASAMessage() +tmp_msg.set_packet_message(0x8033) +tmp_msg.set_packet_message_type(0) +tmp_msg.set_packet_payload([0]) +lst.append(tmp_msg) +tmp_msg = NASAMessage.NASAMessage() +tmp_msg.set_packet_message(0x803c) +tmp_msg.set_packet_message_type(0) +tmp_msg.set_packet_payload([0]) +lst.append(tmp_msg) +tmp_msg = NASAMessage.NASAMessage() +tmp_msg.set_packet_message(0x8045) +tmp_msg.set_packet_message_type(0) +tmp_msg.set_packet_payload([0]) +lst.append(tmp_msg) +decoded_nasa.set_packet_messages(lst) + +#rasw = decoded_nasa.to_raw() + +decoded_nasa = NASAPacket.NASAPacket() +decoded_nasa.set_packet_source_address_class(NASAPacket.AddressClassEnum.WiFiKit) +decoded_nasa.set_packet_source_channel(0) +decoded_nasa.set_packet_source_address(144) +decoded_nasa.set_packet_dest_address_class(NASAPacket.AddressClassEnum.BroadcastSetLayer) +decoded_nasa.set_packet_dest_channel(0) +decoded_nasa.set_packet_dest_address(32) +decoded_nasa.set_packet_information(True) +decoded_nasa.set_packet_version(2) +decoded_nasa.set_packet_retry_count(0) +decoded_nasa.set_packet_type(NASAPacket.PacketType.Normal) +decoded_nasa.set_packet_data_type(NASAPacket.DataType.Read) +decoded_nasa.set_packet_number(240) +lst = [] +tmp_msg = NASAMessage.NASAMessage() +tmp_msg.set_packet_message(0x4093) +tmp_msg.set_packet_message_type(0) +tmp_msg.set_packet_payload([0]) +lst.append(tmp_msg) +tmp_msg = NASAMessage.NASAMessage() +tmp_msg.set_packet_message(0x4094) +tmp_msg.set_packet_message_type(0) +tmp_msg.set_packet_payload([0]) +lst.append(tmp_msg) +tmp_msg = NASAMessage.NASAMessage() +tmp_msg.set_packet_message(0x4273) +tmp_msg.set_packet_message_type(1) +tmp_msg.set_packet_payload([0, 0]) +lst.append(tmp_msg) +tmp_msg = NASAMessage.NASAMessage() +tmp_msg.set_packet_message(0x4274) +tmp_msg.set_packet_message_type(1) +tmp_msg.set_packet_payload([0, 0]) +lst.append(tmp_msg) +tmp_msg = NASAMessage.NASAMessage() +tmp_msg.set_packet_message(0x4275) +tmp_msg.set_packet_message_type(1) +tmp_msg.set_packet_payload([0, 0]) +lst.append(tmp_msg) +tmp_msg = NASAMessage.NASAMessage() +tmp_msg.set_packet_message(0x4276) +tmp_msg.set_packet_message_type(1) +tmp_msg.set_packet_payload([0, 0]) +lst.append(tmp_msg) +tmp_msg = NASAMessage.NASAMessage() +tmp_msg.set_packet_message(0x4277) +tmp_msg.set_packet_message_type(1) +tmp_msg.set_packet_payload([0, 0]) +lst.append(tmp_msg) +tmp_msg = NASAMessage.NASAMessage() +tmp_msg.set_packet_message(0x4278) +tmp_msg.set_packet_message_type(1) +tmp_msg.set_packet_payload([0, 0]) +lst.append(tmp_msg) +tmp_msg = NASAMessage.NASAMessage() +tmp_msg.set_packet_message(0x4279) +tmp_msg.set_packet_message_type(1) +tmp_msg.set_packet_payload([0, 0]) +lst.append(tmp_msg) +tmp_msg = NASAMessage.NASAMessage() +tmp_msg.set_packet_message(0x427a) +tmp_msg.set_packet_message_type(1) +tmp_msg.set_packet_payload([0, 0]) +lst.append(tmp_msg) +tmp_msg = NASAMessage.NASAMessage() +tmp_msg.set_packet_message(0x427b) +tmp_msg.set_packet_message_type(1) +tmp_msg.set_packet_payload([0, 0]) +lst.append(tmp_msg) +decoded_nasa.set_packet_messages(lst) + +decoded_nasa = NASAPacket.NASAPacket() +decoded_nasa.set_packet_source_address_class(NASAPacket.AddressClassEnum.WiFiKit) +decoded_nasa.set_packet_source_channel(0) +decoded_nasa.set_packet_source_address(144) +decoded_nasa.set_packet_dest_address_class(NASAPacket.AddressClassEnum.BroadcastSelfLayer) +decoded_nasa.set_packet_dest_channel(255) +decoded_nasa.set_packet_dest_address(255) +decoded_nasa.set_packet_information(True) +decoded_nasa.set_packet_version(2) +decoded_nasa.set_packet_retry_count(0) +decoded_nasa.set_packet_type(NASAPacket.PacketType.Normal) +decoded_nasa.set_packet_data_type(NASAPacket.DataType.Notification) +decoded_nasa.set_packet_number(168) +tmp_msg = NASAMessage.NASAMessage() +tmp_msg.set_packet_message(0x0000) +tmp_msg.set_packet_message_type(0) +tmp_msg.set_packet_payload([2]) +decoded_nasa.set_packet_messages([tmp_msg]) + + +rasw = decoded_nasa.to_raw() + +print(f"decoded bytearray: {rasw}") +print(f"decoded NASA Object: {decoded_nasa}") +print(f"decoded bytearray: {[int(value) for value in rasw]}") + +print("Reverse Check:") +checkback = NASAPacket.NASAPacket() +checkback.parse(rasw) +print(checkback) \ No newline at end of file diff --git a/startEHSSentinel.py b/startEHSSentinel.py index 2bb21d9..a3fffe7 100644 --- a/startEHSSentinel.py +++ b/startEHSSentinel.py @@ -5,7 +5,7 @@ import traceback from MessageProcessor import MessageProcessor from EHSArguments import EHSArguments from EHSConfig import EHSConfig -from EHSExceptions import MessageWarningException, MessageCapacityStructureWarning +from EHSExceptions import MessageWarningException, SkipInvalidPacketException from MQTTClient import MQTTClient import aiofiles import json @@ -13,10 +13,11 @@ import struct import binascii # Get the logger -from CustomLogger import logger, setSilent -from NASAPacket import NASAPacket +from CustomLogger import logger +from NASAPacket import NASAPacket, AddressClassEnum, PacketType, DataType +from NASAMessage import NASAMessage -version = "0.1.0 Stable" +version = "0.2.0 Stable" async def main(): """ @@ -26,9 +27,8 @@ async def main(): 2. Reads command-line arguments. 3. Reads configuration settings. 4. Connects to the MQTT broker. - 5. Sets silent mode if specified in the configuration. - 6. If dry run mode is enabled, reads data from a dump file and processes it. - 7. If not in dry run mode, reads data from a serial port and processes it. + 5. If dry run mode is enabled, reads data from a dump file and processes it. + 6. If not in dry run mode, reads data from a serial port and processes it. Args: None Returns: @@ -61,10 +61,6 @@ async def main(): await asyncio.sleep(1) - # if Silent is true, set Silent Mode - if config.GENERAL['silentMode']: - setSilent() - # if dryrun then we read from dumpfile if args.DRYRUN: logger.info(f"DRYRUN detected, reading from dumpfile {args.DUMPFILE}") @@ -75,12 +71,12 @@ async def main(): except: line = line.strip().replace("'", "").replace("[", "").replace("]", "").split(", ") # for ['0x1', '0x2' ..] line = [int(value, 16) for value in line] - await process_packet(line, args) + await process_packet(line, args, config) else: # we are not in dryrun mode, so we need to read from Serial Pimort - await serial_read(config, args) + await serial_connection(config, args) -async def process_buffer(buffer, args): +async def process_buffer(buffer, args, config): """ Processes a buffer of data asynchronously, identifying and handling packets based on specific criteria. Args: @@ -108,14 +104,14 @@ async def process_buffer(buffer, args): for i in range(0, len(buffer)): if buffer[i] == 0x32: if (len(buffer[i:]) > 14): - asyncio.create_task(process_packet(buffer[i:], args)) + asyncio.create_task(process_packet(buffer[i:], args, config)) else: logger.debug(f"Buffermessages to short for NASA {len(buffer)}") break else: logger.debug(f"Buffer to short for NASA {len(buffer)}") -async def serial_read(config, args): +async def serial_connection(config, args): """ Asynchronously reads data from a serial connection and processes it. Args: @@ -147,25 +143,28 @@ async def serial_read(config, args): stopbits=serial.STOPBITS_ONE, bytesize=serial.EIGHTBITS, rtscts=True, - timeout=0 + timeout=1 ) - # start the async buffer process - #asyncio.create_task(process_buffer(buffer, args))# start the async buffer process - - # TODO have to be tested and verified, please do not try it yet - # start the async writer process - #asyncio.create_task(serial_write(writer, reader)) + await asyncio.gather( + serial_read(reader, args, config), + #serial_write(writer, reader, args), + ) - # Read loop + +async def serial_read(reader, args, config): while True: data = await reader.readuntil(b'\x34') # Read up to end of next message 0x34 if data: - asyncio.create_task(process_buffer(data, args)) + asyncio.create_task(process_buffer(data, args, config)) #buffer.extend(data) + logger.debug(f"Received: {data}") + logger.debug(f"Received: {data!r}") logger.debug(f"Received: {[hex(x) for x in data]}") -async def serial_write(writer, reader): + await asyncio.sleep(0.1) # Yield control to other tasks + +async def serial_write(writer:asyncio.StreamWriter, reader: asyncio.StreamReader, args): """ TODO Not used yet, only for future use... @@ -183,36 +182,85 @@ async def serial_write(writer, reader): await asyncio.sleep(5) # Example data to write - packet = bytearray([ - #0x32, # Packet Start Byte - #0x00, 0x12, # Packet Size - 0x80, # Source Address Class JIGTester - 0xFF, # Source Channel - 0x00, # Source Address - 0x20, # Destination Address Class Indoor - 0x00, # Destination Channel - 0x00, # Destination Address - 0xC0, # Packet Information + Protocol Version + Retry Count - 0x11, # Packet Type [Normal = 1] + Data Type [Read = 1] - 0xF0, # Packet Number - 0x01, # Capacity (Number of Messages) - 0x42, 0x56, # NASA Message Number - 0x00, 0x00 # Message Payload (placeholder for return value) - ]) - - crc=binascii.crc_hqx(packet, 0) - # NOTE: include length of CRC(2) and length of length field(2) in the - # total length, exclude SF/TF of total length - final_packet = struct.pack(">BH", 0x32, len(packet)+2+2) + packet + struct.pack(">HB", crc, 0x34) - # ['0x32', '0x0', '0x12', '0x80', '0xff', '0x0', '0x20', '0x0', '0x0', '0xc0', '0x11', '0xf0', '0x1', '0x42', '0x56', '0x0', '0x0', '0xf9', '0x65', '0x34'] - # ['0x32', '0x0', '0x12', '0x80', '0xff', '0x0', '0x20', '0x0', '0x0', '0xc0', '0x11', '0xf0', '0x1', '0x42', '0x56', '0x0', '0x0', '0x38', '0xc6', '0x34'] + decoded_nasa = NASAPacket() + decoded_nasa.set_packet_source_address_class(AddressClassEnum.WiFiKit) + decoded_nasa.set_packet_source_channel(0) + decoded_nasa.set_packet_source_address(144) + decoded_nasa.set_packet_dest_address_class(AddressClassEnum.BroadcastSetLayer) + decoded_nasa.set_packet_dest_channel(0) + decoded_nasa.set_packet_dest_address(32) + decoded_nasa.set_packet_information(True) + decoded_nasa.set_packet_version(2) + decoded_nasa.set_packet_retry_count(0) + decoded_nasa.set_packet_type(PacketType.Normal) + decoded_nasa.set_packet_data_type(DataType.Read) + decoded_nasa.set_packet_number(3) + lst = [] + tmp_msg = NASAMessage() + tmp_msg.set_packet_message(0x4093) + tmp_msg.set_packet_message_type(0) + tmp_msg.set_packet_payload([0]) + lst.append(tmp_msg) + tmp_msg = NASAMessage() + tmp_msg.set_packet_message(0x4094) + tmp_msg.set_packet_message_type(0) + tmp_msg.set_packet_payload([0]) + lst.append(tmp_msg) + tmp_msg = NASAMessage() + tmp_msg.set_packet_message(0x4273) + tmp_msg.set_packet_message_type(1) + tmp_msg.set_packet_payload([0, 0]) + lst.append(tmp_msg) + tmp_msg = NASAMessage() + tmp_msg.set_packet_message(0x4274) + tmp_msg.set_packet_message_type(1) + tmp_msg.set_packet_payload([0, 0]) + lst.append(tmp_msg) + tmp_msg = NASAMessage() + tmp_msg.set_packet_message(0x4275) + tmp_msg.set_packet_message_type(1) + tmp_msg.set_packet_payload([0, 0]) + lst.append(tmp_msg) + tmp_msg = NASAMessage() + tmp_msg.set_packet_message(0x4276) + tmp_msg.set_packet_message_type(1) + tmp_msg.set_packet_payload([0, 0]) + lst.append(tmp_msg) + tmp_msg = NASAMessage() + tmp_msg.set_packet_message(0x4277) + tmp_msg.set_packet_message_type(1) + tmp_msg.set_packet_payload([0, 0]) + lst.append(tmp_msg) + tmp_msg = NASAMessage() + tmp_msg.set_packet_message(0x4278) + tmp_msg.set_packet_message_type(1) + tmp_msg.set_packet_payload([0, 0]) + lst.append(tmp_msg) + tmp_msg = NASAMessage() + tmp_msg.set_packet_message(0x4279) + tmp_msg.set_packet_message_type(1) + tmp_msg.set_packet_payload([0, 0]) + lst.append(tmp_msg) + tmp_msg = NASAMessage() + tmp_msg.set_packet_message(0x427a) + tmp_msg.set_packet_message_type(1) + tmp_msg.set_packet_payload([0, 0]) + lst.append(tmp_msg) + tmp_msg = NASAMessage() + tmp_msg.set_packet_message(0x427b) + tmp_msg.set_packet_message_type(1) + tmp_msg.set_packet_payload([0, 0]) + lst.append(tmp_msg) + decoded_nasa.set_packet_messages(lst) + final_packet = decoded_nasa.to_raw() writer.write(final_packet) await writer.drain() - logger.debug(f"Sent data raw: {final_packet}") - logger.debug(f"Sent data raw: {[hex(x) for x in final_packet]}") - await asyncio.sleep(1) # Adjust the interval as needed + logger.info(f"Sent data raw: {final_packet}") + logger.info(f"Sent data raw: {decoded_nasa}") + logger.info(f"Sent data raw: {[hex(x) for x in final_packet]}") + logger.info(f"Sent data raw: {[x for x in final_packet]}") -async def process_packet(buffer, args): +async def process_packet(buffer, args, config): """ Asynchronously processes a packet buffer. If `dumpWriter` is `None`, it attempts to process the packet using `MessageProcessor`. @@ -233,13 +281,29 @@ async def process_packet(buffer, args): logger.debug("Packet processed: ") logger.debug(f"Packet raw: {[hex(x) for x in buffer]}") logger.debug(nasa_packet) - messageProcessor = MessageProcessor() - messageProcessor.process_message(nasa_packet) + if nasa_packet.packet_source_address_class in (AddressClassEnum.Outdoor, AddressClassEnum.Indoor): + messageProcessor = MessageProcessor() + messageProcessor.process_message(nasa_packet) + elif nasa_packet.packet_source_address_class == AddressClassEnum.WiFiKit and \ + nasa_packet.packet_dest_address_class == AddressClassEnum.BroadcastSelfLayer and \ + nasa_packet.packet_data_type == DataType.Notification: + pass + else: + if config.LOGGING['packetNotFromIndoorOutdoor']: + logger.info("Message not From Indoor or Outdoor") + logger.info(nasa_packet) + logger.info(f"Packet int: {[x for x in buffer]}") + logger.info(f"Packet hex: {[hex(x) for x in buffer]}") + else: + logger.debug("Message not From Indoor or Outdoor") + logger.debug(nasa_packet) + logger.debug(f"Packet int: {[x for x in buffer]}") + logger.debug(f"Packet hex: {[hex(x) for x in buffer]}") except ValueError as e: logger.warning("Value Error on parsing Packet, Packet will be skipped") logger.warning(f"Error processing message: {e}") logger.warning(f"Complete Packet: {[hex(x) for x in buffer]}") - except MessageCapacityStructureWarning as e: + except SkipInvalidPacketException as e: logger.debug("Warnung accured, Packet will be skipped") logger.debug(f"Error processing message: {e}") logger.debug(f"Complete Packet: {[hex(x) for x in buffer]}")