From 93b89062f5bafbbf5256184a2f72844e0858e45a Mon Sep 17 00:00:00 2001 From: Stefan Allius Date: Sun, 7 Apr 2024 19:41:05 +0200 Subject: [PATCH] Read pv module details for HA from config file --- CHANGELOG.md | 2 ++ app/config/default_config.toml | 10 +++++++++- app/src/config.py | 26 +++++++++++++++++++++++++- app/src/gen3/talent.py | 5 +++-- app/src/gen3plus/solarman_v5.py | 9 +++++---- app/src/infos.py | 30 ++++++++++++++++++++++++++++++ 6 files changed, 74 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d50288f..86483ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Prepare support of inverters with 6 MTPPs - Clear `Daily Generation` values at midnigth +- Read pv module details from config file and use it for the Home Assistant registration + see: [#43](https://github.com/s-allius/tsun-gen3-proxy/issues/43) ## [0.6.0] - 2024-04-02 diff --git a/app/config/default_config.toml b/app/config/default_config.toml index fccc54b..050ca32 100644 --- a/app/config/default_config.toml +++ b/app/config/default_config.toml @@ -31,13 +31,21 @@ inverters.allow_all = true # allow inverters, even if we have no inverter mapp [inverters."R170000000000001"] #node_id = '' # Optional, MQTT replacement for inverters serial number #suggested_area = '' # Optional, suggested installation area for home-assistant +#pv1 = {type = 'RSM40-8-395M', manufacturer = 'Risen'} +#pv2 = {type = 'RSM40-8-395M', manufacturer = 'Risen'} #[inverters."R17xxxxxxxxxxxx2"] #node_id = '' # Optional, MQTT replacement for inverters serial number #suggested_area = '' # Optional, suggested installation area for home-assistant +#pv1 = {type = 'RSM40-8-405M', manufacturer = 'Risen'} +#pv2 = {type = 'RSM40-8-405M', manufacturer = 'Risen'} [inverters."Y170000000000001"] -#monitor_sn = 2000000000 # The "Monitoring SN:" can be found on a sticker enclosed with the inverter +monitor_sn = 2000000000 # The "Monitoring SN:" can be found on a sticker enclosed with the inverter #node_id = '' # Optional, MQTT replacement for inverters serial number #suggested_area = '' # Optional, suggested installation place for home-assistant +#pv1 = {type = 'RSM40-8-410M', manufacturer = 'Risen'} +#pv2 = {type = 'RSM40-8-410M', manufacturer = 'Risen'} +#pv3 = {type = 'RSM40-8-410M', manufacturer = 'Risen'} +#pv4 = {type = 'RSM40-8-410M', manufacturer = 'Risen'} diff --git a/app/src/config.py b/app/src/config.py index 3778e09..589bb6c 100644 --- a/app/src/config.py +++ b/app/src/config.py @@ -45,7 +45,31 @@ class Config(): if len(s) > 0 and s[-1] != '/' else s)), - Optional('suggested_area', default=""): Use(str) + Optional('suggested_area', default=""): Use(str), + Optional('pv1'): { + Optional('type'): Use(str), + Optional('manufacturer'): Use(str), + }, + Optional('pv2'): { + Optional('type'): Use(str), + Optional('manufacturer'): Use(str), + }, + Optional('pv3'): { + Optional('type'): Use(str), + Optional('manufacturer'): Use(str), + }, + Optional('pv4'): { + Optional('type'): Use(str), + Optional('manufacturer'): Use(str), + }, + Optional('pv5'): { + Optional('type'): Use(str), + Optional('manufacturer'): Use(str), + }, + Optional('pv6'): { + Optional('type'): Use(str), + Optional('manufacturer'): Use(str), + } }} }, ignore_extra_keys=True ) diff --git a/app/src/gen3/talent.py b/app/src/gen3/talent.py index 0bc08a8..46302ac 100644 --- a/app/src/gen3/talent.py +++ b/app/src/gen3/talent.py @@ -59,7 +59,7 @@ class Talent(Message): # deallocated by the garbage collector ==> we get a memory leak self.switch.clear() - def set_serial_no(self, serial_no: str): + def __set_serial_no(self, serial_no: str): if self.unique_id == serial_no: logger.debug(f'SerialNo: {serial_no}') @@ -72,6 +72,7 @@ class Talent(Message): self.node_id = inv['node_id'] self.sug_area = inv['suggested_area'] logger.debug(f'SerialNo {serial_no} allowed! area:{self.sug_area}') # noqa: E501 + self.db.set_pv_module_details(inv) else: self.node_id = '' self.sug_area = '' @@ -95,7 +96,7 @@ class Talent(Message): hex_dump_memory(logging.INFO, f'Received from {self.addr}:', self._recv_buffer, self.header_len+self.data_len) - self.set_serial_no(self.id_str.decode("utf-8")) + self.__set_serial_no(self.id_str.decode("utf-8")) self.__dispatch_msg() self.__flush_recv_msg() return diff --git a/app/src/gen3plus/solarman_v5.py b/app/src/gen3plus/solarman_v5.py index c09512b..fc189b3 100644 --- a/app/src/gen3plus/solarman_v5.py +++ b/app/src/gen3plus/solarman_v5.py @@ -70,7 +70,7 @@ class SolarmanV5(Message): # deallocated by the garbage collector ==> we get a memory leak self.switch.clear() - def set_serial_no(self, snr: int): + def __set_serial_no(self, snr: int): serial_no = str(snr) if self.unique_id == serial_no: logger.debug(f'SerialNo: {serial_no}') @@ -87,6 +87,7 @@ class SolarmanV5(Message): self.node_id = inv['node_id'] self.sug_area = inv['suggested_area'] logger.debug(f'SerialNo {serial_no} allowed! area:{self.sug_area}') # noqa: E501 + self.db.set_pv_module_details(inv) if not found: self.node_id = '' @@ -112,7 +113,7 @@ class SolarmanV5(Message): self._recv_buffer, self.header_len+self.data_len+2) if self.__trailer_is_ok(self._recv_buffer, self.header_len + self.data_len + 2): - self.set_serial_no(self.snr) + self.__set_serial_no(self.snr) self.__dispatch_msg() self.__flush_recv_msg() return @@ -352,9 +353,9 @@ class SolarmanV5(Message): ftype = result[0] # always 2 valid = result[1] == 1 # status ts = result[2] - repeat = result[3] # always 60 + set_hb = result[3] # always 60 or 120 logger.info(f'ftype:{ftype} accepted:{valid}' - f' ts:{ts:08x} repeat:{repeat}s') + f' ts:{ts:08x} nextHeartbeat: {set_hb}s') dt = datetime.fromtimestamp(ts) logger.info(f'ts: {dt.strftime("%Y-%m-%d %H:%M:%S")}') diff --git a/app/src/infos.py b/app/src/infos.py index 21c121e..88e70c4 100644 --- a/app/src/infos.py +++ b/app/src/infos.py @@ -193,6 +193,19 @@ class Infos: Register.MAX_DESIGNED_POWER: {'name': ['inverter', 'Max_Designed_Power'], 'level': logging.DEBUG, 'unit': 'W', 'ha': {'dev': 'inverter', 'dev_cla': None, 'stat_cla': None, 'id': 'designed_power_', 'fmt': '| string + " W"', 'name': 'Max Designed Power', 'icon': 'mdi:lightning-bolt', 'ent_cat': 'diagnostic'}}, # noqa: E501 Register.RATED_POWER: {'name': ['inverter', 'Rated_Power'], 'level': logging.DEBUG, 'unit': 'W', 'ha': {'dev': 'inverter', 'dev_cla': None, 'stat_cla': None, 'id': 'rated_power_', 'fmt': '| string + " W"', 'name': 'Rated Power', 'icon': 'mdi:lightning-bolt', 'ent_cat': 'diagnostic'}}, # noqa: E501 + Register.PV1_MANUFACTURER: {'name': ['inverter', 'PV1_Manufacturer'], 'level': logging.DEBUG, 'unit': ''}, # noqa: E501 + Register.PV1_MODEL: {'name': ['inverter', 'PV1_Model'], 'level': logging.DEBUG, 'unit': ''}, # noqa: E501 + Register.PV2_MANUFACTURER: {'name': ['inverter', 'PV2_Manufacturer'], 'level': logging.DEBUG, 'unit': ''}, # noqa: E501 + Register.PV2_MODEL: {'name': ['inverter', 'PV2_Model'], 'level': logging.DEBUG, 'unit': ''}, # noqa: E501 + Register.PV3_MANUFACTURER: {'name': ['inverter', 'PV3_Manufacturer'], 'level': logging.DEBUG, 'unit': ''}, # noqa: E501 + Register.PV3_MODEL: {'name': ['inverter', 'PV3_Model'], 'level': logging.DEBUG, 'unit': ''}, # noqa: E501 + Register.PV4_MANUFACTURER: {'name': ['inverter', 'PV4_Manufacturer'], 'level': logging.DEBUG, 'unit': ''}, # noqa: E501 + Register.PV4_MODEL: {'name': ['inverter', 'PV4_Model'], 'level': logging.DEBUG, 'unit': ''}, # noqa: E501 + Register.PV5_MANUFACTURER: {'name': ['inverter', 'PV5_Manufacturer'], 'level': logging.DEBUG, 'unit': ''}, # noqa: E501 + Register.PV5_MODEL: {'name': ['inverter', 'PV5_Model'], 'level': logging.DEBUG, 'unit': ''}, # noqa: E501 + Register.PV6_MANUFACTURER: {'name': ['inverter', 'PV6_Manufacturer'], 'level': logging.DEBUG, 'unit': ''}, # noqa: E501 + Register.PV6_MODEL: {'name': ['inverter', 'PV6_Model'], 'level': logging.DEBUG, 'unit': ''}, # noqa: E501 + # proxy: Register.INVERTER_CNT: {'name': ['proxy', 'Inverter_Cnt'], 'singleton': True, 'ha': {'dev': 'proxy', 'comp': 'sensor', 'dev_cla': None, 'stat_cla': None, 'id': 'inv_count_', 'fmt': '| int', 'name': 'Active Inverter Connections', 'icon': 'mdi:counter'}}, # noqa: E501 Register.UNKNOWN_SNR: {'name': ['proxy', 'Unknown_SNR'], 'singleton': True, 'ha': {'dev': 'proxy', 'comp': 'sensor', 'dev_cla': None, 'stat_cla': None, 'id': 'unknown_snr_', 'fmt': '| int', 'name': 'Unknown Serial No', 'icon': 'mdi:counter', 'ent_cat': 'diagnostic'}}, # noqa: E501 @@ -530,3 +543,20 @@ class Infos: elif 'less_eq' in dep: return not value <= dep['less_eq'] return True + + def set_pv_module_details(self, inv: dict) -> None: + map = {'pv1': {'manufacturer': Register.PV1_MANUFACTURER, 'model': Register.PV1_MODEL}, # noqa: E501 + 'pv2': {'manufacturer': Register.PV2_MANUFACTURER, 'model': Register.PV2_MODEL}, # noqa: E501 + 'pv3': {'manufacturer': Register.PV3_MANUFACTURER, 'model': Register.PV3_MODEL}, # noqa: E501 + 'pv4': {'manufacturer': Register.PV4_MANUFACTURER, 'model': Register.PV4_MODEL}, # noqa: E501 + 'pv5': {'manufacturer': Register.PV5_MANUFACTURER, 'model': Register.PV5_MODEL}, # noqa: E501 + 'pv6': {'manufacturer': Register.PV6_MANUFACTURER, 'model': Register.PV6_MODEL} # noqa: E501 + } + + for key, reg in map.items(): + if key in inv: + if 'manufacturer' in inv[key]: + self.set_db_def_value(reg['manufacturer'], + inv[key]['manufacturer']) + if 'type' in inv[key]: + self.set_db_def_value(reg['model'], inv[key]['type'])