Feature/v0.2.0 (#7)

* heateroutput limit 0 - 15000 w

* heatoutput always positive

* ENUM_IN_FSV_2041 value 0c00 unknwon added

* recalculate crc6 checksum and check it

* skip exception to reduce logging

* NASAPacket and NASAMessage prepared for write mode
MQTT Auto Discovery changed single entitity to all entities
NASA Packet crc16 Checksum verificated

* - Changed MQTT Auto Discovery Config Message from single Entitiy to all Entities at ones, known devices are fully configured, not known empty (markt to delete)
- NASAPacket and NASAMessage are now bidirectional, can decode and encode Packets
- Added crc16 Checksum check for any Packet to reduce incorrect value changes
- Folling warnings moved to SkipInvalidPacketException and from warning to debug log level to reduce Logentries
  - 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.

* NASA_OUTDOOR_HP as kw unit

* NASA Repository, measurements enums completed

* filter wifikit heartbeat

* process only packets from indoor or outdoor

* correct readme

* remove expire

* device discovery status

* new mqtt hass configuration approach

* added new measurements

* added new logging features from config


* NASA_EHSSENTINEL_TOTAL_COP added

* removed silentMode, added logging proccessedMessage

* loaded devices

* loaded devices counter fix

* only if retain true

* final 0.2.0 commit
This commit is contained in:
echoDaveD
2025-02-22 22:45:18 +01:00
committed by GitHub
parent cce625dabb
commit 48ef003f22
13 changed files with 1069 additions and 386 deletions

View File

@@ -63,13 +63,6 @@ class IndentFormatter(logging.Formatter):
del rec.indent; del rec.function del rec.indent; del rec.function
return out 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. # The following code sets up a custom logger with indentation support.
# It creates a custom formatter, a logger instance, and a stream handler. # 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. # 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.setLevel(logging.DEBUG)
logger.debug("Debug mode is on...") 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())

View File

@@ -26,6 +26,7 @@ class EHSConfig():
GENERAL = None GENERAL = None
SERIAL = None SERIAL = None
NASA_REPO = None NASA_REPO = None
LOGGING = {}
def __new__(cls, *args, **kwargs): def __new__(cls, *args, **kwargs):
""" """
@@ -77,6 +78,10 @@ class EHSConfig():
self.MQTT = config.get('mqtt') self.MQTT = config.get('mqtt')
self.GENERAL = config.get('general') self.GENERAL = config.get('general')
self.SERIAL = config.get('serial') self.SERIAL = config.get('serial')
if 'logging' in config:
self.LOGGING = config.get('logging')
else:
self.LOGGING = {}
logger.debug(f"Configuration loaded: {config}") logger.debug(f"Configuration loaded: {config}")
self.validate() self.validate()
@@ -97,9 +102,6 @@ class EHSConfig():
self.NASA_REPO = yaml.safe_load(file) self.NASA_REPO = yaml.safe_load(file)
else: else:
raise ConfigException(argument=self.GENERAL['nasaRepositoryFile'], message="NASA Respository File is missing") 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: if 'protocolFile' not in self.GENERAL:
self.GENERAL['protocolFile'] = None self.GENERAL['protocolFile'] = None
@@ -132,4 +134,24 @@ class EHSConfig():
raise ConfigException(argument=self.SERIAL['device'], message="mqtt user parameter is missing") raise ConfigException(argument=self.SERIAL['device'], message="mqtt user parameter is missing")
if 'password' not in self.MQTT and 'user' in self.MQTT: if 'password' not in self.MQTT and 'user' in self.MQTT:
raise ConfigException(argument=self.SERIAL['device'], message="mqtt password parameter is missing") 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}")

View File

@@ -46,8 +46,8 @@ class ArgumentException(EHSException):
def __str__(self): def __str__(self):
return f'{self.argument} -> {self.message}' return f'{self.argument} -> {self.message}'
class MessageCapacityStructureWarning(EHSException): class SkipInvalidPacketException(EHSException):
"""Exception raised for invalid message types. """Exception raised for invalid message types.
Attributes: Attributes:

View File

@@ -13,25 +13,9 @@ from EHSConfig import EHSConfig
class MQTTClient: class MQTTClient:
""" """
MQTTClient is a singleton class that manages the connection to an MQTT broker and handles MQTTClient is a singleton class that manages the connection and communication with an MQTT broker.
publishing and subscribing to topics. It is designed to work with Home Assistant for It handles the initialization, connection, subscription, and message publishing for the MQTT client.
auto-discovery of devices and sensors. The class also supports Home Assistant auto-discovery and maintains a list of known devices.
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.
""" """
_instance = None _instance = None
STOP = asyncio.Event() STOP = asyncio.Event()
@@ -40,17 +24,16 @@ class MQTTClient:
def __new__(cls, *args, **kwargs): def __new__(cls, *args, **kwargs):
""" """
Create a new instance of the MQTTClient class if one does not already exist. Create a new instance of the class if one does not already exist.
This method ensures that the MQTTClient class follows the Singleton design pattern, This method ensures that only one instance of the class is created (singleton pattern).
meaning only one instance of the class can exist at any given time. If an instance If an instance already exists, it returns the existing instance.
already exists, it returns the existing instance. Otherwise, it creates a new instance Otherwise, it creates a new instance, marks it as uninitialized, and returns it.
and sets the _initialized attribute to False.
Args: Args:
cls (type): The class being instantiated. cls: The class being instantiated.
*args: Variable length argument list. *args: Variable length argument list.
**kwargs: Arbitrary keyword arguments. **kwargs: Arbitrary keyword arguments.
Returns: Returns:
MQTTClient: The single instance of the MQTTClient class. An instance of the class.
""" """
if not cls._instance: if not cls._instance:
@@ -60,26 +43,25 @@ class MQTTClient:
def __init__(self): def __init__(self):
""" """
Initialize the MQTTClient instance. Initializes the MQTTClient instance.
This constructor initializes the MQTT client with the configuration This constructor sets up the MQTT client with the necessary configuration
provided by the EHSConfig and EHSArguments classes. It sets up the parameters, including broker URL, port, client ID, and authentication credentials.
MQTT broker connection details, client ID, and authentication credentials It also assigns callback functions for various MQTT events such as connect,
if provided. It also assigns callback functions for various MQTT events disconnect, message, and subscribe. Additionally, it initializes topic-related
such as connect, disconnect, message, and subscribe. Additionally, it settings and a list to keep track of known topics.
initializes the topic prefix, Home Assistant auto-discover topic, and
topic naming convention.
Attributes: Attributes:
config (EHSConfig): Configuration object for the MQTT client. config (EHSConfig): Configuration object for MQTT settings.
args (EHSArguments): Argument parser object for the MQTT client. args (EHSArguments): Argument parser object.
broker (str): URL of the MQTT broker. broker (str): URL of the MQTT broker.
port (int): Port number of the MQTT broker. port (int): Port number of the MQTT broker.
client_id (str): Client ID for the MQTT connection. 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. topicPrefix (str): Prefix for MQTT topics.
homeAssistantAutoDiscoverTopic (str): Topic for Home Assistant auto-discovery. homeAssistantAutoDiscoverTopic (str): Topic for Home Assistant auto-discovery.
useCamelCaseTopicNames (bool): Flag to use camel case for topic names. 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_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: if self._initialized:
@@ -108,17 +90,15 @@ class MQTTClient:
async def connect(self): async def connect(self):
""" """
Asynchronously connects to the MQTT broker and optionally clears the known devices topic. 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 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 broker address and port, and sets the keepalive interval. If the CLEAN_KNOWN_DEVICES argument
argument is set to True, it publishes an empty message to the known devices topic to clear it. is set, it publishes an empty message to the known devices topic to clear it.
Args: Args:
None None
Returns: Returns:
None None
Raises:
Any exceptions raised by the underlying MQTT client library during connection.
""" """
logger.info("[MQTT] Connecting to broker...") logger.info("[MQTT] Connecting to broker...")
await self.client.connect(self.broker, self.port, keepalive=60, version=gmqtt.constants.MQTTv311) 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): def subscribe_known_topics(self):
""" """
Subscribes the MQTT client to known topics. Subscribe to predefined MQTT topics.
This method subscribes the MQTT client to two specific topics: This method subscribes the MQTT client to a set of known topics, which include:
1. A topic for known devices, constructed using the topic prefix and known devices topic. - A topic for known devices, constructed using the topic prefix and known devices topic.
2. A status topic for Home Assistant auto-discovery. - A status topic for Home Assistant auto-discovery.
The subscription QoS (Quality of Service) level for both topics is set to 1. The subscription is done with a QoS level of 1 for both topics.
Logging: Logging:
Logs an informational message indicating that the client is subscribing to known devices topic. - Logs an info message indicating the subscription to known devices topic.
Raises:
Any exceptions raised by the gmqtt.Subscription or self.client.subscribe methods.
""" """
logger.info("Subscribe to known devices topic") logger.info("Subscribe to known devices topic")
@@ -158,7 +136,7 @@ class MQTTClient:
Returns: Returns:
None None
""" """
logger.debug('SUBSCRIBED') logger.debug('SUBSCRIBED')
def on_message(self, client, topic, payload, qos, properties): 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. topic (str): The topic that the message was received on.
payload (bytes): The message payload. payload (bytes): The message payload.
qos (int): The quality of service level of the message. qos (int): The quality of service level of the message.
properties (paho.mqtt.properties.Properties): The properties of the message. properties (paho.mqtt.properties.Properties): The properties associated with the message.
Behavior: This function performs the following actions:
- If the topic matches the known devices topic, updates the known devices set with the retained message. - 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, logs the status message and clears the known devices topic. - 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: if self.known_devices_topic in topic:
# Update the known devices set with the retained message # Update the known devices set with the retained message
self.known_topics = list(filter(None, [x.strip() for x in payload.decode().split(",")])) 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: if f"{self.homeAssistantAutoDiscoverTopic}/status" == topic:
logger.info(f"HASS Status Messages {topic} received: {payload.decode()}") logger.info(f"HASS Status Messages {topic} received: {payload.decode()}")
if payload.decode() == "online": if payload.decode() == "online":
self._publish(f"{self.topicPrefix.replace('/', '')}/{self.known_devices_topic}", " ", retain=True) self._publish(f"{self.topicPrefix.replace('/', '')}/{self.known_devices_topic}", " ", retain=True)
logger.info("Known Devices Topic have been cleared") logger.info("Known Devices Topic have been cleared")
self.clear_hass()
def refresh_known_devices(self, devname): logger.info("All configuration from HASS has been resetet")
"""
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)
def on_connect(self, client, flags, rc, properties): def on_connect(self, client, flags, rc, properties):
""" """
Callback function for when the client receives a CONNACK response from the server. Callback function for when the client receives a CONNACK response from the server.
Args: Parameters:
client (paho.mqtt.client.Client): The client instance for this callback. client (paho.mqtt.client.Client): The client instance for this callback.
flags (dict): Response flags sent by the broker. flags (dict): Response flags sent by the broker.
rc (int): The connection result. rc (int): The connection result.
properties (paho.mqtt.properties.Properties): The properties associated with the connection. properties (paho.mqtt.properties.Properties): The properties associated with the connection.
Returns: If the connection is successful (rc == 0), logs a success message and subscribes to known topics if any.
None Otherwise, logs an error message with the return code.
Logs:
- Info: When connected successfully with result code 0.
- Error: When failed to connect with a non-zero result code.
""" """
if rc == 0: if rc == 0:
logger.info(f"Connected to MQTT with result code {rc}") logger.info(f"Connected to MQTT with result code {rc}")
if len(self.homeAssistantAutoDiscoverTopic) > 0: if len(self.homeAssistantAutoDiscoverTopic) > 0:
@@ -222,14 +197,13 @@ class MQTTClient:
def on_disconnect(self, client, packet, exc=None): def on_disconnect(self, client, packet, exc=None):
""" """
Callback function that is called when the client disconnects from the MQTT broker. 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: Args:
client (paho.mqtt.client.Client): The MQTT client instance that disconnected. client (paho.mqtt.client.Client): The MQTT client instance that disconnected.
packet (paho.mqtt.client.MQTTMessage): The MQTT message packet received during disconnection. packet (paho.mqtt.packet.Packet): The disconnect packet.
exc (Exception, optional): The exception that caused the disconnection, if any. Defaults to None. exc (Exception, optional): The exception that caused the disconnection, if any.
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.
""" """
logger.info(f"Disconnected with result code ") logger.info(f"Disconnected with result code ")
@@ -244,11 +218,11 @@ class MQTTClient:
def _publish(self, topic, payload, qos=0, retain=False): 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: Args:
topic (str): The MQTT topic to publish to. topic (str): The MQTT topic to publish to.
payload (str): The message payload to publish. 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. retain (bool, optional): If True, the message will be retained by the broker. Defaults to False.
Returns: Returns:
None None
@@ -257,33 +231,51 @@ class MQTTClient:
logger.debug(f"MQTT Publish Topic: {topic} payload: {payload}") logger.debug(f"MQTT Publish Topic: {topic} payload: {payload}")
self.client.publish(f"{topic}", payload, qos, retain) self.client.publish(f"{topic}", payload, qos, retain)
#time.sleep(0.1) #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): def publish_message(self, name, value):
""" """
Publishes a message to an MQTT topic. Publishes a message to an MQTT topic.
This method normalizes the given name, determines the appropriate MQTT topic, This function normalizes the given name, determines the appropriate MQTT topic,
and publishes the provided value to that topic. If Home Assistant auto-discovery and publishes the provided value to that topic. It also handles Home Assistant
is enabled, it will also handle the auto-discovery configuration. auto-discovery if configured.
Args: Args:
name (str): The name of the sensor or device. 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, 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: 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)}" newname = f"{self._normalize_name(name)}"
if len(self.homeAssistantAutoDiscoverTopic) > 0: 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" sensor_type = "sensor"
if 'enum' in self.config.NASA_REPO[name]: if 'enum' in self.config.NASA_REPO[name]:
enum = [*self.config.NASA_REPO[name]['enum'].values()] enum = [*self.config.NASA_REPO[name]['enum'].values()]
if all([en.lower() in ['on', 'off'] for en in enum]): if all([en.lower() in ['on', 'off'] for en in enum]):
sensor_type = "binary_sensor" sensor_type = "binary_sensor"
topicname = f"{self.config.MQTT['homeAssistantAutoDiscoverTopic']}/{sensor_type}/{self.DEVICE_ID}_{newname.lower()}/state" 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: else:
topicname = f"{self.topicPrefix.replace('/', '')}/{newname}" topicname = f"{self.topicPrefix.replace('/', '')}/{newname}"
@@ -292,78 +284,24 @@ class MQTTClient:
self._publish(topicname, value, qos=2, retain=False) self._publish(topicname, value, qos=2, retain=False)
def auto_discover_hass(self, topicname, nameraw, namenorm, sensor_type): def clear_hass(self):
"""
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
""" """
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 = {
"device": { "device": self._get_device(),
"identifiers": self.DEVICE_ID, "origin": self._get_origin(),
"name": "Samsung EHS", "components": entities,
"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,
"qos": 2 "qos": 2
} }
logger.debug(f"Auto Discovery HomeAssistant Message: ") logger.debug(f"Auto Discovery HomeAssistant Clear Message: ")
logger.debug(f"{device}") logger.debug(f"{device}")
self._publish(f"{self.config.MQTT['homeAssistantAutoDiscoverTopic']}/device/{self.DEVICE_ID}/config", self._publish(f"{self.config.MQTT['homeAssistantAutoDiscoverTopic']}/device/{self.DEVICE_ID}/config",
@@ -371,9 +309,99 @@ class MQTTClient:
qos=2, qos=2,
retain=True) retain=True)
self.known_topics.append(nameraw) def auto_discover_hass(self, name):
self.refresh_known_devices(nameraw) """
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): def _normalize_name(self, name):
""" """
@@ -405,3 +433,19 @@ class MQTTClient:
tmpname = name tmpname = name
return tmpname 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

View File

@@ -2,7 +2,7 @@ import asyncio
import logging import logging
import traceback import traceback
import yaml import yaml
from CustomLogger import logger, setSilent from CustomLogger import logger
from EHSArguments import EHSArguments from EHSArguments import EHSArguments
from EHSConfig import EHSConfig from EHSConfig import EHSConfig
from EHSExceptions import MessageWarningException from EHSExceptions import MessageWarningException
@@ -53,7 +53,6 @@ class MessageProcessor:
if self._initialized: if self._initialized:
return return
self._initialized = True self._initialized = True
logger.debug("init MessageProcessor")
self.config = EHSConfig() self.config = EHSConfig()
self.args = EHSArguments() self.args = EHSArguments()
self.mqtt = MQTTClient() 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}") 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) self.protocolMessage(msg, msgname, msgvalue)
else: 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): def protocolMessage(self, msg: NASAMessage, msgname, msgvalue):
""" """
@@ -98,7 +100,10 @@ class MessageProcessor:
- Calculates and processes derived values for specific message names. - 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: if self.config.GENERAL['protocolFile'] is not None:
with open(self.config.GENERAL['protocolFile'], "a") as protWriter: 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 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']): 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), value = round(
"NASA_EHSSENTINEL_HEAT_OUTPUT", abs(
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)
(self.NASA_VAL_STORE['NASA_OUTDOOR_TW2_TEMP'] - self.NASA_VAL_STORE['NASA_OUTDOOR_TW1_TEMP']) * * 4190
(self.NASA_VAL_STORE['VAR_IN_FLOW_SENSOR_CALC']/60) ) , 4
* 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 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']): 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), self.protocolMessage(NASAMessage(packet_message=0x9998, packet_message_type=1),
"NASA_EHSSENTINEL_COP", "NASA_EHSSENTINEL_COP",
round((self.NASA_VAL_STORE['NASA_EHSSENTINEL_HEAT_OUTPUT'] / self.NASA_VAL_STORE['NASA_OUTDOOR_CONTROL_WATTMETER_ALL_UNIT']/1000.), 3)) 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): def search_nasa_table(self, address):
""" """
@@ -157,7 +173,7 @@ class MessageProcessor:
try: try:
value = eval(arithmetic) value = eval(arithmetic)
except Exception as e: 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 value = packed_value
else: else:
value = packed_value value = packed_value

View File

@@ -48,12 +48,53 @@ class NASAMessage:
self.packet_message_type: int = packet_message_type self.packet_message_type: int = packet_message_type
self.packet_payload: bytes = bytes([int(hex(x), 16) for x in packet_payload]) 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): def __str__(self):
return ( return (
f"NASAMessage(\n" 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_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")" f")"
) )

View File

@@ -1,6 +1,8 @@
from enum import Enum from enum import Enum
from NASAMessage import NASAMessage from NASAMessage import NASAMessage
from EHSExceptions import MessageCapacityStructureWarning from EHSExceptions import SkipInvalidPacketException
import binascii
import struct
class AddressClassEnum(Enum): class AddressClassEnum(Enum):
""" """
@@ -210,13 +212,21 @@ class NASAPacket:
self._packet_raw = packet self._packet_raw = packet
if len(packet) < 14: if len(packet) < 14:
raise ValueError("Data too short to be a valid NASAPacket") 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_start = packet[0]
self.packet_size = ((packet[1] << 8) | packet[2]) 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_channel = packet[4]
self.packet_source_address = packet[5] 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_channel = packet[7]
self.packet_dest_address = packet[8] self.packet_dest_address = packet[8]
self.packet_information = (int(packet[9]) & 128) >> 7 == 1 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_data_type = DataType(int(packet[10]) & 15)
self.packet_number = packet[11] self.packet_number = packet[11]
self.packet_capacity = packet[12] 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_end = packet[-1]
self.packet_messages = self._extract_messages(0, self.packet_capacity, packet[13:-3], []) 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): 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. Recursively extracts messages from a bytearray and appends them to a list.
@@ -260,7 +273,7 @@ class NASAPacket:
elif message_type == 3: elif message_type == 3:
payload_size = len(msg_rest) payload_size = len(msg_rest)
if capacity != 1: 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: else:
raise ValueError(f"Mssage type unknown: {message_type}") raise ValueError(f"Mssage type unknown: {message_type}")
@@ -303,6 +316,84 @@ class NASAPacket:
def __repr__(self): def __repr__(self):
return self.__str__() 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: # Example usage:
# packet = NASAPacket() # packet = NASAPacket()
# packet.parse(bytearray([0x01, 0x02, 0x03, 0x04, 0x05, 0x06])) # packet.parse(bytearray([0x01, 0x02, 0x03, 0x04, 0x05, 0x06]))

134
README.md
View File

@@ -169,11 +169,20 @@ The `config.yml` file contains configuration settings for the EHS-Sentinel proje
- **nasaRepositoryFile**: Path to the NASA repository file. - **nasaRepositoryFile**: Path to the NASA repository file.
- Default: `data/NasaRepository.yml` - 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) - **protocolFile**: Path to the protocol file. (not set in Sample config.yml)
- Example: `prot.csv` - 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 ### Serial Connection Settings
- **device**: The serial device URL. - **device**: The serial device URL.
@@ -205,8 +214,12 @@ The `config.yml` file contains configuration settings for the EHS-Sentinel proje
```yaml ```yaml
general: general:
nasaRepositoryFile: data/NasaRepository.yml nasaRepositoryFile: data/NasaRepository.yml
silentMode: False
protocolFile: prot.csv protocolFile: prot.csv
logging:
deviceAdded: True
messageNotFound: False
packetNotFromIndoorOutdoor: False
proccessedMessage: False
serial: serial:
device: /dev/ttyUSB0 device: /dev/ttyUSB0
baudrate: 9600 baudrate: 9600
@@ -254,5 +267,120 @@ if you want to see how many uniquie Messages have been collected in the Dumpfile
# Changelog # 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 ### v0.1.0Beta - 2025-02-08
- Initial Commit - Initial Commit

View File

@@ -203,8 +203,9 @@ enum:
ENUM_IN_FSV_2041: ENUM_IN_FSV_2041:
address: '0x4093' address: '0x4093'
arithmetic: '' arithmetic: ''
description: FSV Water Law Type Heating description: Heating - Heating WL Selection WL Type
enum: enum:
0x00: 'Unknown'
0x01: 'Floor' 0x01: 'Floor'
0x02: 'FCU' 0x02: 'FCU'
remarks: 1 Floor, 2 FCU remarks: 1 Floor, 2 FCU
@@ -214,8 +215,9 @@ ENUM_IN_FSV_2041:
ENUM_IN_FSV_2081: ENUM_IN_FSV_2081:
address: '0x4094' address: '0x4094'
arithmetic: '' arithmetic: ''
description: FSV Water Law Type Cooling description: Cooling - Cooling WL Selection WL Type
enum: enum:
0x00: 'Unknown'
0x01: 'Floor' 0x01: 'Floor'
0x02: 'FCU' 0x02: 'FCU'
remarks: 1 Floor, 2 FCU remarks: 1 Floor, 2 FCU
@@ -225,12 +227,12 @@ ENUM_IN_FSV_2081:
ENUM_IN_FSV_2093: ENUM_IN_FSV_2093:
address: '0x4127' address: '0x4127'
arithmetic: '' arithmetic: ''
description: '' description: Remote Controller - Remote Controller Room Temp. Control
enum: enum:
0x01: 'one' 0x01: 'Off (1min delay) by Room Sensor'
0x02: 'two' 0x02: 'Off (1min delay) by Room Sensor or WL'
0x03: 'three' 0x03: 'On'
0x04: 'four' 0x04: 'Repeat 3min On/7min Off by Room Sensor or WL'
remarks: Min = 1 Max = 4 remarks: Min = 1 Max = 4
signed: '' signed: ''
type: ENUM type: ENUM
@@ -245,6 +247,7 @@ ENUM_IN_FSV_2094:
0x02: 'two' 0x02: 'two'
0x03: 'three' 0x03: 'three'
0x04: 'four' 0x04: 'four'
0xFF: 'off'
remarks: values 0="No" up to 4="4" remarks: values 0="No" up to 4="4"
signed: '' signed: ''
type: ENUM type: ENUM
@@ -252,10 +255,10 @@ ENUM_IN_FSV_2094:
ENUM_IN_FSV_3041: ENUM_IN_FSV_3041:
address: '0x4099' address: '0x4099'
arithmetic: '' arithmetic: ''
description: '' description: DHW - Disinfection Application
enum: enum:
0x00: 'No' 0x00: 'Off'
0x01: 'Yes' 0x01: 'on'
remarks: 0 No, 1 Yes remarks: 0 No, 1 Yes
signed: '' signed: ''
type: ENUM type: ENUM
@@ -263,7 +266,7 @@ ENUM_IN_FSV_3041:
ENUM_IN_FSV_3042: ENUM_IN_FSV_3042:
address: '0x409A' address: '0x409A'
arithmetic: '' arithmetic: ''
description: '' description: DHW - Disinfection Interval
enum: enum:
0x00: 'Sunday' 0x00: 'Sunday'
0x01: 'Monday' 0x01: 'Monday'
@@ -280,7 +283,7 @@ ENUM_IN_FSV_3042:
ENUM_IN_FSV_3051: ENUM_IN_FSV_3051:
address: '0x409B' address: '0x409B'
arithmetic: '' arithmetic: ''
description: '' description: DHW - Forced DHW Operation Timer OFF Function
enum: enum:
0x00: 'No' 0x00: 'No'
0x01: 'Yes' 0x01: 'Yes'
@@ -291,7 +294,10 @@ ENUM_IN_FSV_3051:
ENUM_IN_FSV_3071: ENUM_IN_FSV_3071:
address: '0x409D' address: '0x409D'
arithmetic: '' arithmetic: ''
description: '' description: DHW - Direction of 3Way Valve DHW Tank
enum:
0x00: 'Room'
0x01: 'Tank'
remarks: '' remarks: ''
signed: '' signed: ''
type: ENUM type: ENUM
@@ -299,7 +305,7 @@ ENUM_IN_FSV_3071:
ENUM_IN_FSV_4011: ENUM_IN_FSV_4011:
address: '0x409E' address: '0x409E'
arithmetic: '' arithmetic: ''
description: '' description: Heating - Heat Pump Heating/ DHW Priority
enum: enum:
0x00: 'DHW' 0x00: 'DHW'
0x01: 'Heating' 0x01: 'Heating'
@@ -310,11 +316,11 @@ ENUM_IN_FSV_4011:
ENUM_IN_FSV_4021: ENUM_IN_FSV_4021:
address: '0x409F' address: '0x409F'
arithmetic: '' arithmetic: ''
description: '' description: Heating - Backup Heater Application
enum: enum:
0x00: 'zero' 0x00: 'No'
0x01: 'one' 0x01: '2 Step BUH1+BUH2'
0x02: 'two' 0x02: '1 Step BUH2'
remarks: '' remarks: ''
signed: '' signed: ''
type: ENUM type: ENUM
@@ -322,7 +328,7 @@ ENUM_IN_FSV_4021:
ENUM_IN_FSV_4022: ENUM_IN_FSV_4022:
address: '0x40A0' address: '0x40A0'
arithmetic: '' arithmetic: ''
description: '' description: Heating - Backup Heater BUH/BSH Priority
enum: enum:
0x00: 'BUH_BSH_Both' 0x00: 'BUH_BSH_Both'
0x01: 'BUH' 0x01: 'BUH'
@@ -334,7 +340,7 @@ ENUM_IN_FSV_4022:
ENUM_IN_FSV_4023: ENUM_IN_FSV_4023:
address: '0x40A1' address: '0x40A1'
arithmetic: '' arithmetic: ''
description: '' description: Heating - Backup Heater Cold Weather Compensation
enum: enum:
0x00: 'No' 0x00: 'No'
0x01: 'Yes' 0x01: 'Yes'
@@ -345,7 +351,7 @@ ENUM_IN_FSV_4023:
ENUM_IN_FSV_4031: ENUM_IN_FSV_4031:
address: '0x40A2' address: '0x40A2'
arithmetic: '' arithmetic: ''
description: '' description: Heating - Backup Boiler Application
enum: enum:
0x00: 'No' 0x00: 'No'
0x01: 'Yes' 0x01: 'Yes'
@@ -356,7 +362,7 @@ ENUM_IN_FSV_4031:
ENUM_IN_FSV_4032: ENUM_IN_FSV_4032:
address: '0x40A3' address: '0x40A3'
arithmetic: '' arithmetic: ''
description: '' description: Heating - Backup Boiler Priority
enum: enum:
0x00: 'No' 0x00: 'No'
0x01: 'Yes' 0x01: 'Yes'
@@ -367,11 +373,11 @@ ENUM_IN_FSV_4032:
ENUM_IN_FSV_4041: ENUM_IN_FSV_4041:
address: '0x40C0' address: '0x40C0'
arithmetic: '' arithmetic: ''
description: '' description: Heating - Mixing Valve Application
enum: enum:
0x00: 'No' 0x00: 'No'
0x01: 'one' 0x01: 'FSV_TARGET_DELTA'
0x02: 'two' 0x02: 'WL'
remarks: '' remarks: ''
signed: '' signed: ''
type: ENUM type: ENUM
@@ -379,7 +385,13 @@ ENUM_IN_FSV_4041:
ENUM_IN_FSV_4044: ENUM_IN_FSV_4044:
address: '0x40C1' address: '0x40C1'
arithmetic: '' arithmetic: ''
description: '' description: Heating - Mixing Valve Control Factor
enum:
0x01: '1'
0x02: '2'
0x03: '3'
0x04: '4'
0x05: '5'
remarks: '' remarks: ''
signed: '' signed: ''
type: ENUM type: ENUM
@@ -387,10 +399,11 @@ ENUM_IN_FSV_4044:
ENUM_IN_FSV_4051: ENUM_IN_FSV_4051:
address: '0x40C2' address: '0x40C2'
arithmetic: '' arithmetic: ''
description: '' description: Heating - Inverter Pump Application
enum: enum:
0x00: 'No' 0x00: 'No'
0x01: 'Yes' 0x01: '100%'
0x02: '70%'
remarks: '' remarks: ''
signed: '' signed: ''
type: ENUM type: ENUM
@@ -398,10 +411,11 @@ ENUM_IN_FSV_4051:
ENUM_IN_FSV_4053: ENUM_IN_FSV_4053:
address: '0x40C3' address: '0x40C3'
arithmetic: '' arithmetic: ''
description: '' description: Heating - Inverter Pump Control Factor
enum: enum:
0x00: 'No' 0x01: '1'
0x01: 'Yes' 0x02: '2'
0x03: '3'
remarks: '' remarks: ''
signed: '' signed: ''
type: ENUM type: ENUM
@@ -409,10 +423,10 @@ ENUM_IN_FSV_4053:
ENUM_IN_FSV_4061: ENUM_IN_FSV_4061:
address: '0x411A' address: '0x411A'
arithmetic: '' arithmetic: ''
description: '' description: Heating - Zone Control Application
enum: enum:
0x00: 'zero' 0x00: 'No'
0x01: 'one' 0x01: 'Yes'
remarks: '' remarks: ''
signed: '' signed: ''
type: ENUM type: ENUM
@@ -420,10 +434,10 @@ ENUM_IN_FSV_4061:
ENUM_IN_FSV_5022: ENUM_IN_FSV_5022:
address: '0x4128' address: '0x4128'
arithmetic: '' arithmetic: ''
description: '' description: DHW Saving - DHW Saving Mode
enum: enum:
0x00: 'min' 0x00: 'ON'
0x01: 'max' 0x01: 'OFF'
remarks: Min = 0 Max = 1 remarks: Min = 0 Max = 1
signed: '' signed: ''
type: ENUM type: ENUM
@@ -442,7 +456,7 @@ ENUM_IN_FSV_5033:
ENUM_IN_FSV_5041: ENUM_IN_FSV_5041:
address: '0x40A4' address: '0x40A4'
arithmetic: '' arithmetic: ''
description: '' description: Power Peak Control - Application
enum: enum:
0x00: 'No' 0x00: 'No'
0x01: 'Yes' 0x01: 'Yes'
@@ -453,12 +467,12 @@ ENUM_IN_FSV_5041:
ENUM_IN_FSV_5042: ENUM_IN_FSV_5042:
address: '0x40A5' address: '0x40A5'
arithmetic: '' arithmetic: ''
description: '' description: Power Peak Control - Select Forced Off Parts
enum: enum:
0x00: 'All' 0x00: 'COMP_PER_BUH_OFF_BSH_PER'
0x01: 'one' 0x01: 'COMP_PER_BUH_OFF_BSH_OFF'
0x02: 'two' 0x02: 'COMP_OFF_BUH_OFF_BSH_PER'
0x03: 'three' 0x03: 'COMP_OFF_BUH_OFF_BSH_OFF'
remarks: '' remarks: ''
signed: '' signed: ''
type: ENUM type: ENUM
@@ -466,7 +480,7 @@ ENUM_IN_FSV_5042:
ENUM_IN_FSV_5043: ENUM_IN_FSV_5043:
address: '0x40A6' address: '0x40A6'
arithmetic: '' arithmetic: ''
description: '' description: Power Peak Control - Using Input Voltage
enum: enum:
0x00: 'Low' 0x00: 'Low'
0x01: 'High' 0x01: 'High'
@@ -477,10 +491,10 @@ ENUM_IN_FSV_5043:
ENUM_IN_FSV_5051: ENUM_IN_FSV_5051:
address: '0x40A7' address: '0x40A7'
arithmetic: '' arithmetic: ''
description: '' description: Frequency Ratio Control
enum: enum:
0x00: 'No' 0x00: 'Disable'
0x01: 'Yes' 0x01: 'Use'
remarks: '' remarks: ''
signed: '' signed: ''
type: ENUM type: ENUM
@@ -499,10 +513,10 @@ ENUM_IN_FSV_5061:
ENUM_IN_FSV_5081: ENUM_IN_FSV_5081:
address: '0x411B' address: '0x411B'
arithmetic: '' arithmetic: ''
description: '' description: PV Control - Application
enum: enum:
0x00: 'zero' 0x00: 'No'
0x01: 'one' 0x01: 'Yes'
remarks: '' remarks: ''
signed: '' signed: ''
type: ENUM type: ENUM
@@ -510,10 +524,10 @@ ENUM_IN_FSV_5081:
ENUM_IN_FSV_5091: ENUM_IN_FSV_5091:
address: '0x411C' address: '0x411C'
arithmetic: '' arithmetic: ''
description: '' description: Smart Grid Control - Application
enum: enum:
0x00: 'zero' 0x00: 'No'
0x01: 'one' 0x01: 'Yes'
remarks: '' remarks: ''
signed: '' signed: ''
type: ENUM type: ENUM
@@ -521,10 +535,10 @@ ENUM_IN_FSV_5091:
ENUM_IN_FSV_5094: ENUM_IN_FSV_5094:
address: '0x411D' address: '0x411D'
arithmetic: '' arithmetic: ''
description: '' description: Smart Grid Control - DHW Mode
enum: enum:
0x00: 'zero' 0x00: '55° by HP'
0x01: 'one' 0x01: '70° by HP and BSH'
remarks: '' remarks: ''
signed: '' signed: ''
type: ENUM type: ENUM
@@ -1227,6 +1241,45 @@ LVAR_IN_ETO_HEAT_CONTROL_DATA:
signed: 'false' signed: 'false'
type: LVAR type: LVAR
unit: '' 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: LVAR_OUT_AUTO_INSPECT_RESULT0:
address: '0x840B' address: '0x840B'
arithmetic: '' arithmetic: ''
@@ -2752,7 +2805,7 @@ NASA_OUTDOOR_COMP2_TARGET_HZ:
remarks: '' remarks: ''
signed: 'false' signed: 'false'
type: VAR type: VAR
uunit: 'Hz' unit: 'Hz'
NASA_OUTDOOR_COMP3_ORDER_HZ: NASA_OUTDOOR_COMP3_ORDER_HZ:
address: '0x82C0' address: '0x82C0'
arithmetic: '' arithmetic: ''
@@ -3295,12 +3348,12 @@ NASA_OUTDOOR_HOTGAS2:
unit: '' unit: ''
NASA_OUTDOOR_HP: NASA_OUTDOOR_HP:
address: '0x8287' address: '0x8287'
arithmetic: '' arithmetic: 'value * 0.7457'
description: Outdoor unit horsepower description: Outdoor unit horsepower (in kw)
remarks: unknown UNIT "HP" remarks: unknown UNIT "HP"
signed: 'false' signed: 'false'
type: VAR type: VAR
unit: HP unit: kw
NASA_OUTDOOR_HREEV: NASA_OUTDOOR_HREEV:
address: '0x822F' address: '0x822F'
arithmetic: '' arithmetic: ''
@@ -4820,7 +4873,7 @@ VAR_IN_FLOW_SENSOR_VOLTAGE:
VAR_IN_FSV_2011: VAR_IN_FSV_2011:
address: '0x4254' address: '0x4254'
arithmetic: value / 10 arithmetic: value / 10
description: Water Law Auto heating ambient temperature - Max. description: Heating - Outdoor Temp. for WL Max.
remarks: '' remarks: ''
signed: 'true' signed: 'true'
type: VAR type: VAR
@@ -4828,7 +4881,7 @@ VAR_IN_FSV_2011:
VAR_IN_FSV_2012: VAR_IN_FSV_2012:
address: '0x4255' address: '0x4255'
arithmetic: value / 10 arithmetic: value / 10
description: '' description: Heating - Outdoor Temp. for WL Min.
remarks: '' remarks: ''
signed: 'true' signed: 'true'
type: VAR type: VAR
@@ -4836,7 +4889,7 @@ VAR_IN_FSV_2012:
VAR_IN_FSV_2021: VAR_IN_FSV_2021:
address: '0x4256' address: '0x4256'
arithmetic: value / 10 arithmetic: value / 10
description: Water Law (WL1-Floor) Temperature auto heating - Max. description: Heating - Water out Temp. UFH Max.
remarks: '' remarks: ''
signed: 'true' signed: 'true'
type: VAR type: VAR
@@ -4844,7 +4897,7 @@ VAR_IN_FSV_2021:
VAR_IN_FSV_2022: VAR_IN_FSV_2022:
address: '0x4257' address: '0x4257'
arithmetic: value / 10 arithmetic: value / 10
description: '' description: Heating - Water out Temp. UFH Min.
remarks: '' remarks: ''
signed: 'true' signed: 'true'
type: VAR type: VAR
@@ -4852,7 +4905,7 @@ VAR_IN_FSV_2022:
VAR_IN_FSV_2031: VAR_IN_FSV_2031:
address: '0x4258' address: '0x4258'
arithmetic: value / 10 arithmetic: value / 10
description: Water Law (WL2-FCU) Temperature auto heating - Max. description: Heating - Water out Temp. FCU Max.
remarks: '' remarks: ''
signed: 'true' signed: 'true'
type: VAR type: VAR
@@ -4860,7 +4913,7 @@ VAR_IN_FSV_2031:
VAR_IN_FSV_2032: VAR_IN_FSV_2032:
address: '0x4259' address: '0x4259'
arithmetic: value / 10 arithmetic: value / 10
description: '' description: Heating - Water out Temp. FCU Min.
remarks: '' remarks: ''
signed: 'true' signed: 'true'
type: VAR type: VAR
@@ -4868,7 +4921,7 @@ VAR_IN_FSV_2032:
VAR_IN_FSV_2051: VAR_IN_FSV_2051:
address: '0x425A' address: '0x425A'
arithmetic: value / 10 arithmetic: value / 10
description: '' description: Cooling - Outdoor Temp. for WL Max.
remarks: '' remarks: ''
signed: 'true' signed: 'true'
type: VAR type: VAR
@@ -4876,7 +4929,7 @@ VAR_IN_FSV_2051:
VAR_IN_FSV_2052: VAR_IN_FSV_2052:
address: '0x425B' address: '0x425B'
arithmetic: value / 10 arithmetic: value / 10
description: '' description: Cooling - Outdoor Temp. for WL Min.
remarks: '' remarks: ''
signed: 'true' signed: 'true'
type: VAR type: VAR
@@ -4884,7 +4937,7 @@ VAR_IN_FSV_2052:
VAR_IN_FSV_2061: VAR_IN_FSV_2061:
address: '0x425C' address: '0x425C'
arithmetic: value / 10 arithmetic: value / 10
description: '' description: Cooling - Water out Temp. UFH Max.
remarks: '' remarks: ''
signed: 'true' signed: 'true'
type: VAR type: VAR
@@ -4892,7 +4945,7 @@ VAR_IN_FSV_2061:
VAR_IN_FSV_2062: VAR_IN_FSV_2062:
address: '0x425D' address: '0x425D'
arithmetic: value / 10 arithmetic: value / 10
description: '' description: Cooling - Water out Temp. UFH Min.
remarks: '' remarks: ''
signed: 'true' signed: 'true'
type: VAR type: VAR
@@ -4900,7 +4953,7 @@ VAR_IN_FSV_2062:
VAR_IN_FSV_2071: VAR_IN_FSV_2071:
address: '0x425E' address: '0x425E'
arithmetic: value / 10 arithmetic: value / 10
description: '' description: Cooling - Water out Temp. FCU Max.
remarks: '' remarks: ''
signed: 'true' signed: 'true'
type: VAR type: VAR
@@ -4908,7 +4961,7 @@ VAR_IN_FSV_2071:
VAR_IN_FSV_2072: VAR_IN_FSV_2072:
address: '0x425F' address: '0x425F'
arithmetic: value / 10 arithmetic: value / 10
description: '' description: Cooling - Water out Temp. FCU Min.
remarks: '' remarks: ''
signed: 'true' signed: 'true'
type: VAR type: VAR
@@ -4916,7 +4969,7 @@ VAR_IN_FSV_2072:
VAR_IN_FSV_3021: VAR_IN_FSV_3021:
address: '0x4260' address: '0x4260'
arithmetic: value / 10 arithmetic: value / 10
description: DHW Heating mode - Max. description: DHW - Heat Pump Max. temp.
remarks: '' remarks: ''
signed: 'true' signed: 'true'
type: VAR type: VAR
@@ -4924,7 +4977,7 @@ VAR_IN_FSV_3021:
VAR_IN_FSV_3022: VAR_IN_FSV_3022:
address: '0x4261' address: '0x4261'
arithmetic: value / 10 arithmetic: value / 10
description: '' description: DHW - Heat Pump Stop
remarks: '' remarks: ''
signed: 'true' signed: 'true'
type: VAR type: VAR
@@ -4932,7 +4985,7 @@ VAR_IN_FSV_3022:
VAR_IN_FSV_3023: VAR_IN_FSV_3023:
address: '0x4262' address: '0x4262'
arithmetic: value / 10 arithmetic: value / 10
description: DHW Heating mode - Start description: DHW - Heat Pump Start
remarks: '' remarks: ''
signed: 'true' signed: 'true'
type: VAR type: VAR
@@ -4940,7 +4993,7 @@ VAR_IN_FSV_3023:
VAR_IN_FSV_3024: VAR_IN_FSV_3024:
address: '0x4263' address: '0x4263'
arithmetic: '' arithmetic: ''
description: '' description: DHW - Heat Pump Min. Space heating operation time
remarks: '' remarks: ''
signed: 'false' signed: 'false'
type: VAR type: VAR
@@ -4948,7 +5001,7 @@ VAR_IN_FSV_3024:
VAR_IN_FSV_3025: VAR_IN_FSV_3025:
address: '0x4264' address: '0x4264'
arithmetic: '' arithmetic: ''
description: DHW Heating mode - DHW operation time description: DHW - Heat Pump Max. DHW operation time
remarks: '' remarks: ''
signed: 'false' signed: 'false'
type: VAR type: VAR
@@ -4956,7 +5009,7 @@ VAR_IN_FSV_3025:
VAR_IN_FSV_3026: VAR_IN_FSV_3026:
address: '0x4265' address: '0x4265'
arithmetic: '' arithmetic: ''
description: '' description: DHW - Heat Pump Max. Space heating operation time
remarks: '' remarks: ''
signed: 'false' signed: 'false'
type: VAR type: VAR
@@ -4964,7 +5017,7 @@ VAR_IN_FSV_3026:
VAR_IN_FSV_3032: VAR_IN_FSV_3032:
address: '0x4266' address: '0x4266'
arithmetic: '' arithmetic: ''
description: DHW Booster heater - Delayed time description: DHW - Booster Heat Delay Time
remarks: '' remarks: ''
signed: 'false' signed: 'false'
type: VAR type: VAR
@@ -4972,7 +5025,7 @@ VAR_IN_FSV_3032:
VAR_IN_FSV_3033: VAR_IN_FSV_3033:
address: '0x4267' address: '0x4267'
arithmetic: value / 10 arithmetic: value / 10
description: '' description: DHW - Booster Heat Overshoot
remarks: '' remarks: ''
signed: 'true' signed: 'true'
type: VAR type: VAR
@@ -4988,7 +5041,7 @@ VAR_IN_FSV_3034:
VAR_IN_FSV_3043: VAR_IN_FSV_3043:
address: '0x4269' address: '0x4269'
arithmetic: '' arithmetic: ''
description: '' description: DHW - Disinfection Start Time
remarks: '' remarks: ''
signed: 'false' signed: 'false'
type: VAR type: VAR
@@ -4996,7 +5049,7 @@ VAR_IN_FSV_3043:
VAR_IN_FSV_3044: VAR_IN_FSV_3044:
address: '0x426A' address: '0x426A'
arithmetic: value / 10 arithmetic: value / 10
description: Desinfection - Target temp. description: DHW - Disinfection Target Temp.
remarks: '' remarks: ''
signed: 'true' signed: 'true'
type: VAR type: VAR
@@ -5004,7 +5057,7 @@ VAR_IN_FSV_3044:
VAR_IN_FSV_3045: VAR_IN_FSV_3045:
address: '0x426B' address: '0x426B'
arithmetic: '' arithmetic: ''
description: '' description: DHW - Disinfection Duration
remarks: '' remarks: ''
signed: 'true' signed: 'true'
type: VAR type: VAR
@@ -5012,7 +5065,7 @@ VAR_IN_FSV_3045:
VAR_IN_FSV_3046: VAR_IN_FSV_3046:
address: '0x42CE' address: '0x42CE'
arithmetic: value / 60 arithmetic: value / 60
description: DHW Desinfection - Max. operation time description: DHW - Disinfection Max time
remarks: NASA Value is [minutes], not [hours] remarks: NASA Value is [minutes], not [hours]
signed: 'false' signed: 'false'
type: VAR type: VAR
@@ -5020,7 +5073,7 @@ VAR_IN_FSV_3046:
VAR_IN_FSV_3052: VAR_IN_FSV_3052:
address: '0x426C' address: '0x426C'
arithmetic: value / 0.1 arithmetic: value / 0.1
description: '' description: DHW - Forced DHW Operation Time Duration
remarks: '' remarks: ''
signed: 'true' signed: 'true'
type: VAR type: VAR
@@ -5028,7 +5081,7 @@ VAR_IN_FSV_3052:
VAR_IN_FSV_3081: VAR_IN_FSV_3081:
address: '0x42ED' address: '0x42ED'
arithmetic: '' arithmetic: ''
description: '' description: DHW - Energy Metering BUH 1 step capacity
remarks: '' remarks: ''
signed: 'true' signed: 'true'
type: VAR type: VAR
@@ -5036,7 +5089,7 @@ VAR_IN_FSV_3081:
VAR_IN_FSV_3082: VAR_IN_FSV_3082:
address: '0x42EE' address: '0x42EE'
arithmetic: '' arithmetic: ''
description: '' description: DHW - Energy Metering BUH 2 step capacity
remarks: '' remarks: ''
signed: 'true' signed: 'true'
type: VAR type: VAR
@@ -5044,7 +5097,7 @@ VAR_IN_FSV_3082:
VAR_IN_FSV_3083: VAR_IN_FSV_3083:
address: '0x42EF' address: '0x42EF'
arithmetic: '' arithmetic: ''
description: '' description: DHW - Energy Metering BSH capacity
remarks: '' remarks: ''
signed: 'true' signed: 'true'
type: VAR type: VAR
@@ -5052,7 +5105,7 @@ VAR_IN_FSV_3083:
VAR_IN_FSV_4012: VAR_IN_FSV_4012:
address: '0x426D' address: '0x426D'
arithmetic: value / 10 arithmetic: value / 10
description: '' description: Heating - Heat Pump Outdoor Temp. for Priority
remarks: '' remarks: ''
signed: 'true' signed: 'true'
type: VAR type: VAR
@@ -5060,7 +5113,7 @@ VAR_IN_FSV_4012:
VAR_IN_FSV_4013: VAR_IN_FSV_4013:
address: '0x426E' address: '0x426E'
arithmetic: value / 10 arithmetic: value / 10
description: Heating mode - Heating Off description: Heating - Heat Pump Outdoor Heat OFF
remarks: '' remarks: ''
signed: 'true' signed: 'true'
type: VAR type: VAR
@@ -5076,7 +5129,7 @@ VAR_IN_FSV_4014:
VAR_IN_FSV_4024: VAR_IN_FSV_4024:
address: '0x4270' address: '0x4270'
arithmetic: value / 10 arithmetic: value / 10
description: '' description: Heating - Backup Heater Threshold Temp.
remarks: '' remarks: ''
signed: 'true' signed: 'true'
type: VAR type: VAR
@@ -5084,7 +5137,7 @@ VAR_IN_FSV_4024:
VAR_IN_FSV_4025: VAR_IN_FSV_4025:
address: '0x4271' address: '0x4271'
arithmetic: value / 10 arithmetic: value / 10
description: '' description: Heating - Backup Heater Defrost Backup Temp.
remarks: '' remarks: ''
signed: 'true' signed: 'true'
type: VAR type: VAR
@@ -5092,7 +5145,7 @@ VAR_IN_FSV_4025:
VAR_IN_FSV_4033: VAR_IN_FSV_4033:
address: '0x4272' address: '0x4272'
arithmetic: value / 10 arithmetic: value / 10
description: '' description: Heating - Backup Boiler Threshold Temp.
remarks: '' remarks: ''
signed: 'true' signed: 'true'
type: VAR type: VAR
@@ -5100,7 +5153,7 @@ VAR_IN_FSV_4033:
VAR_IN_FSV_4042: VAR_IN_FSV_4042:
address: '0x4286' address: '0x4286'
arithmetic: value / 10 arithmetic: value / 10
description: '' description: Heating - Mixing Valve Target Delta Heating
remarks: '' remarks: ''
signed: 'false' signed: 'false'
type: VAR type: VAR
@@ -5108,7 +5161,7 @@ VAR_IN_FSV_4042:
VAR_IN_FSV_4043: VAR_IN_FSV_4043:
address: '0x4287' address: '0x4287'
arithmetic: value / 10 arithmetic: value / 10
description: '' description: Heating - Mixing Valve Target Delta Cooling
remarks: '' remarks: ''
signed: 'false' signed: 'false'
type: VAR type: VAR
@@ -5116,7 +5169,7 @@ VAR_IN_FSV_4043:
VAR_IN_FSV_4045: VAR_IN_FSV_4045:
address: '0x4288' address: '0x4288'
arithmetic: '' arithmetic: ''
description: '' description: Heating - Mixing Valve Control Interval
remarks: '' remarks: ''
signed: 'false' signed: 'false'
type: VAR type: VAR
@@ -5124,7 +5177,7 @@ VAR_IN_FSV_4045:
VAR_IN_FSV_4046: VAR_IN_FSV_4046:
address: '0x4289' address: '0x4289'
arithmetic: value / 0.1 arithmetic: value / 0.1
description: '' description: Heating - Mixing Valve Running Time
remarks: '' remarks: ''
signed: 'false' signed: 'false'
type: VAR type: VAR
@@ -5132,7 +5185,7 @@ VAR_IN_FSV_4046:
VAR_IN_FSV_4052: VAR_IN_FSV_4052:
address: '0x428A' address: '0x428A'
arithmetic: value / 10 arithmetic: value / 10
description: '' description: Heating - Inverter Pump Target Delta
remarks: '' remarks: ''
signed: 'false' signed: 'false'
type: VAR type: VAR
@@ -5140,7 +5193,7 @@ VAR_IN_FSV_4052:
VAR_IN_FSV_5011: VAR_IN_FSV_5011:
address: '0x4273' address: '0x4273'
arithmetic: value / 10 arithmetic: value / 10
description: '' description: Outing Mode - Water Out Temp. for Cooling
remarks: '' remarks: ''
signed: 'true' signed: 'true'
type: VAR type: VAR
@@ -5148,7 +5201,7 @@ VAR_IN_FSV_5011:
VAR_IN_FSV_5012: VAR_IN_FSV_5012:
address: '0x4274' address: '0x4274'
arithmetic: value / 10 arithmetic: value / 10
description: Outing mode - Room Temperature of cooling Mode description: Outing mode - Room Temp. for Cooling
remarks: '' remarks: ''
signed: 'true' signed: 'true'
type: VAR type: VAR
@@ -5156,7 +5209,7 @@ VAR_IN_FSV_5012:
VAR_IN_FSV_5013: VAR_IN_FSV_5013:
address: '0x4275' address: '0x4275'
arithmetic: value / 10 arithmetic: value / 10
description: '' description: Outing mode - Water Out Temp. for Heating
remarks: '' remarks: ''
signed: 'true' signed: 'true'
type: VAR type: VAR
@@ -5164,7 +5217,7 @@ VAR_IN_FSV_5013:
VAR_IN_FSV_5014: VAR_IN_FSV_5014:
address: '0x4276' address: '0x4276'
arithmetic: value / 10 arithmetic: value / 10
description: Outing mode- Indoor heating temperature description: Outing mode - Room Temp. for Heating
remarks: '' remarks: ''
signed: 'true' signed: 'true'
type: VAR type: VAR
@@ -5172,7 +5225,7 @@ VAR_IN_FSV_5014:
VAR_IN_FSV_5015: VAR_IN_FSV_5015:
address: '0x4277' address: '0x4277'
arithmetic: value / 10 arithmetic: value / 10
description: '' description: Outing mode - Auto Cooling WL1 Temp.
remarks: '' remarks: ''
signed: 'true' signed: 'true'
type: VAR type: VAR
@@ -5180,7 +5233,7 @@ VAR_IN_FSV_5015:
VAR_IN_FSV_5016: VAR_IN_FSV_5016:
address: '0x4278' address: '0x4278'
arithmetic: value / 10 arithmetic: value / 10
description: '' description: Outing mode - Auto Cooling WL2 Temp.
remarks: '' remarks: ''
signed: 'true' signed: 'true'
type: VAR type: VAR
@@ -5188,7 +5241,7 @@ VAR_IN_FSV_5016:
VAR_IN_FSV_5017: VAR_IN_FSV_5017:
address: '0x4279' address: '0x4279'
arithmetic: value / 10 arithmetic: value / 10
description: '' description: Outing mode - Auto Heating WL1 Temp
remarks: '' remarks: ''
signed: 'true' signed: 'true'
type: VAR type: VAR
@@ -5196,7 +5249,7 @@ VAR_IN_FSV_5017:
VAR_IN_FSV_5018: VAR_IN_FSV_5018:
address: '0x427A' address: '0x427A'
arithmetic: value / 10 arithmetic: value / 10
description: Outing mode - Temperature of auto heating WL2 water description: Outing mode - Auto Heating WL2 Temp
remarks: '' remarks: ''
signed: 'true' signed: 'true'
type: VAR type: VAR
@@ -5204,7 +5257,7 @@ VAR_IN_FSV_5018:
VAR_IN_FSV_5019: VAR_IN_FSV_5019:
address: '0x427B' address: '0x427B'
arithmetic: value / 10 arithmetic: value / 10
description: '' description: Outing mode - Target Tank Temp
remarks: '' remarks: ''
signed: 'true' signed: 'true'
type: VAR type: VAR
@@ -5212,7 +5265,7 @@ VAR_IN_FSV_5019:
VAR_IN_FSV_5021: VAR_IN_FSV_5021:
address: '0x427C' address: '0x427C'
arithmetic: value / 10 arithmetic: value / 10
description: Economic DHW mode - Temperature of hot water Tank description: DHW Saving - DHW Saving Temp.
remarks: '' remarks: ''
signed: 'true' signed: 'true'
type: VAR type: VAR
@@ -5220,7 +5273,7 @@ VAR_IN_FSV_5021:
VAR_IN_FSV_5023: VAR_IN_FSV_5023:
address: '0x42F0' address: '0x42F0'
arithmetic: value / 10 arithmetic: value / 10
description: '' description: DHW Saving - DHW Saving Thermo on Temp.
remarks: '' remarks: ''
signed: 'true' signed: 'true'
type: VAR type: VAR
@@ -5244,7 +5297,7 @@ VAR_IN_FSV_5032:
VAR_IN_FSV_5082: VAR_IN_FSV_5082:
address: '0x42DB' address: '0x42DB'
arithmetic: value / 10 arithmetic: value / 10
description: '' description: PV Control - Setting Temp. Shift Value (Cool)
remarks: '' remarks: ''
signed: 'false' signed: 'false'
type: VAR type: VAR
@@ -5252,7 +5305,7 @@ VAR_IN_FSV_5082:
VAR_IN_FSV_5083: VAR_IN_FSV_5083:
address: '0x42DC' address: '0x42DC'
arithmetic: value / 10 arithmetic: value / 10
description: '' description: PV Control - Setting Temp. Shift Value (Heat)
remarks: '' remarks: ''
signed: 'false' signed: 'false'
type: VAR type: VAR
@@ -5260,7 +5313,7 @@ VAR_IN_FSV_5083:
VAR_IN_FSV_5092: VAR_IN_FSV_5092:
address: '0x42DD' address: '0x42DD'
arithmetic: value / 10 arithmetic: value / 10
description: '' description: Smart Grid Control - Setting Temp. Shift Value (Heat)
remarks: '' remarks: ''
signed: 'false' signed: 'false'
type: VAR type: VAR
@@ -5268,7 +5321,7 @@ VAR_IN_FSV_5092:
VAR_IN_FSV_5093: VAR_IN_FSV_5093:
address: '0x42DE' address: '0x42DE'
arithmetic: value / 10 arithmetic: value / 10
description: '' description: Smart Grid Control - Setting Temp. Shift Value (DHW)
remarks: '' remarks: ''
signed: 'false' signed: 'false'
type: VAR type: VAR
@@ -5793,6 +5846,15 @@ VAR_OUT_SENSOR_TOTAL_SUCTION:
signed: 'true' signed: 'true'
type: VAR type: VAR
unit: "\u00b0C" 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: NASA_EHSSENTINEL_COP:
address: '0x9998' address: '0x9998'
arithmetic: '' arithmetic: ''
@@ -5810,4 +5872,4 @@ NASA_EHSSENTINEL_HEAT_OUTPUT:
state_class: measurement state_class: measurement
signed: 'true' signed: 'true'
type: VAR type: VAR
unit: "W" unit: "W"

View File

@@ -1,6 +1,10 @@
general: general:
nasaRepositoryFile: data/NasaRepository.yml nasaRepositoryFile: data/NasaRepository.yml
silentMode: True logging:
deviceAdded: True
messageNotFound: False
packetNotFromIndoorOutdoor: False
proccessedMessage: False
serial: serial:
device: /dev/ttyUSB0 device: /dev/ttyUSB0
baudrate: 9600 baudrate: 9600

View File

@@ -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] ['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']
[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', '0x12', '0x10', '0x0', '0x0', '0xb0', '0x0', '0xff', '0xc0', '0x14', '0x5a', '0x1', '0x82', '0x1c', '0x1', '0x38', '0x1f', '0x34']

View File

@@ -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)

View File

@@ -5,7 +5,7 @@ import traceback
from MessageProcessor import MessageProcessor from MessageProcessor import MessageProcessor
from EHSArguments import EHSArguments from EHSArguments import EHSArguments
from EHSConfig import EHSConfig from EHSConfig import EHSConfig
from EHSExceptions import MessageWarningException, MessageCapacityStructureWarning from EHSExceptions import MessageWarningException, SkipInvalidPacketException
from MQTTClient import MQTTClient from MQTTClient import MQTTClient
import aiofiles import aiofiles
import json import json
@@ -13,10 +13,11 @@ import struct
import binascii import binascii
# Get the logger # Get the logger
from CustomLogger import logger, setSilent from CustomLogger import logger
from NASAPacket import NASAPacket from NASAPacket import NASAPacket, AddressClassEnum, PacketType, DataType
from NASAMessage import NASAMessage
version = "0.1.0 Stable" version = "0.2.0 Stable"
async def main(): async def main():
""" """
@@ -26,9 +27,8 @@ async def main():
2. Reads command-line arguments. 2. Reads command-line arguments.
3. Reads configuration settings. 3. Reads configuration settings.
4. Connects to the MQTT broker. 4. Connects to the MQTT broker.
5. Sets silent mode if specified in the configuration. 5. If dry run mode is enabled, reads data from a dump file and processes it.
6. 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.
7. If not in dry run mode, reads data from a serial port and processes it.
Args: Args:
None None
Returns: Returns:
@@ -61,10 +61,6 @@ async def main():
await asyncio.sleep(1) await asyncio.sleep(1)
# if Silent is true, set Silent Mode
if config.GENERAL['silentMode']:
setSilent()
# if dryrun then we read from dumpfile # if dryrun then we read from dumpfile
if args.DRYRUN: if args.DRYRUN:
logger.info(f"DRYRUN detected, reading from dumpfile {args.DUMPFILE}") logger.info(f"DRYRUN detected, reading from dumpfile {args.DUMPFILE}")
@@ -75,12 +71,12 @@ async def main():
except: except:
line = line.strip().replace("'", "").replace("[", "").replace("]", "").split(", ") # for ['0x1', '0x2' ..] line = line.strip().replace("'", "").replace("[", "").replace("]", "").split(", ") # for ['0x1', '0x2' ..]
line = [int(value, 16) for value in line] line = [int(value, 16) for value in line]
await process_packet(line, args) await process_packet(line, args, config)
else: else:
# we are not in dryrun mode, so we need to read from Serial Pimort # 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. Processes a buffer of data asynchronously, identifying and handling packets based on specific criteria.
Args: Args:
@@ -108,14 +104,14 @@ async def process_buffer(buffer, args):
for i in range(0, len(buffer)): for i in range(0, len(buffer)):
if buffer[i] == 0x32: if buffer[i] == 0x32:
if (len(buffer[i:]) > 14): if (len(buffer[i:]) > 14):
asyncio.create_task(process_packet(buffer[i:], args)) asyncio.create_task(process_packet(buffer[i:], args, config))
else: else:
logger.debug(f"Buffermessages to short for NASA {len(buffer)}") logger.debug(f"Buffermessages to short for NASA {len(buffer)}")
break break
else: else:
logger.debug(f"Buffer to short for NASA {len(buffer)}") 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. Asynchronously reads data from a serial connection and processes it.
Args: Args:
@@ -147,25 +143,28 @@ async def serial_read(config, args):
stopbits=serial.STOPBITS_ONE, stopbits=serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS, bytesize=serial.EIGHTBITS,
rtscts=True, rtscts=True,
timeout=0 timeout=1
) )
# start the async buffer process await asyncio.gather(
#asyncio.create_task(process_buffer(buffer, args))# start the async buffer process serial_read(reader, args, config),
#serial_write(writer, reader, args),
# 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))
# Read loop
async def serial_read(reader, args, config):
while True: while True:
data = await reader.readuntil(b'\x34') # Read up to end of next message 0x34 data = await reader.readuntil(b'\x34') # Read up to end of next message 0x34
if data: if data:
asyncio.create_task(process_buffer(data, args)) asyncio.create_task(process_buffer(data, args, config))
#buffer.extend(data) #buffer.extend(data)
logger.debug(f"Received: {data}")
logger.debug(f"Received: {data!r}")
logger.debug(f"Received: {[hex(x) for x in data]}") 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... TODO Not used yet, only for future use...
@@ -183,36 +182,85 @@ async def serial_write(writer, reader):
await asyncio.sleep(5) await asyncio.sleep(5)
# Example data to write # Example data to write
packet = bytearray([ decoded_nasa = NASAPacket()
#0x32, # Packet Start Byte decoded_nasa.set_packet_source_address_class(AddressClassEnum.WiFiKit)
#0x00, 0x12, # Packet Size decoded_nasa.set_packet_source_channel(0)
0x80, # Source Address Class JIGTester decoded_nasa.set_packet_source_address(144)
0xFF, # Source Channel decoded_nasa.set_packet_dest_address_class(AddressClassEnum.BroadcastSetLayer)
0x00, # Source Address decoded_nasa.set_packet_dest_channel(0)
0x20, # Destination Address Class Indoor decoded_nasa.set_packet_dest_address(32)
0x00, # Destination Channel decoded_nasa.set_packet_information(True)
0x00, # Destination Address decoded_nasa.set_packet_version(2)
0xC0, # Packet Information + Protocol Version + Retry Count decoded_nasa.set_packet_retry_count(0)
0x11, # Packet Type [Normal = 1] + Data Type [Read = 1] decoded_nasa.set_packet_type(PacketType.Normal)
0xF0, # Packet Number decoded_nasa.set_packet_data_type(DataType.Read)
0x01, # Capacity (Number of Messages) decoded_nasa.set_packet_number(3)
0x42, 0x56, # NASA Message Number lst = []
0x00, 0x00 # Message Payload (placeholder for return value) tmp_msg = NASAMessage()
]) tmp_msg.set_packet_message(0x4093)
tmp_msg.set_packet_message_type(0)
crc=binascii.crc_hqx(packet, 0) tmp_msg.set_packet_payload([0])
# NOTE: include length of CRC(2) and length of length field(2) in the lst.append(tmp_msg)
# total length, exclude SF/TF of total length tmp_msg = NASAMessage()
final_packet = struct.pack(">BH", 0x32, len(packet)+2+2) + packet + struct.pack(">HB", crc, 0x34) tmp_msg.set_packet_message(0x4094)
# ['0x32', '0x0', '0x12', '0x80', '0xff', '0x0', '0x20', '0x0', '0x0', '0xc0', '0x11', '0xf0', '0x1', '0x42', '0x56', '0x0', '0x0', '0xf9', '0x65', '0x34'] tmp_msg.set_packet_message_type(0)
# ['0x32', '0x0', '0x12', '0x80', '0xff', '0x0', '0x20', '0x0', '0x0', '0xc0', '0x11', '0xf0', '0x1', '0x42', '0x56', '0x0', '0x0', '0x38', '0xc6', '0x34'] 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) writer.write(final_packet)
await writer.drain() await writer.drain()
logger.debug(f"Sent data raw: {final_packet}") logger.info(f"Sent data raw: {final_packet}")
logger.debug(f"Sent data raw: {[hex(x) for x in final_packet]}") logger.info(f"Sent data raw: {decoded_nasa}")
await asyncio.sleep(1) # Adjust the interval as needed 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. Asynchronously processes a packet buffer.
If `dumpWriter` is `None`, it attempts to process the packet using `MessageProcessor`. 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("Packet processed: ")
logger.debug(f"Packet raw: {[hex(x) for x in buffer]}") logger.debug(f"Packet raw: {[hex(x) for x in buffer]}")
logger.debug(nasa_packet) logger.debug(nasa_packet)
messageProcessor = MessageProcessor() if nasa_packet.packet_source_address_class in (AddressClassEnum.Outdoor, AddressClassEnum.Indoor):
messageProcessor.process_message(nasa_packet) 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: except ValueError as e:
logger.warning("Value Error on parsing Packet, Packet will be skipped") logger.warning("Value Error on parsing Packet, Packet will be skipped")
logger.warning(f"Error processing message: {e}") logger.warning(f"Error processing message: {e}")
logger.warning(f"Complete Packet: {[hex(x) for x in buffer]}") 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("Warnung accured, Packet will be skipped")
logger.debug(f"Error processing message: {e}") logger.debug(f"Error processing message: {e}")
logger.debug(f"Complete Packet: {[hex(x) for x in buffer]}") logger.debug(f"Complete Packet: {[hex(x) for x in buffer]}")