Feature/0.1.0 releasebranch (#6)
* device_class: measurement for NASA_EHSSENTINEL_COP and NASA_EHSSENTINEL_HEAT_OUTPUT * state_class: measurement for NASA_EHSSENTINEL_COP and NASA_EHSSENTINEL_HEAT_OUTPUT * ENUM_IN_WATERPUMP_PWM_VALUE as var not enum Unit % as = measurement * NASA_OUTDOOR_CONTROL_WATTMETER_ALL_UNIT_ACCUM state_class: total_increasing * NASA_OUTDOOR_CONTROL_WATTMETER_ALL_UNIT_ACCUM device_class and unit * NASA_OUTDOOR_CONTROL_WATTMETER_ALL_UNIT_ACCUM device_class energy * fixing ValueError and better logging for determine_value try catch * update reqierments and rreadme for venv * ENUM_IN_FSV_2041 enum value fixed * large buffer log * prevent buffer overloading * remove await * a * Feature/test without buffer (#5) * test * test * test * devision by 0 error fixed remove task print * logger * topic clear only on online message * expand logging * reduce logging
This commit is contained in:
@@ -99,7 +99,7 @@ def setSilent():
|
||||
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 ERROR or higher are displayed")
|
||||
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())
|
||||
@@ -47,7 +47,7 @@ class ArgumentException(EHSException):
|
||||
def __str__(self):
|
||||
return f'{self.argument} -> {self.message}'
|
||||
|
||||
class InvalidMessageTypeException(EHSException):
|
||||
class MessageCapacityStructureWarning(EHSException):
|
||||
"""Exception raised for invalid message types.
|
||||
|
||||
Attributes:
|
||||
@@ -55,10 +55,9 @@ class InvalidMessageTypeException(EHSException):
|
||||
message -- explanation of the error
|
||||
"""
|
||||
|
||||
def __init__(self, message_type, message="Invalid message type provided"):
|
||||
self.message_type = message_type
|
||||
def __init__(self, message="Invalid message type provided"):
|
||||
self.message = message
|
||||
super().__init__(self.message)
|
||||
|
||||
def __str__(self):
|
||||
return f'{self.message_type} -> {self.message}'
|
||||
return f'{self.message}'
|
||||
@@ -90,7 +90,7 @@ class MQTTClient:
|
||||
self.broker = self.config.MQTT['broker-url']
|
||||
self.port = self.config.MQTT['broker-port']
|
||||
self.client_id = self.config.MQTT['client-id']
|
||||
self.client = gmqtt.Client(self.client_id, logger=logger)
|
||||
self.client = gmqtt.Client(self.client_id)
|
||||
self.client.on_connect = self.on_connect
|
||||
self.client.on_disconnect = self.on_disconnect
|
||||
self.client.on_message = self.on_message
|
||||
@@ -182,8 +182,9 @@ class MQTTClient:
|
||||
|
||||
if f"{self.homeAssistantAutoDiscoverTopic}/status" == topic:
|
||||
logger.info(f"HASS Status Messages {topic} received: {payload.decode()}")
|
||||
self._publish(f"{self.topicPrefix.replace('/', '')}/{self.known_devices_topic}", " ", retain=True)
|
||||
logger.info("Known Devices Topic have been cleared")
|
||||
if payload.decode() == "online":
|
||||
self._publish(f"{self.topicPrefix.replace('/', '')}/{self.known_devices_topic}", " ", retain=True)
|
||||
logger.info("Known Devices Topic have been cleared")
|
||||
|
||||
def refresh_known_devices(self, devname):
|
||||
"""
|
||||
|
||||
@@ -76,7 +76,7 @@ class MessageProcessor:
|
||||
try:
|
||||
msgvalue = self.determine_value(msg.packet_payload, msgname)
|
||||
except Exception as e:
|
||||
raise MessageWarningException(argument=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)
|
||||
else:
|
||||
logger.debug(f"Message not Found in NASA repository: {hexmsg:<6} Type: {msg.packet_message_type} Payload: {msg.packet_payload}")
|
||||
@@ -111,8 +111,8 @@ class MessageProcessor:
|
||||
if msgname in ['NASA_OUTDOOR_TW2_TEMP', 'NASA_OUTDOOR_TW1_TEMP', 'VAR_IN_FLOW_SENSOR_CALC']:
|
||||
if all(k in self.NASA_VAL_STORE for k in ['NASA_OUTDOOR_TW2_TEMP', 'NASA_OUTDOOR_TW1_TEMP', 'VAR_IN_FLOW_SENSOR_CALC']):
|
||||
self.protocolMessage(NASAMessage(packet_message=0x9999, packet_message_type=1),
|
||||
"NASA_EHSSENTINEL_HEAT_OUTPUT",
|
||||
round(
|
||||
"NASA_EHSSENTINEL_HEAT_OUTPUT",
|
||||
round(
|
||||
(
|
||||
(self.NASA_VAL_STORE['NASA_OUTDOOR_TW2_TEMP'] - self.NASA_VAL_STORE['NASA_OUTDOOR_TW1_TEMP']) *
|
||||
(self.NASA_VAL_STORE['VAR_IN_FLOW_SENSOR_CALC']/60)
|
||||
@@ -121,9 +121,10 @@ class MessageProcessor:
|
||||
|
||||
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']):
|
||||
self.protocolMessage(NASAMessage(packet_message=0x9998, packet_message_type=1),
|
||||
"NASA_EHSSENTINEL_COP",
|
||||
round((self.NASA_VAL_STORE['NASA_EHSSENTINEL_HEAT_OUTPUT'] / self.NASA_VAL_STORE['NASA_OUTDOOR_CONTROL_WATTMETER_ALL_UNIT']/1000.), 3))
|
||||
if (self.NASA_VAL_STORE['NASA_OUTDOOR_CONTROL_WATTMETER_ALL_UNIT'] > 0):
|
||||
self.protocolMessage(NASAMessage(packet_message=0x9998, packet_message_type=1),
|
||||
"NASA_EHSSENTINEL_COP",
|
||||
round((self.NASA_VAL_STORE['NASA_EHSSENTINEL_HEAT_OUTPUT'] / self.NASA_VAL_STORE['NASA_OUTDOOR_CONTROL_WATTMETER_ALL_UNIT']/1000.), 3))
|
||||
|
||||
def search_nasa_table(self, address):
|
||||
"""
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from enum import Enum
|
||||
from NASAMessage import NASAMessage
|
||||
from EHSExceptions import MessageCapacityStructureWarning
|
||||
|
||||
class AddressClassEnum(Enum):
|
||||
"""
|
||||
@@ -259,7 +260,7 @@ class NASAPacket:
|
||||
elif message_type == 3:
|
||||
payload_size = len(msg_rest)
|
||||
if capacity != 1:
|
||||
raise ValueError("Message with structure type must have capacity of 1.")
|
||||
raise MessageCapacityStructureWarning("Message with structure type must have capacity of 1.")
|
||||
else:
|
||||
raise ValueError(f"Mssage type unknown: {message_type}")
|
||||
|
||||
|
||||
@@ -2991,6 +2991,8 @@ NASA_OUTDOOR_DEFROST_STEP:
|
||||
0x05: 'Defrost stage 5'
|
||||
0x06: 'Defrost stage 6'
|
||||
0x07: 'Defrost operation end stage'
|
||||
0x08: 'Defrost stage 8'
|
||||
0x09: 'Defrost stage 9'
|
||||
0xFF: 'No defrost operation'
|
||||
remarks: 1 Defrost stage 1, 2 Defrost stage 2, 3 Defrost stage 3, 4 Defrost stage
|
||||
4, 7 Defrost operation end stage, 255 No defrost operation
|
||||
@@ -5808,4 +5810,4 @@ NASA_EHSSENTINEL_HEAT_OUTPUT:
|
||||
state_class: measurement
|
||||
signed: 'true'
|
||||
type: VAR
|
||||
unit: "kW"
|
||||
unit: "W"
|
||||
|
||||
@@ -5,7 +5,7 @@ import traceback
|
||||
from MessageProcessor import MessageProcessor
|
||||
from EHSArguments import EHSArguments
|
||||
from EHSConfig import EHSConfig
|
||||
from EHSExceptions import MessageWarningException
|
||||
from EHSExceptions import MessageWarningException, MessageCapacityStructureWarning
|
||||
from MQTTClient import MQTTClient
|
||||
import aiofiles
|
||||
import json
|
||||
@@ -16,7 +16,7 @@ import binascii
|
||||
from CustomLogger import logger, setSilent
|
||||
from NASAPacket import NASAPacket
|
||||
|
||||
version = "0.0.1Beta"
|
||||
version = "0.1.0 Stable"
|
||||
|
||||
async def main():
|
||||
"""
|
||||
@@ -70,7 +70,11 @@ async def main():
|
||||
logger.info(f"DRYRUN detected, reading from dumpfile {args.DUMPFILE}")
|
||||
async with aiofiles.open(args.DUMPFILE, mode='r') as file:
|
||||
async for line in file:
|
||||
line = json.loads(line.strip())
|
||||
try:
|
||||
line = json.loads(line.strip()) # for [12, 234, 456 ,67]
|
||||
except:
|
||||
line = line.strip().replace("'", "").replace("[", "").replace("]", "").split(", ") # for ['0x1', '0x2' ..]
|
||||
line = [int(value, 16) for value in line]
|
||||
await process_packet(line, args)
|
||||
else:
|
||||
# we are not in dryrun mode, so we need to read from Serial Pimort
|
||||
@@ -99,30 +103,17 @@ async def process_buffer(buffer, args):
|
||||
- Logs if a received byte is not a start byte.
|
||||
"""
|
||||
|
||||
while True:
|
||||
if buffer:
|
||||
logger.debug(f"Buffersize: {len(buffer)}")
|
||||
if buffer[0] == 0x32:
|
||||
logger.debug("Start Byte recognized")
|
||||
packet_size = ((buffer[1] << 8) | buffer[2]) +2
|
||||
logger.debug(f"Readed packet size: {packet_size-1}")
|
||||
if len(buffer) > packet_size-1:
|
||||
packet = []
|
||||
for i in range(0, len(buffer)):
|
||||
packet.append(buffer[i])
|
||||
if i == packet_size-1: #buffer[i] == 0x34 or
|
||||
logger.debug(f"Complete Packet: {i}/{packet_size-1}")
|
||||
logger.debug(f"Last Byte readed: {hex(buffer[i])}")
|
||||
await process_packet(packet, args)
|
||||
del buffer[0:i]
|
||||
break
|
||||
else:
|
||||
logger.debug(f"Buffer to small to read hole packet, wait... buffer size {len(buffer)} packet size {packet_size}")
|
||||
else:
|
||||
logger.debug(f"Received byte not a startbyte 0x32 {buffer[0]} / {hex(buffer[0])}")
|
||||
buffer.pop(0)
|
||||
|
||||
await asyncio.sleep(0.03)
|
||||
if buffer:
|
||||
if (len(buffer) > 14):
|
||||
for i in range(0, len(buffer)):
|
||||
if buffer[i] == 0x32:
|
||||
if (len(buffer[i:]) > 14):
|
||||
asyncio.create_task(process_packet(buffer[i:], args))
|
||||
else:
|
||||
logger.debug(f"Buffermessages to short for NASA {len(buffer)}")
|
||||
break
|
||||
else:
|
||||
logger.debug(f"Buffer to short for NASA {len(buffer)}")
|
||||
|
||||
async def serial_read(config, args):
|
||||
"""
|
||||
@@ -160,7 +151,7 @@ async def serial_read(config, args):
|
||||
)
|
||||
|
||||
# start the async buffer process
|
||||
asyncio.create_task(process_buffer(buffer, args))# start the async buffer process
|
||||
#asyncio.create_task(process_buffer(buffer, args))# start the async buffer process
|
||||
|
||||
# TODO have to be tested and verified, please do not try it yet
|
||||
# start the async writer process
|
||||
@@ -170,7 +161,8 @@ async def serial_read(config, args):
|
||||
while True:
|
||||
data = await reader.readuntil(b'\x34') # Read up to end of next message 0x34
|
||||
if data:
|
||||
buffer.extend(data)
|
||||
asyncio.create_task(process_buffer(data, args))
|
||||
#buffer.extend(data)
|
||||
logger.debug(f"Received: {[hex(x) for x in data]}")
|
||||
|
||||
async def serial_write(writer, reader):
|
||||
@@ -246,13 +238,23 @@ async def process_packet(buffer, args):
|
||||
except ValueError as e:
|
||||
logger.warning("Value Error on parsing Packet, Packet will be skipped")
|
||||
logger.warning(f"Error processing message: {e}")
|
||||
logger.warning(f"Complete Packet: {[hex(x) for x in buffer]}")
|
||||
except MessageCapacityStructureWarning as e:
|
||||
logger.debug("Warnung accured, Packet will be skipped")
|
||||
logger.debug(f"Error processing message: {e}")
|
||||
logger.debug(f"Complete Packet: {[hex(x) for x in buffer]}")
|
||||
except MessageWarningException as e:
|
||||
logger.warning("Warnung accured, Packet will be skipped")
|
||||
logger.warning(f"Error processing message: {e}")
|
||||
logger.warning(f"Complete Packet: {[hex(x) for x in buffer]}")
|
||||
except Exception as e:
|
||||
logger.error("Error Accured, Packet will be skipped")
|
||||
logger.error(f"Error processing message: {e}")
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
try:
|
||||
loop = asyncio.get_event_loop()
|
||||
loop.run_until_complete(main())
|
||||
except RuntimeError as e:
|
||||
logger.error(f"Runtime error: {e}")
|
||||
Reference in New Issue
Block a user