S allius/issue281 (#282)
* accept DCU serial number starting with '410' * determine sensor-list by serial number * adapt unit test for DCU support * send first batterie measurements to home assistant * add test case for sensor-list==3036 * add more registers for batteries * improve error logging (Monitoring SN) * update the add-on repro only for one stage * add configuration for energie storages * add License and Readme file to the add-on * addon: add date and time to dev and debug docker container tag * disable duplicate code check for config.py * cleanup unit test, remove trailing whitespaces * update changelog * fix example config for batteries * cleanup config.jinja template * fix comments * improve help texts
This commit is contained in:
@@ -9,9 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
- Respect logging.ini file, if LOG_ENV isn't set well [#288](https://github.com/s-allius/tsun-gen3-proxy/issues/288)
|
- Respect logging.ini file, if LOG_ENV isn't set well [#288](https://github.com/s-allius/tsun-gen3-proxy/issues/288)
|
||||||
- Remove trailing apostrophe in the log output [#288](https://github.com/s-allius/tsun-gen3-proxy/issues/288)
|
- Remove trailing apostrophe in the log output [#288](https://github.com/s-allius/tsun-gen3-proxy/issues/288)
|
||||||
- Update AddOn base docker image to version 17.1.3 and python3 to 3.12.9-r0
|
- update AddOn base docker image to version 17.2.1
|
||||||
|
- addon: add date and time to dev container version
|
||||||
|
- Update AddOn python3 to 3.12.9-r0
|
||||||
- add initial DCU support
|
- add initial DCU support
|
||||||
- update AddOn base docker image to version 17.1.2
|
|
||||||
- update aiohttp to version 3.11.12
|
- update aiohttp to version 3.11.12
|
||||||
- fix the path handling for logging.ini and default_config.toml [#180](https://github.com/s-allius/tsun-gen3-proxy/issues/180)
|
- fix the path handling for logging.ini and default_config.toml [#180](https://github.com/s-allius/tsun-gen3-proxy/issues/180)
|
||||||
|
|
||||||
|
|||||||
@@ -78,7 +78,8 @@ class Config():
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
'inverters': {
|
'inverters': {
|
||||||
'allow_all': Use(bool), And(Use(str), lambda s: len(s) == 16): {
|
'allow_all': Use(bool),
|
||||||
|
And(Use(str), lambda s: len(s) == 16): {
|
||||||
Optional('monitor_sn', default=0): Use(int),
|
Optional('monitor_sn', default=0): Use(int),
|
||||||
Optional('node_id', default=""): And(Use(str),
|
Optional('node_id', default=""): And(Use(str),
|
||||||
Use(lambda s: s + '/'
|
Use(lambda s: s + '/'
|
||||||
@@ -93,7 +94,7 @@ class Config():
|
|||||||
},
|
},
|
||||||
Optional('modbus_polling', default=True): Use(bool),
|
Optional('modbus_polling', default=True): Use(bool),
|
||||||
Optional('suggested_area', default=""): Use(str),
|
Optional('suggested_area', default=""): Use(str),
|
||||||
Optional('sensor_list', default=0x2b0): Use(int),
|
Optional('sensor_list', default=0): Use(int),
|
||||||
Optional('pv1'): {
|
Optional('pv1'): {
|
||||||
Optional('type'): Use(str),
|
Optional('type'): Use(str),
|
||||||
Optional('manufacturer'): Use(str),
|
Optional('manufacturer'): Use(str),
|
||||||
@@ -119,6 +120,33 @@ class Config():
|
|||||||
Optional('manufacturer'): Use(str),
|
Optional('manufacturer'): Use(str),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
'batteries': {
|
||||||
|
And(Use(str), lambda s: len(s) == 16): {
|
||||||
|
Optional('monitor_sn', default=0): Use(int),
|
||||||
|
Optional('node_id', default=""): And(Use(str),
|
||||||
|
Use(lambda s: s + '/'
|
||||||
|
if len(s) > 0
|
||||||
|
and s[-1] != '/'
|
||||||
|
else s)),
|
||||||
|
Optional('client_mode'): {
|
||||||
|
'host': Use(str),
|
||||||
|
Optional('port', default=8899):
|
||||||
|
And(Use(int), lambda n: 1024 <= n <= 65535),
|
||||||
|
Optional('forward', default=False): Use(bool),
|
||||||
|
},
|
||||||
|
Optional('modbus_polling', default=True): Use(bool),
|
||||||
|
Optional('suggested_area', default=""): Use(str),
|
||||||
|
Optional('sensor_list', default=0): Use(int),
|
||||||
|
Optional('pv1'): {
|
||||||
|
Optional('type'): Use(str),
|
||||||
|
Optional('manufacturer'): Use(str),
|
||||||
|
},
|
||||||
|
Optional('pv2'): {
|
||||||
|
Optional('type'): Use(str),
|
||||||
|
Optional('manufacturer'): Use(str),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, ignore_extra_keys=True
|
}, ignore_extra_keys=True
|
||||||
)
|
)
|
||||||
@@ -178,7 +206,7 @@ here. The default config reader is handled in the Config.init method'''
|
|||||||
rd_config = reader.get_config()
|
rd_config = reader.get_config()
|
||||||
config = cls.act_config.copy()
|
config = cls.act_config.copy()
|
||||||
for key in ['tsun', 'solarman', 'mqtt', 'ha', 'inverters',
|
for key in ['tsun', 'solarman', 'mqtt', 'ha', 'inverters',
|
||||||
'gen3plus']:
|
'gen3plus', 'batteries']:
|
||||||
if key in rd_config:
|
if key in rd_config:
|
||||||
config[key] = config[key] | rd_config[key]
|
config[key] = config[key] | rd_config[key]
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,8 @@ class ConfigReadJson(ConfigIfc):
|
|||||||
def convert_to_obj(self, data):
|
def convert_to_obj(self, data):
|
||||||
conf = {}
|
conf = {}
|
||||||
for key, val in data.items():
|
for key, val in data.items():
|
||||||
if key == 'inverters' and isinstance(val, list):
|
if (key == 'inverters' or key == 'batteries') and \
|
||||||
|
isinstance(val, list):
|
||||||
self.convert_inv_arr(conf, key, val)
|
self.convert_inv_arr(conf, key, val)
|
||||||
else:
|
else:
|
||||||
self._extend_key(conf, key, val)
|
self._extend_key(conf, key, val)
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ inverters.allow_all = false # only allow known inverters
|
|||||||
##
|
##
|
||||||
## For each GEN3 inverter, the serial number of the inverter must be mapped to an MQTT
|
## For each GEN3 inverter, the serial number of the inverter must be mapped to an MQTT
|
||||||
## definition. To do this, the corresponding configuration block is started with
|
## definition. To do this, the corresponding configuration block is started with
|
||||||
## `[Inverter.“<16-digit serial number>”]` so that all subsequent parameters are assigned
|
## `[inverters.“<16-digit serial number>”]` so that all subsequent parameters are assigned
|
||||||
## to this inverter. Further inverter-specific parameters (e.g. polling mode) can be set
|
## to this inverter. Further inverter-specific parameters (e.g. polling mode) can be set
|
||||||
## in the configuration block
|
## in the configuration block
|
||||||
##
|
##
|
||||||
@@ -132,7 +132,7 @@ pv2 = {type = 'RSM40-8-395M', manufacturer = 'Risen'} # Optional, PV module de
|
|||||||
##
|
##
|
||||||
## For each GEN3PLUS inverter, the serial number of the inverter must be mapped to an MQTT
|
## For each GEN3PLUS inverter, the serial number of the inverter must be mapped to an MQTT
|
||||||
## definition. To do this, the corresponding configuration block is started with
|
## definition. To do this, the corresponding configuration block is started with
|
||||||
## `[Inverter.“<16-digit serial number>”]` so that all subsequent parameters are assigned
|
## `[inverters.“<16-digit serial number>”]` so that all subsequent parameters are assigned
|
||||||
## to this inverter. Further inverter-specific parameters (e.g. polling mode, client mode)
|
## to this inverter. Further inverter-specific parameters (e.g. polling mode, client mode)
|
||||||
## can be set in the configuration block
|
## can be set in the configuration block
|
||||||
##
|
##
|
||||||
@@ -157,6 +157,33 @@ pv3 = {type = 'RSM40-8-410M', manufacturer = 'Risen'} # Optional, PV module de
|
|||||||
pv4 = {type = 'RSM40-8-410M', manufacturer = 'Risen'} # Optional, PV module descr
|
pv4 = {type = 'RSM40-8-410M', manufacturer = 'Risen'} # Optional, PV module descr
|
||||||
|
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
##
|
||||||
|
## For each GEN3PLUS enrgy storage system, the serial number must be mapped to an MQTT
|
||||||
|
## definition. To do this, the corresponding configuration block is started with
|
||||||
|
## `[batteries.“<16-digit serial number>”]` so that all subsequent parameters are assigned
|
||||||
|
## to this energy storage system. Further device-specific parameters (e.g. polling mode,
|
||||||
|
## client mode) can be set in the configuration block
|
||||||
|
##
|
||||||
|
## The serial numbers of all GEN3PLUS energy storage systems/batteries start with `410`!
|
||||||
|
## Each GEN3PLUS device is supplied with a “Monitoring SN:”. This can be found on a
|
||||||
|
## sticker enclosed with the inverter.
|
||||||
|
##
|
||||||
|
|
||||||
|
[batteries."4100000000000001"]
|
||||||
|
monitor_sn = 3000000000 # The GEN3PLUS "Monitoring SN:"
|
||||||
|
node_id = '' # MQTT replacement for devices serial number
|
||||||
|
suggested_area = '' # suggested installation place for home-assistant
|
||||||
|
modbus_polling = true # Enable optional MODBUS polling
|
||||||
|
|
||||||
|
# if your inverter supports SSL connections you must use the client_mode. Pls, uncomment
|
||||||
|
# the next line and configure the fixed IP of your inverter
|
||||||
|
#client_mode = {host = '192.168.0.1', port = 8899, forward = true}
|
||||||
|
|
||||||
|
pv1 = {type = 'RSM40-8-410M', manufacturer = 'Risen'} # Optional, PV module descr
|
||||||
|
pv2 = {type = 'RSM40-8-410M', manufacturer = 'Risen'} # Optional, PV module descr
|
||||||
|
|
||||||
|
|
||||||
##########################################################################################
|
##########################################################################################
|
||||||
###
|
###
|
||||||
### If the proxy mode is configured, commands from TSUN can be sent to the inverter via
|
### If the proxy mode is configured, commands from TSUN can be sent to the inverter via
|
||||||
|
|||||||
@@ -116,6 +116,33 @@ class RegisterMap:
|
|||||||
0x4201000c: {'reg': Register.SENSOR_LIST, 'fmt': '<H', 'func': Fmt.hex4}, # noqa: E501
|
0x4201000c: {'reg': Register.SENSOR_LIST, 'fmt': '<H', 'func': Fmt.hex4}, # noqa: E501
|
||||||
0x4201001c: {'reg': Register.POWER_ON_TIME, 'fmt': '<H', 'ratio': 1, 'dep': ProxyMode.SERVER}, # noqa: E501, or packet number
|
0x4201001c: {'reg': Register.POWER_ON_TIME, 'fmt': '<H', 'ratio': 1, 'dep': ProxyMode.SERVER}, # noqa: E501, or packet number
|
||||||
0x42010020: {'reg': Register.SERIAL_NUMBER, 'fmt': '!16s'}, # noqa: E501
|
0x42010020: {'reg': Register.SERIAL_NUMBER, 'fmt': '!16s'}, # noqa: E501
|
||||||
|
0x42010030: {'reg': Register.BATT_PV1_VOLT, 'fmt': '!H', 'ratio': 0.01}, # noqa: E501, PV1 voltage
|
||||||
|
0x42010032: {'reg': Register.BATT_PV1_CUR, 'fmt': '!H', 'ratio': 0.01}, # noqa: E501, PV1 current
|
||||||
|
0x42010034: {'reg': Register.BATT_PV2_VOLT, 'fmt': '!H', 'ratio': 0.01}, # noqa: E501, PV2 voltage
|
||||||
|
0x42010036: {'reg': Register.BATT_PV2_CUR, 'fmt': '!H', 'ratio': 0.01}, # noqa: E501, PV2 current
|
||||||
|
0x42010038: {'reg': Register.BATT_38, 'fmt': '!h'}, # noqa: E501
|
||||||
|
0x4201003a: {'reg': Register.BATT_3a, 'fmt': '!h'}, # noqa: E501
|
||||||
|
0x4201003c: {'reg': Register.BATT_3c, 'fmt': '!h'}, # noqa: E501
|
||||||
|
0x4201003e: {'reg': Register.BATT_3e, 'fmt': '!h'}, # noqa: E501
|
||||||
|
0x42010040: {'reg': Register.BATT_40, 'fmt': '!h', 'ratio': 0.01}, # noqa: E501
|
||||||
|
0x42010042: {'reg': Register.BATT_42, 'fmt': '!h', 'ratio': 0.01}, # noqa: E501
|
||||||
|
0x42010044: {'reg': Register.BATT_SOC, 'fmt': '!H', 'ratio': 0.01}, # noqa: E501, state of charge (SOC) in percent
|
||||||
|
0x42010046: {'reg': Register.BATT_46, 'fmt': '!h'}, # noqa: E501
|
||||||
|
0x42010048: {'reg': Register.BATT_48, 'fmt': '!h'}, # noqa: E501
|
||||||
|
0x4201004a: {'reg': Register.BATT_4a, 'fmt': '!h'}, # noqa: E501
|
||||||
|
0x4201004c: {'reg': Register.BATT_4c, 'fmt': '!h'}, # noqa: E501
|
||||||
|
0x4201004e: {'reg': Register.BATT_4e, 'fmt': '!h'}, # noqa: E501
|
||||||
|
|
||||||
|
0x42010066: {'reg': Register.BATT_66, 'fmt': '!h'}, # noqa: E501
|
||||||
|
0x42010068: {'reg': Register.BATT_68, 'fmt': '!h'}, # noqa: E501
|
||||||
|
0x4201006a: {'reg': Register.BATT_6a, 'fmt': '!h'}, # noqa: E501
|
||||||
|
0x4201006c: {'reg': Register.BATT_6c, 'fmt': '!h'}, # noqa: E501
|
||||||
|
0x4201006e: {'reg': Register.BATT_6e, 'fmt': '!h'}, # noqa: E501
|
||||||
|
0x42010070: {'reg': Register.BATT_70, 'fmt': '!h'}, # noqa: E501
|
||||||
|
0x42010072: {'reg': Register.BATT_72, 'fmt': '!h'}, # noqa: E501
|
||||||
|
0x42010074: {'reg': Register.BATT_74, 'fmt': '!h'}, # noqa: E501
|
||||||
|
0x42010076: {'reg': Register.BATT_76, 'fmt': '!h'}, # noqa: E501
|
||||||
|
0x42010078: {'reg': Register.BATT_78, 'fmt': '!h'}, # noqa: E501
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -162,9 +189,15 @@ class InfosG3P(Infos):
|
|||||||
entity strings
|
entity strings
|
||||||
sug_area:str ==> suggested area string from the config file'''
|
sug_area:str ==> suggested area string from the config file'''
|
||||||
# iterate over RegisterMap.map and get the register values
|
# iterate over RegisterMap.map and get the register values
|
||||||
for _, row in chain(RegisterMap.map.items(),
|
sensor = self.get_db_value(Register.SENSOR_LIST)
|
||||||
RegisterMap.map_02b0.items(),
|
if "3026" == sensor:
|
||||||
RegisterMap.map_3026.items()):
|
items = RegisterMap.map_3026.items()
|
||||||
|
elif "02b0" == sensor:
|
||||||
|
items = RegisterMap.map_02b0.items()
|
||||||
|
else:
|
||||||
|
items = {}
|
||||||
|
|
||||||
|
for _, row in chain(RegisterMap.map.items(), items):
|
||||||
info_id = row['reg']
|
info_id = row['reg']
|
||||||
if self.__hide_topic(row):
|
if self.__hide_topic(row):
|
||||||
res = self.ha_remove(info_id, node_id, snr) # noqa: E501
|
res = self.ha_remove(info_id, node_id, snr) # noqa: E501
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import struct
|
|||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
import asyncio
|
import asyncio
|
||||||
|
from itertools import chain
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from async_ifc import AsyncIfc
|
from async_ifc import AsyncIfc
|
||||||
@@ -376,12 +377,23 @@ class SolarmanV5(SolarmanBase):
|
|||||||
self.ifc.fwd_add(build_msg)
|
self.ifc.fwd_add(build_msg)
|
||||||
self.ifc.fwd_add(struct.pack('<BB', 0, 0x15)) # crc & stop
|
self.ifc.fwd_add(struct.pack('<BB', 0, 0x15)) # crc & stop
|
||||||
|
|
||||||
def __set_config_parms(self, inv: dict):
|
def __set_config_parms(self, inv: dict, serial_no: str):
|
||||||
'''init connection with params from the configuration'''
|
'''init connection with params from the configuration'''
|
||||||
self.node_id = inv['node_id']
|
self.node_id = inv['node_id']
|
||||||
self.sug_area = inv['suggested_area']
|
self.sug_area = inv['suggested_area']
|
||||||
self.modbus_polling = inv['modbus_polling']
|
self.modbus_polling = inv['modbus_polling']
|
||||||
self.sensor_list = inv['sensor_list']
|
self.sensor_list = inv['sensor_list']
|
||||||
|
if 0 == self.sensor_list:
|
||||||
|
snr = serial_no[:3]
|
||||||
|
if '410' == snr:
|
||||||
|
self.sensor_list = 0x3026
|
||||||
|
else:
|
||||||
|
self.sensor_list = 0x02b0
|
||||||
|
self.db.set_db_def_value(Register.SENSOR_LIST,
|
||||||
|
f"{self.sensor_list:04x}")
|
||||||
|
logging.debug(f"Use sensor-list: {self.sensor_list:#04x}"
|
||||||
|
f" for '{serial_no}'")
|
||||||
|
|
||||||
if self.mb:
|
if self.mb:
|
||||||
self.mb.set_node_id(self.node_id)
|
self.mb.set_node_id(self.node_id)
|
||||||
|
|
||||||
@@ -392,13 +404,14 @@ class SolarmanV5(SolarmanBase):
|
|||||||
logger.debug(f'SerialNo: {serial_no}')
|
logger.debug(f'SerialNo: {serial_no}')
|
||||||
else:
|
else:
|
||||||
inverters = Config.get('inverters')
|
inverters = Config.get('inverters')
|
||||||
|
batteries = Config.get('batteries')
|
||||||
# logger.debug(f'Inverters: {inverters}')
|
# logger.debug(f'Inverters: {inverters}')
|
||||||
|
|
||||||
for key, inv in inverters.items():
|
for key, inv in chain(inverters.items(), batteries.items()):
|
||||||
# logger.debug(f'key: {key} -> {inv}')
|
# logger.debug(f'key: {key} -> {inv}')
|
||||||
if (type(inv) is dict and 'monitor_sn' in inv
|
if (type(inv) is dict and 'monitor_sn' in inv
|
||||||
and inv['monitor_sn'] == snr):
|
and inv['monitor_sn'] == snr):
|
||||||
self.__set_config_parms(inv)
|
self.__set_config_parms(inv, key)
|
||||||
self.db.set_pv_module_details(inv)
|
self.db.set_pv_module_details(inv)
|
||||||
logger.debug(f'SerialNo {serial_no} allowed! area:{self.sug_area}') # noqa: E501
|
logger.debug(f'SerialNo {serial_no} allowed! area:{self.sug_area}') # noqa: E501
|
||||||
|
|
||||||
@@ -411,9 +424,11 @@ class SolarmanV5(SolarmanBase):
|
|||||||
if 'allow_all' not in inverters or not inverters['allow_all']:
|
if 'allow_all' not in inverters or not inverters['allow_all']:
|
||||||
self.inc_counter('Unknown_SNR')
|
self.inc_counter('Unknown_SNR')
|
||||||
self.unique_id = None
|
self.unique_id = None
|
||||||
logger.warning(f'ignore message from unknow inverter! (SerialNo: {serial_no})') # noqa: E501
|
logging.error(f"Ignore message from unknow inverter with Monitoring-SN: {serial_no})!\n" # noqa: E501
|
||||||
|
" !!Check the 'monitor_sn' setting in your configuration!!") # noqa: E501
|
||||||
return
|
return
|
||||||
logger.warning(f'SerialNo {serial_no} not known but accepted!')
|
logging.warning(f"Monitoring-SN: {serial_no} not configured but accepted!" # noqa: E501
|
||||||
|
" !!Check the 'monitor_sn' setting in your configuration!!") # noqa: E501
|
||||||
|
|
||||||
self.unique_id = serial_no
|
self.unique_id = serial_no
|
||||||
|
|
||||||
@@ -460,11 +475,11 @@ class SolarmanV5(SolarmanBase):
|
|||||||
def mb_timout_cb(self, exp_cnt):
|
def mb_timout_cb(self, exp_cnt):
|
||||||
self.mb_timer.start(self.mb_timeout)
|
self.mb_timer.start(self.mb_timeout)
|
||||||
|
|
||||||
self._send_modbus_cmd(Modbus.READ_REGS, 0x3000, 48, logging.DEBUG)
|
self._send_modbus_cmd(Modbus.READ_REGS, 0x3000, 48, logging.INFO)
|
||||||
|
|
||||||
if 1 == (exp_cnt % 30):
|
if 1 == (exp_cnt % 30):
|
||||||
# logging.info("Regular Modbus Status request")
|
# logging.info("Regular Modbus Status request")
|
||||||
self._send_modbus_cmd(Modbus.READ_REGS, 0x2000, 96, logging.DEBUG)
|
self._send_modbus_cmd(Modbus.READ_REGS, 0x2000, 96, logging.INFO)
|
||||||
|
|
||||||
def at_cmd_forbidden(self, cmd: str, connection: str) -> bool:
|
def at_cmd_forbidden(self, cmd: str, connection: str) -> bool:
|
||||||
return not cmd.startswith(tuple(self.at_acl[connection]['allow'])) or \
|
return not cmd.startswith(tuple(self.at_acl[connection]['allow'])) or \
|
||||||
|
|||||||
@@ -121,6 +121,32 @@ class Register(Enum):
|
|||||||
TS_INPUT = 600
|
TS_INPUT = 600
|
||||||
TS_GRID = 601
|
TS_GRID = 601
|
||||||
TS_TOTAL = 602
|
TS_TOTAL = 602
|
||||||
|
BATT_PV1_VOLT = 1000
|
||||||
|
BATT_PV1_CUR = 1001
|
||||||
|
BATT_PV2_VOLT = 1002
|
||||||
|
BATT_PV2_CUR = 1003
|
||||||
|
BATT_38 = 1004
|
||||||
|
BATT_3a = 1005
|
||||||
|
BATT_3c = 1006
|
||||||
|
BATT_3e = 1007
|
||||||
|
BATT_40 = 1010
|
||||||
|
BATT_42 = 1011
|
||||||
|
BATT_SOC = 1012
|
||||||
|
BATT_46 = 1013
|
||||||
|
BATT_48 = 1014
|
||||||
|
BATT_4a = 1015
|
||||||
|
BATT_4c = 1016
|
||||||
|
BATT_4e = 1017
|
||||||
|
BATT_66 = 1018
|
||||||
|
BATT_68 = 1019
|
||||||
|
BATT_6a = 1020
|
||||||
|
BATT_6c = 1021
|
||||||
|
BATT_6e = 1022
|
||||||
|
BATT_70 = 1023
|
||||||
|
BATT_72 = 1024
|
||||||
|
BATT_74 = 1025
|
||||||
|
BATT_76 = 1026
|
||||||
|
BATT_78 = 1027
|
||||||
VALUE_1 = 9000
|
VALUE_1 = 9000
|
||||||
TEST_REG1 = 10000
|
TEST_REG1 = 10000
|
||||||
TEST_REG2 = 10001
|
TEST_REG2 = 10001
|
||||||
@@ -266,6 +292,7 @@ class Infos:
|
|||||||
__info_devs = {
|
__info_devs = {
|
||||||
'proxy': {'singleton': True, 'name': 'Proxy', 'mf': 'Stefan Allius'}, # noqa: E501
|
'proxy': {'singleton': True, 'name': 'Proxy', 'mf': 'Stefan Allius'}, # noqa: E501
|
||||||
'controller': {'via': 'proxy', 'name': 'Controller', 'mdl': Register.CHIP_MODEL, 'mf': Register.CHIP_TYPE, 'sw': Register.COLLECTOR_FW_VERSION, 'mac': Register.MAC_ADDR, 'sn': Register.COLLECTOR_SNR}, # noqa: E501
|
'controller': {'via': 'proxy', 'name': 'Controller', 'mdl': Register.CHIP_MODEL, 'mf': Register.CHIP_TYPE, 'sw': Register.COLLECTOR_FW_VERSION, 'mac': Register.MAC_ADDR, 'sn': Register.COLLECTOR_SNR}, # noqa: E501
|
||||||
|
|
||||||
'inverter': {'via': 'controller', 'name': 'Micro Inverter', 'mdl': Register.EQUIPMENT_MODEL, 'mf': Register.MANUFACTURER, 'sw': Register.VERSION, 'sn': Register.SERIAL_NUMBER}, # noqa: E501
|
'inverter': {'via': 'controller', 'name': 'Micro Inverter', 'mdl': Register.EQUIPMENT_MODEL, 'mf': Register.MANUFACTURER, 'sw': Register.VERSION, 'sn': Register.SERIAL_NUMBER}, # noqa: E501
|
||||||
'input_pv1': {'via': 'inverter', 'name': 'Module PV1', 'mdl': Register.PV1_MODEL, 'mf': Register.PV1_MANUFACTURER}, # noqa: E501
|
'input_pv1': {'via': 'inverter', 'name': 'Module PV1', 'mdl': Register.PV1_MODEL, 'mf': Register.PV1_MANUFACTURER}, # noqa: E501
|
||||||
'input_pv2': {'via': 'inverter', 'name': 'Module PV2', 'mdl': Register.PV2_MODEL, 'mf': Register.PV2_MANUFACTURER, 'dep': {'reg': Register.NO_INPUTS, 'gte': 2}}, # noqa: E501
|
'input_pv2': {'via': 'inverter', 'name': 'Module PV2', 'mdl': Register.PV2_MODEL, 'mf': Register.PV2_MANUFACTURER, 'dep': {'reg': Register.NO_INPUTS, 'gte': 2}}, # noqa: E501
|
||||||
@@ -273,6 +300,10 @@ class Infos:
|
|||||||
'input_pv4': {'via': 'inverter', 'name': 'Module PV4', 'mdl': Register.PV4_MODEL, 'mf': Register.PV4_MANUFACTURER, 'dep': {'reg': Register.NO_INPUTS, 'gte': 4}}, # noqa: E501
|
'input_pv4': {'via': 'inverter', 'name': 'Module PV4', 'mdl': Register.PV4_MODEL, 'mf': Register.PV4_MANUFACTURER, 'dep': {'reg': Register.NO_INPUTS, 'gte': 4}}, # noqa: E501
|
||||||
'input_pv5': {'via': 'inverter', 'name': 'Module PV5', 'mdl': Register.PV5_MODEL, 'mf': Register.PV5_MANUFACTURER, 'dep': {'reg': Register.NO_INPUTS, 'gte': 5}}, # noqa: E501
|
'input_pv5': {'via': 'inverter', 'name': 'Module PV5', 'mdl': Register.PV5_MODEL, 'mf': Register.PV5_MANUFACTURER, 'dep': {'reg': Register.NO_INPUTS, 'gte': 5}}, # noqa: E501
|
||||||
'input_pv6': {'via': 'inverter', 'name': 'Module PV6', 'mdl': Register.PV6_MODEL, 'mf': Register.PV6_MANUFACTURER, 'dep': {'reg': Register.NO_INPUTS, 'gte': 6}}, # noqa: E501
|
'input_pv6': {'via': 'inverter', 'name': 'Module PV6', 'mdl': Register.PV6_MODEL, 'mf': Register.PV6_MANUFACTURER, 'dep': {'reg': Register.NO_INPUTS, 'gte': 6}}, # noqa: E501
|
||||||
|
|
||||||
|
'batterie': {'via': 'controller', 'name': 'Batterie', 'mdl': Register.EQUIPMENT_MODEL, 'mf': Register.MANUFACTURER, 'sw': Register.VERSION, 'sn': Register.SERIAL_NUMBER}, # noqa: E501
|
||||||
|
'bat_inp_pv1': {'via': 'batterie', 'name': 'Module PV1', 'mdl': Register.PV1_MODEL, 'mf': Register.PV1_MANUFACTURER}, # noqa: E501
|
||||||
|
'bat_inp_pv2': {'via': 'batterie', 'name': 'Module PV2', 'mdl': Register.PV2_MODEL, 'mf': Register.PV2_MANUFACTURER}, # noqa: E501
|
||||||
}
|
}
|
||||||
|
|
||||||
__comm_type_val_tpl = "{%set com_types = ['n/a','Wi-Fi', 'G4', 'G5', 'GPRS'] %}{{com_types[value_json['Communication_Type']|int(0)]|default(value_json['Communication_Type'])}}" # noqa: E501
|
__comm_type_val_tpl = "{%set com_types = ['n/a','Wi-Fi', 'G4', 'G5', 'GPRS'] %}{{com_types[value_json['Communication_Type']|int(0)]|default(value_json['Communication_Type'])}}" # noqa: E501
|
||||||
@@ -518,15 +549,15 @@ class Infos:
|
|||||||
Register.TOTAL_GENERATION: {'name': ['total', 'Total_Generation'], 'level': logging.INFO, 'unit': 'kWh', 'ha': {'dev': 'inverter', 'dev_cla': 'energy', 'stat_cla': 'total', 'id': 'total_gen_', 'fmt': FMT_FLOAT, 'name': TOTAL_GEN, 'icon': SOLAR_POWER, 'must_incr': True}}, # noqa: E501
|
Register.TOTAL_GENERATION: {'name': ['total', 'Total_Generation'], 'level': logging.INFO, 'unit': 'kWh', 'ha': {'dev': 'inverter', 'dev_cla': 'energy', 'stat_cla': 'total', 'id': 'total_gen_', 'fmt': FMT_FLOAT, 'name': TOTAL_GEN, 'icon': SOLAR_POWER, 'must_incr': True}}, # noqa: E501
|
||||||
|
|
||||||
# controller:
|
# controller:
|
||||||
Register.SIGNAL_STRENGTH: {'name': ['controller', 'Signal_Strength'], 'level': logging.DEBUG, 'unit': '%', 'ha': {'dev': 'controller', 'dev_cla': None, 'stat_cla': 'measurement', 'id': 'signal_', 'fmt': FMT_INT, 'name': 'Signal Strength', 'icon': WIFI}}, # noqa: E501
|
Register.SIGNAL_STRENGTH: {'name': ['controller', 'Signal_Strength'], 'level': logging.INFO, 'unit': '%', 'ha': {'dev': 'controller', 'dev_cla': None, 'stat_cla': 'measurement', 'id': 'signal_', 'fmt': FMT_INT, 'name': 'Signal Strength', 'icon': WIFI}}, # noqa: E501
|
||||||
Register.POWER_ON_TIME: {'name': ['controller', 'Power_On_Time'], 'level': logging.DEBUG, 'unit': 's', 'ha': {'dev': 'controller', 'dev_cla': 'duration', 'stat_cla': 'measurement', 'id': 'power_on_time_', 'fmt': FMT_INT, 'name': 'Power on Time', 'ent_cat': 'diagnostic'}}, # noqa: E501
|
Register.POWER_ON_TIME: {'name': ['controller', 'Power_On_Time'], 'level': logging.INFO, 'unit': 's', 'ha': {'dev': 'controller', 'dev_cla': 'duration', 'stat_cla': 'measurement', 'id': 'power_on_time_', 'fmt': FMT_INT, 'name': 'Power on Time', 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
Register.COLLECT_INTERVAL: {'name': ['controller', 'Collect_Interval'], 'level': logging.DEBUG, 'unit': 'min', 'ha': {'dev': 'controller', 'dev_cla': None, 'stat_cla': None, 'id': 'data_collect_intval_', 'fmt': '| string + " min"', 'name': 'Data Collect Interval', 'icon': UPDATE, 'ent_cat': 'diagnostic'}}, # noqa: E501
|
Register.COLLECT_INTERVAL: {'name': ['controller', 'Collect_Interval'], 'level': logging.INFO, 'unit': 'min', 'ha': {'dev': 'controller', 'dev_cla': None, 'stat_cla': None, 'id': 'data_collect_intval_', 'fmt': '| string + " min"', 'name': 'Data Collect Interval', 'icon': UPDATE, 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
Register.CONNECT_COUNT: {'name': ['controller', 'Connect_Count'], 'level': logging.DEBUG, 'unit': '', 'ha': {'dev': 'controller', 'dev_cla': None, 'stat_cla': None, 'id': 'connect_count_', 'fmt': FMT_INT, 'name': 'Connect Count', 'icon': COUNTER, 'comp': 'sensor', 'ent_cat': 'diagnostic'}}, # noqa: E501
|
Register.CONNECT_COUNT: {'name': ['controller', 'Connect_Count'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'controller', 'dev_cla': None, 'stat_cla': None, 'id': 'connect_count_', 'fmt': FMT_INT, 'name': 'Connect Count', 'icon': COUNTER, 'comp': 'sensor', 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
Register.COMMUNICATION_TYPE: {'name': ['controller', 'Communication_Type'], 'level': logging.DEBUG, 'unit': '', 'ha': {'dev': 'controller', 'dev_cla': None, 'stat_cla': None, 'id': 'comm_type_', 'name': 'Communication Type', 'val_tpl': __comm_type_val_tpl, 'comp': 'sensor', 'icon': WIFI}}, # noqa: E501
|
Register.COMMUNICATION_TYPE: {'name': ['controller', 'Communication_Type'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'controller', 'dev_cla': None, 'stat_cla': None, 'id': 'comm_type_', 'name': 'Communication Type', 'val_tpl': __comm_type_val_tpl, 'comp': 'sensor', 'icon': WIFI}}, # noqa: E501
|
||||||
Register.DATA_UP_INTERVAL: {'name': ['controller', 'Data_Up_Interval'], 'level': logging.DEBUG, 'unit': 's', 'ha': {'dev': 'controller', 'dev_cla': None, 'stat_cla': None, 'id': 'data_up_intval_', 'fmt': FMT_STRING_SEC, 'name': 'Data Up Interval', 'icon': UPDATE, 'ent_cat': 'diagnostic'}}, # noqa: E501
|
Register.DATA_UP_INTERVAL: {'name': ['controller', 'Data_Up_Interval'], 'level': logging.INFO, 'unit': 's', 'ha': {'dev': 'controller', 'dev_cla': None, 'stat_cla': None, 'id': 'data_up_intval_', 'fmt': FMT_STRING_SEC, 'name': 'Data Up Interval', 'icon': UPDATE, 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
Register.HEARTBEAT_INTERVAL: {'name': ['controller', 'Heartbeat_Interval'], 'level': logging.DEBUG, 'unit': 's', 'ha': {'dev': 'controller', 'dev_cla': None, 'stat_cla': None, 'id': 'heartbeat_intval_', 'fmt': FMT_STRING_SEC, 'name': 'Heartbeat Interval', 'icon': UPDATE, 'ent_cat': 'diagnostic'}}, # noqa: E501
|
Register.HEARTBEAT_INTERVAL: {'name': ['controller', 'Heartbeat_Interval'], 'level': logging.INFO, 'unit': 's', 'ha': {'dev': 'controller', 'dev_cla': None, 'stat_cla': None, 'id': 'heartbeat_intval_', 'fmt': FMT_STRING_SEC, 'name': 'Heartbeat Interval', 'icon': UPDATE, 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
Register.IP_ADDRESS: {'name': ['controller', 'IP_Address'], 'level': logging.DEBUG, 'unit': '', 'ha': {'dev': 'controller', 'dev_cla': None, 'stat_cla': None, 'id': 'ip_address_', 'fmt': '| string', 'name': 'IP Address', 'icon': WIFI, 'ent_cat': 'diagnostic'}}, # noqa: E501
|
Register.IP_ADDRESS: {'name': ['controller', 'IP_Address'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'controller', 'dev_cla': None, 'stat_cla': None, 'id': 'ip_address_', 'fmt': '| string', 'name': 'IP Address', 'icon': WIFI, 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
Register.POLLING_INTERVAL: {'name': ['controller', 'Polling_Interval'], 'level': logging.DEBUG, 'unit': 's', 'ha': {'dev': 'controller', 'dev_cla': None, 'stat_cla': None, 'id': 'polling_intval_', 'fmt': FMT_STRING_SEC, 'name': 'Polling Interval', 'icon': UPDATE, 'ent_cat': 'diagnostic'}}, # noqa: E501
|
Register.POLLING_INTERVAL: {'name': ['controller', 'Polling_Interval'], 'level': logging.INFO, 'unit': 's', 'ha': {'dev': 'controller', 'dev_cla': None, 'stat_cla': None, 'id': 'polling_intval_', 'fmt': FMT_STRING_SEC, 'name': 'Polling Interval', 'icon': UPDATE, 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
Register.SENSOR_LIST: {'name': ['controller', 'Sensor_List'], 'level': logging.INFO, 'unit': ''}, # noqa: E501
|
Register.SENSOR_LIST: {'name': ['controller', 'Sensor_List'], 'level': logging.INFO, 'unit': ''}, # noqa: E501
|
||||||
Register.SSID: {'name': ['controller', 'WiFi_SSID'], 'level': logging.DEBUG, 'unit': ''}, # noqa: E501
|
Register.SSID: {'name': ['controller', 'WiFi_SSID'], 'level': logging.DEBUG, 'unit': ''}, # noqa: E501
|
||||||
|
|
||||||
@@ -536,6 +567,33 @@ class Infos:
|
|||||||
Register.PROD_COMPL_TYPE: {'name': ['other', 'Prod_Compliance_Type'], 'level': logging.INFO, 'unit': ''}, # noqa: E501
|
Register.PROD_COMPL_TYPE: {'name': ['other', 'Prod_Compliance_Type'], 'level': logging.INFO, 'unit': ''}, # noqa: E501
|
||||||
Register.INV_UNKNOWN_1: {'name': ['inv_unknown', 'Unknown_1'], 'level': logging.DEBUG, 'unit': ''}, # noqa: E501
|
Register.INV_UNKNOWN_1: {'name': ['inv_unknown', 'Unknown_1'], 'level': logging.DEBUG, 'unit': ''}, # noqa: E501
|
||||||
|
|
||||||
|
Register.BATT_PV1_VOLT: {'name': ['batterie', 'pv1', 'Voltage'], 'level': logging.INFO, 'unit': 'V', 'ha': {'dev': 'bat_inp_pv1', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_pv1_', 'val_tpl': "{{ (value_json['pv1']['Voltage'] | float)}}", 'icon': GAUGE, 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
|
Register.BATT_PV1_CUR: {'name': ['batterie', 'pv1', 'Current'], 'level': logging.INFO, 'unit': 'A', 'ha': {'dev': 'bat_inp_pv1', 'dev_cla': 'current', 'stat_cla': 'measurement', 'id': 'cur_pv1_', 'val_tpl': "{{ (value_json['pv1']['Current'] | float)}}", 'icon': GAUGE, 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
|
Register.BATT_PV2_VOLT: {'name': ['batterie', 'pv2', 'Voltage'], 'level': logging.INFO, 'unit': 'V', 'ha': {'dev': 'bat_inp_pv2', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_pv2_', 'val_tpl': "{{ (value_json['pv2']['Voltage'] | float)}}", 'icon': GAUGE, 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
|
Register.BATT_PV2_CUR: {'name': ['batterie', 'pv2', 'Current'], 'level': logging.INFO, 'unit': 'A', 'ha': {'dev': 'bat_inp_pv2', 'dev_cla': 'current', 'stat_cla': 'measurement', 'id': 'cur_pv2_', 'val_tpl': "{{ (value_json['pv2']['Current'] | float)}}", 'icon': GAUGE, 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
|
Register.BATT_38: {'name': ['batterie', 'Reg_38'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'batterie', 'dev_cla': 'power', 'stat_cla': 'measurement', 'id': 'batt_38_', 'fmt': FMT_FLOAT, 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
|
Register.BATT_3a: {'name': ['batterie', 'Reg_3a'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'batterie', 'dev_cla': 'power', 'stat_cla': 'measurement', 'id': 'batt_3a_', 'fmt': FMT_INT, 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
|
Register.BATT_3c: {'name': ['batterie', 'Reg_3c'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'batterie', 'dev_cla': 'power', 'stat_cla': 'measurement', 'id': 'batt_3c_', 'fmt': FMT_INT, 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
|
Register.BATT_3e: {'name': ['batterie', 'Reg_3e'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'batterie', 'dev_cla': 'power', 'stat_cla': 'measurement', 'id': 'batt_3e_', 'fmt': FMT_INT, 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
|
Register.BATT_40: {'name': ['batterie', 'Reg_40'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'batterie', 'dev_cla': 'power', 'stat_cla': 'measurement', 'id': 'batt_40_', 'fmt': FMT_FLOAT, 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
|
Register.BATT_42: {'name': ['batterie', 'Reg_42'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'batterie', 'dev_cla': 'power', 'stat_cla': 'measurement', 'id': 'batt_42_', 'fmt': FMT_FLOAT, 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
|
Register.BATT_SOC: {'name': ['batterie', 'SOC'], 'level': logging.INFO, 'unit': '%', 'ha': {'dev': 'batterie', 'dev_cla': None, 'stat_cla': 'measurement', 'id': 'soc_', 'fmt': FMT_FLOAT, 'name': 'State of Charge', 'icon': 'mdi:battery-90'}}, # noqa: E501
|
||||||
|
# Register.BATT_46: {'name': ['batterie', 'Reg_46'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'batterie', 'dev_cla': 'power', 'stat_cla': 'measurement', 'id': 'batt_46_', 'fmt': FMT_INT, 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
|
# Register.BATT_48: {'name': ['batterie', 'Reg_48'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'batterie', 'dev_cla': 'power', 'stat_cla': 'measurement', 'id': 'batt_48_', 'fmt': FMT_INT, 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
|
# Register.BATT_4a: {'name': ['batterie', 'Reg_4a'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'batterie', 'dev_cla': 'power', 'stat_cla': 'measurement', 'id': 'batt_4a_', 'fmt': FMT_INT, 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
|
# Register.BATT_4c: {'name': ['batterie', 'Reg_4c'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'batterie', 'dev_cla': 'power', 'stat_cla': 'measurement', 'id': 'batt_4c_', 'fmt': FMT_INT, 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
|
# Register.BATT_4e: {'name': ['batterie', 'Reg_4e'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'batterie', 'dev_cla': 'power', 'stat_cla': 'measurement', 'id': 'batt_4e_', 'fmt': FMT_INT, 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
|
|
||||||
|
Register.BATT_66: {'name': ['batterie', 'Reg_66'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'batterie', 'dev_cla': 'power', 'stat_cla': 'measurement', 'id': 'batt_66_', 'fmt': FMT_INT, 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
|
Register.BATT_68: {'name': ['batterie', 'Reg_68'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'batterie', 'dev_cla': 'power', 'stat_cla': 'measurement', 'id': 'batt_68_', 'fmt': FMT_INT, 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
|
Register.BATT_6a: {'name': ['batterie', 'Reg_6a'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'batterie', 'dev_cla': 'power', 'stat_cla': 'measurement', 'id': 'batt_6a_', 'fmt': FMT_INT, 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
|
Register.BATT_6c: {'name': ['batterie', 'Reg_6c'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'batterie', 'dev_cla': 'power', 'stat_cla': 'measurement', 'id': 'batt_6c_', 'fmt': FMT_INT, 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
|
Register.BATT_6e: {'name': ['batterie', 'Reg_6e'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'batterie', 'dev_cla': 'power', 'stat_cla': 'measurement', 'id': 'batt_6e_', 'fmt': FMT_INT, 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
|
Register.BATT_70: {'name': ['batterie', 'Reg_70'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'batterie', 'dev_cla': 'power', 'stat_cla': 'measurement', 'id': 'batt_70_', 'fmt': FMT_INT, 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
|
Register.BATT_72: {'name': ['batterie', 'Reg_72'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'batterie', 'dev_cla': 'power', 'stat_cla': 'measurement', 'id': 'batt_72_', 'fmt': FMT_INT, 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
|
Register.BATT_74: {'name': ['batterie', 'Reg_74'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'batterie', 'dev_cla': 'power', 'stat_cla': 'measurement', 'id': 'batt_74_', 'fmt': FMT_INT, 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
|
Register.BATT_76: {'name': ['batterie', 'Reg_76'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'batterie', 'dev_cla': 'power', 'stat_cla': 'measurement', 'id': 'batt_76_', 'fmt': FMT_INT, 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
|
Register.BATT_78: {'name': ['batterie', 'Reg_78'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'batterie', 'dev_cla': 'power', 'stat_cla': 'measurement', 'id': 'batt_78_', 'fmt': FMT_INT, 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
}
|
}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|||||||
@@ -151,6 +151,8 @@ class InverterBase(InverterIfc, Proxy):
|
|||||||
# home assistant has changed the status back to online
|
# home assistant has changed the status back to online
|
||||||
try:
|
try:
|
||||||
if (('inverter' in stream.new_data and stream.new_data['inverter'])
|
if (('inverter' in stream.new_data and stream.new_data['inverter'])
|
||||||
|
or ('batterie' in stream.new_data and
|
||||||
|
stream.new_data['batterie'])
|
||||||
or ('collector' in stream.new_data and
|
or ('collector' in stream.new_data and
|
||||||
stream.new_data['collector'])
|
stream.new_data['collector'])
|
||||||
or self.mqtt.ha_restarts != self.__ha_restarts):
|
or self.mqtt.ha_restarts != self.__ha_restarts):
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import logging
|
import logging
|
||||||
import traceback
|
import traceback
|
||||||
import asyncio
|
import asyncio
|
||||||
|
from itertools import chain
|
||||||
|
|
||||||
from cnf.config import Config
|
from cnf.config import Config
|
||||||
from gen3plus.inverter_g3p import InverterG3P
|
from gen3plus.inverter_g3p import InverterG3P
|
||||||
@@ -42,14 +43,15 @@ class ModbusTcp():
|
|||||||
self.tim_restart = tim_restart
|
self.tim_restart = tim_restart
|
||||||
|
|
||||||
inverters = Config.get('inverters')
|
inverters = Config.get('inverters')
|
||||||
|
batteries = Config.get('batteries')
|
||||||
# logging.info(f'Inverters: {inverters}')
|
# logging.info(f'Inverters: {inverters}')
|
||||||
|
|
||||||
for inv in inverters.values():
|
for _, inv in chain(inverters.items(), batteries.items()):
|
||||||
if (type(inv) is dict
|
if (type(inv) is dict
|
||||||
and 'monitor_sn' in inv
|
and 'monitor_sn' in inv
|
||||||
and 'client_mode' in inv):
|
and 'client_mode' in inv):
|
||||||
client = inv['client_mode']
|
client = inv['client_mode']
|
||||||
logger.info(f"'client_mode' for snr: {inv['monitor_sn']} host: {client['host']}:{client['port']}, forward: {client['forward']}") # noqa: E501
|
logger.info(f"'client_mode' for Monitoring-SN: {inv['monitor_sn']} host: {client['host']}:{client['port']}, forward: {client['forward']}") # noqa: E501
|
||||||
loop.create_task(self.modbus_loop(client['host'],
|
loop.create_task(self.modbus_loop(client['host'],
|
||||||
client['port'],
|
client['port'],
|
||||||
inv['monitor_sn'],
|
inv['monitor_sn'],
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
import json
|
import json
|
||||||
|
from itertools import chain
|
||||||
|
|
||||||
from cnf.config import Config
|
from cnf.config import Config
|
||||||
from mqtt import Mqtt
|
from mqtt import Mqtt
|
||||||
@@ -56,8 +57,9 @@ class Proxy():
|
|||||||
# reset at midnight when you restart the proxy just before
|
# reset at midnight when you restart the proxy just before
|
||||||
# midnight!
|
# midnight!
|
||||||
inverters = Config.get('inverters')
|
inverters = Config.get('inverters')
|
||||||
|
batteries = Config.get('batteries')
|
||||||
# logger.debug(f'Proxys: {inverters}')
|
# logger.debug(f'Proxys: {inverters}')
|
||||||
for inv in inverters.values():
|
for _, inv in chain(inverters.items(), batteries.items()):
|
||||||
if (type(inv) is dict):
|
if (type(inv) is dict):
|
||||||
node_id = inv['node_id']
|
node_id = inv['node_id']
|
||||||
cls.db_stat.reg_clr_at_midnight(f'{cls.entity_prfx}{node_id}',
|
cls.db_stat.reg_clr_at_midnight(f'{cls.entity_prfx}{node_id}',
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ def ConfigDefault():
|
|||||||
'type': 'RSM40-8-395M'},
|
'type': 'RSM40-8-395M'},
|
||||||
'pv2': {'manufacturer': 'Risen',
|
'pv2': {'manufacturer': 'Risen',
|
||||||
'type': 'RSM40-8-395M'},
|
'type': 'RSM40-8-395M'},
|
||||||
'sensor_list': 688
|
'sensor_list': 0
|
||||||
},
|
},
|
||||||
'Y170000000000001': {
|
'Y170000000000001': {
|
||||||
'modbus_polling': True,
|
'modbus_polling': True,
|
||||||
@@ -91,8 +91,21 @@ def ConfigDefault():
|
|||||||
'type': 'RSM40-8-410M'},
|
'type': 'RSM40-8-410M'},
|
||||||
'pv4': {'manufacturer': 'Risen',
|
'pv4': {'manufacturer': 'Risen',
|
||||||
'type': 'RSM40-8-410M'},
|
'type': 'RSM40-8-410M'},
|
||||||
'sensor_list': 688
|
'sensor_list': 0
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
'batteries': {
|
||||||
|
'4100000000000001': {
|
||||||
|
'modbus_polling': True,
|
||||||
|
'monitor_sn': 3000000000,
|
||||||
|
'suggested_area': '',
|
||||||
|
'node_id': '',
|
||||||
|
'pv1': {'manufacturer': 'Risen',
|
||||||
|
'type': 'RSM40-8-410M'},
|
||||||
|
'pv2': {'manufacturer': 'Risen',
|
||||||
|
'type': 'RSM40-8-410M'},
|
||||||
|
'sensor_list': 0,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,6 +153,18 @@ def ConfigComplete():
|
|||||||
'type': 'type4'},
|
'type': 'type4'},
|
||||||
'suggested_area': 'Garage2',
|
'suggested_area': 'Garage2',
|
||||||
'sensor_list': 688}
|
'sensor_list': 688}
|
||||||
|
},
|
||||||
|
'batteries': {
|
||||||
|
'4100000000000001': {
|
||||||
|
'modbus_polling': True,
|
||||||
|
'monitor_sn': 3000000000,
|
||||||
|
'suggested_area': 'Garage3',
|
||||||
|
'node_id': 'Bat-Garage3/',
|
||||||
|
'pv1': {'manufacturer': 'man5',
|
||||||
|
'type': 'type5'},
|
||||||
|
'pv2': {'manufacturer': 'man6',
|
||||||
|
'type': 'type6'},
|
||||||
|
'sensor_list': 12326}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,6 +172,19 @@ def test_default_config():
|
|||||||
Config.init(ConfigReadToml("app/src/cnf/default_config.toml"))
|
Config.init(ConfigReadToml("app/src/cnf/default_config.toml"))
|
||||||
validated = Config.def_config
|
validated = Config.def_config
|
||||||
assert validated == {'gen3plus': {'at_acl': {'mqtt': {'allow': ['AT+'], 'block': []}, 'tsun': {'allow': ['AT+Z', 'AT+UPURL', 'AT+SUPDATE'], 'block': []}}}, 'tsun': {'enabled': True, 'host': 'logger.talent-monitoring.com', 'port': 5005}, 'solarman': {'enabled': True, 'host': 'iot.talent-monitoring.com', 'port': 10000}, 'mqtt': {'host': 'mqtt', 'port': 1883, 'user': None, 'passwd': None}, 'ha': {'auto_conf_prefix': 'homeassistant', 'discovery_prefix': 'homeassistant', 'entity_prefix': 'tsun', 'proxy_node_id': 'proxy', 'proxy_unique_id': 'P170000000000001'},
|
assert validated == {'gen3plus': {'at_acl': {'mqtt': {'allow': ['AT+'], 'block': []}, 'tsun': {'allow': ['AT+Z', 'AT+UPURL', 'AT+SUPDATE'], 'block': []}}}, 'tsun': {'enabled': True, 'host': 'logger.talent-monitoring.com', 'port': 5005}, 'solarman': {'enabled': True, 'host': 'iot.talent-monitoring.com', 'port': 10000}, 'mqtt': {'host': 'mqtt', 'port': 1883, 'user': None, 'passwd': None}, 'ha': {'auto_conf_prefix': 'homeassistant', 'discovery_prefix': 'homeassistant', 'entity_prefix': 'tsun', 'proxy_node_id': 'proxy', 'proxy_unique_id': 'P170000000000001'},
|
||||||
|
'batteries': {
|
||||||
|
'4100000000000001': {
|
||||||
|
'modbus_polling': True,
|
||||||
|
'monitor_sn': 3000000000,
|
||||||
|
'node_id': '',
|
||||||
|
'pv1': {'manufacturer': 'Risen',
|
||||||
|
'type': 'RSM40-8-410M'},
|
||||||
|
'pv2': {'manufacturer': 'Risen',
|
||||||
|
'type': 'RSM40-8-410M'},
|
||||||
|
'sensor_list': 0,
|
||||||
|
'suggested_area': ''
|
||||||
|
}
|
||||||
|
},
|
||||||
'inverters': {
|
'inverters': {
|
||||||
'allow_all': False,
|
'allow_all': False,
|
||||||
'R170000000000001': {
|
'R170000000000001': {
|
||||||
@@ -158,7 +196,7 @@ def test_default_config():
|
|||||||
'modbus_polling': False,
|
'modbus_polling': False,
|
||||||
'monitor_sn': 0,
|
'monitor_sn': 0,
|
||||||
'suggested_area': '',
|
'suggested_area': '',
|
||||||
'sensor_list': 688},
|
'sensor_list': 0},
|
||||||
'Y170000000000001': {
|
'Y170000000000001': {
|
||||||
'modbus_polling': True,
|
'modbus_polling': True,
|
||||||
'monitor_sn': 2000000000,
|
'monitor_sn': 2000000000,
|
||||||
@@ -172,7 +210,7 @@ def test_default_config():
|
|||||||
'pv4': {'manufacturer': 'Risen',
|
'pv4': {'manufacturer': 'Risen',
|
||||||
'type': 'RSM40-8-410M'},
|
'type': 'RSM40-8-410M'},
|
||||||
'suggested_area': '',
|
'suggested_area': '',
|
||||||
'sensor_list': 688}}}
|
'sensor_list': 0}}}
|
||||||
|
|
||||||
def test_full_config(ConfigComplete):
|
def test_full_config(ConfigComplete):
|
||||||
cnf = {'tsun': {'enabled': True, 'host': 'logger.talent-monitoring.com', 'port': 5005},
|
cnf = {'tsun': {'enabled': True, 'host': 'logger.talent-monitoring.com', 'port': 5005},
|
||||||
@@ -181,9 +219,14 @@ def test_full_config(ConfigComplete):
|
|||||||
'solarman': {'enabled': True, 'host': 'iot.talent-monitoring.com', 'port': 10000},
|
'solarman': {'enabled': True, 'host': 'iot.talent-monitoring.com', 'port': 10000},
|
||||||
'mqtt': {'host': 'mqtt', 'port': 1883, 'user': '', 'passwd': ''},
|
'mqtt': {'host': 'mqtt', 'port': 1883, 'user': '', 'passwd': ''},
|
||||||
'ha': {'auto_conf_prefix': 'homeassistant', 'discovery_prefix': 'homeassistant', 'entity_prefix': 'tsun', 'proxy_node_id': 'proxy', 'proxy_unique_id': 'P170000000000001'},
|
'ha': {'auto_conf_prefix': 'homeassistant', 'discovery_prefix': 'homeassistant', 'entity_prefix': 'tsun', 'proxy_node_id': 'proxy', 'proxy_unique_id': 'P170000000000001'},
|
||||||
|
'batteries': {
|
||||||
|
'4100000000000001': {'modbus_polling': True, 'monitor_sn': 3000000000, 'node_id': 'Bat-Garage3/', 'sensor_list': 0x3026, 'suggested_area': 'Garage3', 'pv1': {'type': 'type5', 'manufacturer': 'man5'}, 'pv2': {'type': 'type6', 'manufacturer': 'man6'}}
|
||||||
|
},
|
||||||
'inverters': {'allow_all': False,
|
'inverters': {'allow_all': False,
|
||||||
'R170000000000001': {'modbus_polling': False, 'node_id': 'PV-Garage/', 'sensor_list': 0x02B0, 'suggested_area': 'Garage', 'pv1': {'type': 'type1', 'manufacturer': 'man1'}, 'pv2': {'type': 'type2', 'manufacturer': 'man2'}},
|
'R170000000000001': {'modbus_polling': False, 'node_id': 'PV-Garage/', 'sensor_list': 0x02B0, 'suggested_area': 'Garage', 'pv1': {'type': 'type1', 'manufacturer': 'man1'}, 'pv2': {'type': 'type2', 'manufacturer': 'man2'}},
|
||||||
'Y170000000000001': {'modbus_polling': True, 'monitor_sn': 2000000000, 'node_id': 'PV-Garage2/', 'sensor_list': 0x02B0, 'suggested_area': 'Garage2', 'pv1': {'type': 'type1', 'manufacturer': 'man1'}, 'pv2': {'type': 'type2', 'manufacturer': 'man2'}, 'pv3': {'type': 'type3', 'manufacturer': 'man3'}, 'pv4': {'type': 'type4', 'manufacturer': 'man4'}}}}
|
'Y170000000000001': {'modbus_polling': True, 'monitor_sn': 2000000000, 'node_id': 'PV-Garage2/', 'sensor_list': 0x02B0, 'suggested_area': 'Garage2', 'pv1': {'type': 'type1', 'manufacturer': 'man1'}, 'pv2': {'type': 'type2', 'manufacturer': 'man2'}, 'pv3': {'type': 'type3', 'manufacturer': 'man3'}, 'pv4': {'type': 'type4', 'manufacturer': 'man4'}}
|
||||||
|
}
|
||||||
|
}
|
||||||
try:
|
try:
|
||||||
validated = Config.conf_schema.validate(cnf)
|
validated = Config.conf_schema.validate(cnf)
|
||||||
except Exception:
|
except Exception:
|
||||||
@@ -240,6 +283,19 @@ def test_read_cnf1():
|
|||||||
assert err == None
|
assert err == None
|
||||||
cnf = Config.get()
|
cnf = Config.get()
|
||||||
assert cnf == {'gen3plus': {'at_acl': {'mqtt': {'allow': ['AT+'], 'block': []}, 'tsun': {'allow': ['AT+Z', 'AT+UPURL', 'AT+SUPDATE'], 'block': []}}}, 'tsun': {'enabled': True, 'host': 'logger.talent-monitoring.com', 'port': 5005}, 'solarman': {'enabled': False, 'host': 'iot.talent-monitoring.com', 'port': 10000}, 'mqtt': {'host': 'mqtt', 'port': 1883, 'user': None, 'passwd': None}, 'ha': {'auto_conf_prefix': 'homeassistant', 'discovery_prefix': 'homeassistant', 'entity_prefix': 'tsun', 'proxy_node_id': 'proxy', 'proxy_unique_id': 'P170000000000001'},
|
assert cnf == {'gen3plus': {'at_acl': {'mqtt': {'allow': ['AT+'], 'block': []}, 'tsun': {'allow': ['AT+Z', 'AT+UPURL', 'AT+SUPDATE'], 'block': []}}}, 'tsun': {'enabled': True, 'host': 'logger.talent-monitoring.com', 'port': 5005}, 'solarman': {'enabled': False, 'host': 'iot.talent-monitoring.com', 'port': 10000}, 'mqtt': {'host': 'mqtt', 'port': 1883, 'user': None, 'passwd': None}, 'ha': {'auto_conf_prefix': 'homeassistant', 'discovery_prefix': 'homeassistant', 'entity_prefix': 'tsun', 'proxy_node_id': 'proxy', 'proxy_unique_id': 'P170000000000001'},
|
||||||
|
'batteries': {
|
||||||
|
'4100000000000001': {
|
||||||
|
'modbus_polling': True,
|
||||||
|
'monitor_sn': 3000000000,
|
||||||
|
'node_id': '',
|
||||||
|
'pv1': {'manufacturer': 'Risen',
|
||||||
|
'type': 'RSM40-8-410M'},
|
||||||
|
'pv2': {'manufacturer': 'Risen',
|
||||||
|
'type': 'RSM40-8-410M'},
|
||||||
|
'sensor_list': 0,
|
||||||
|
'suggested_area': ''
|
||||||
|
}
|
||||||
|
},
|
||||||
'inverters': {
|
'inverters': {
|
||||||
'allow_all': False,
|
'allow_all': False,
|
||||||
'R170000000000001': {
|
'R170000000000001': {
|
||||||
@@ -251,7 +307,7 @@ def test_read_cnf1():
|
|||||||
'type': 'RSM40-8-395M'},
|
'type': 'RSM40-8-395M'},
|
||||||
'pv2': {'manufacturer': 'Risen',
|
'pv2': {'manufacturer': 'Risen',
|
||||||
'type': 'RSM40-8-395M'},
|
'type': 'RSM40-8-395M'},
|
||||||
'sensor_list': 688
|
'sensor_list': 0
|
||||||
},
|
},
|
||||||
'Y170000000000001': {
|
'Y170000000000001': {
|
||||||
'modbus_polling': True,
|
'modbus_polling': True,
|
||||||
@@ -266,7 +322,7 @@ def test_read_cnf1():
|
|||||||
'type': 'RSM40-8-410M'},
|
'type': 'RSM40-8-410M'},
|
||||||
'pv4': {'manufacturer': 'Risen',
|
'pv4': {'manufacturer': 'Risen',
|
||||||
'type': 'RSM40-8-410M'},
|
'type': 'RSM40-8-410M'},
|
||||||
'sensor_list': 688
|
'sensor_list': 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -287,6 +343,19 @@ def test_read_cnf2():
|
|||||||
assert err == None
|
assert err == None
|
||||||
cnf = Config.get()
|
cnf = Config.get()
|
||||||
assert cnf == {'gen3plus': {'at_acl': {'mqtt': {'allow': ['AT+'], 'block': []}, 'tsun': {'allow': ['AT+Z', 'AT+UPURL', 'AT+SUPDATE'], 'block': []}}}, 'tsun': {'enabled': True, 'host': 'logger.talent-monitoring.com', 'port': 5005}, 'solarman': {'enabled': True, 'host': 'iot.talent-monitoring.com', 'port': 10000}, 'mqtt': {'host': 'mqtt', 'port': 1883, 'user': None, 'passwd': None}, 'ha': {'auto_conf_prefix': 'homeassistant', 'discovery_prefix': 'homeassistant', 'entity_prefix': 'tsun', 'proxy_node_id': 'proxy', 'proxy_unique_id': 'P170000000000001'},
|
assert cnf == {'gen3plus': {'at_acl': {'mqtt': {'allow': ['AT+'], 'block': []}, 'tsun': {'allow': ['AT+Z', 'AT+UPURL', 'AT+SUPDATE'], 'block': []}}}, 'tsun': {'enabled': True, 'host': 'logger.talent-monitoring.com', 'port': 5005}, 'solarman': {'enabled': True, 'host': 'iot.talent-monitoring.com', 'port': 10000}, 'mqtt': {'host': 'mqtt', 'port': 1883, 'user': None, 'passwd': None}, 'ha': {'auto_conf_prefix': 'homeassistant', 'discovery_prefix': 'homeassistant', 'entity_prefix': 'tsun', 'proxy_node_id': 'proxy', 'proxy_unique_id': 'P170000000000001'},
|
||||||
|
'batteries': {
|
||||||
|
'4100000000000001': {
|
||||||
|
'modbus_polling': True,
|
||||||
|
'monitor_sn': 3000000000,
|
||||||
|
'node_id': '',
|
||||||
|
'pv1': {'manufacturer': 'Risen',
|
||||||
|
'type': 'RSM40-8-410M'},
|
||||||
|
'pv2': {'manufacturer': 'Risen',
|
||||||
|
'type': 'RSM40-8-410M'},
|
||||||
|
'sensor_list': 0,
|
||||||
|
'suggested_area': ''
|
||||||
|
}
|
||||||
|
},
|
||||||
'inverters': {
|
'inverters': {
|
||||||
'allow_all': False,
|
'allow_all': False,
|
||||||
'R170000000000001': {
|
'R170000000000001': {
|
||||||
@@ -298,7 +367,7 @@ def test_read_cnf2():
|
|||||||
'type': 'RSM40-8-395M'},
|
'type': 'RSM40-8-395M'},
|
||||||
'pv2': {'manufacturer': 'Risen',
|
'pv2': {'manufacturer': 'Risen',
|
||||||
'type': 'RSM40-8-395M'},
|
'type': 'RSM40-8-395M'},
|
||||||
'sensor_list': 688
|
'sensor_list': 0
|
||||||
},
|
},
|
||||||
'Y170000000000001': {
|
'Y170000000000001': {
|
||||||
'modbus_polling': True,
|
'modbus_polling': True,
|
||||||
@@ -313,7 +382,7 @@ def test_read_cnf2():
|
|||||||
'type': 'RSM40-8-410M'},
|
'type': 'RSM40-8-410M'},
|
||||||
'pv4': {'manufacturer': 'Risen',
|
'pv4': {'manufacturer': 'Risen',
|
||||||
'type': 'RSM40-8-410M'},
|
'type': 'RSM40-8-410M'},
|
||||||
'sensor_list': 688
|
'sensor_list': 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -342,6 +411,19 @@ def test_read_cnf4():
|
|||||||
assert err == None
|
assert err == None
|
||||||
cnf = Config.get()
|
cnf = Config.get()
|
||||||
assert cnf == {'gen3plus': {'at_acl': {'mqtt': {'allow': ['AT+'], 'block': []}, 'tsun': {'allow': ['AT+Z', 'AT+UPURL', 'AT+SUPDATE'], 'block': []}}}, 'tsun': {'enabled': True, 'host': 'logger.talent-monitoring.com', 'port': 5005}, 'solarman': {'enabled': True, 'host': 'iot.talent-monitoring.com', 'port': 5000}, 'mqtt': {'host': 'mqtt', 'port': 1883, 'user': None, 'passwd': None}, 'ha': {'auto_conf_prefix': 'homeassistant', 'discovery_prefix': 'homeassistant', 'entity_prefix': 'tsun', 'proxy_node_id': 'proxy', 'proxy_unique_id': 'P170000000000001'},
|
assert cnf == {'gen3plus': {'at_acl': {'mqtt': {'allow': ['AT+'], 'block': []}, 'tsun': {'allow': ['AT+Z', 'AT+UPURL', 'AT+SUPDATE'], 'block': []}}}, 'tsun': {'enabled': True, 'host': 'logger.talent-monitoring.com', 'port': 5005}, 'solarman': {'enabled': True, 'host': 'iot.talent-monitoring.com', 'port': 5000}, 'mqtt': {'host': 'mqtt', 'port': 1883, 'user': None, 'passwd': None}, 'ha': {'auto_conf_prefix': 'homeassistant', 'discovery_prefix': 'homeassistant', 'entity_prefix': 'tsun', 'proxy_node_id': 'proxy', 'proxy_unique_id': 'P170000000000001'},
|
||||||
|
'batteries': {
|
||||||
|
'4100000000000001': {
|
||||||
|
'modbus_polling': True,
|
||||||
|
'monitor_sn': 3000000000,
|
||||||
|
'node_id': '',
|
||||||
|
'pv1': {'manufacturer': 'Risen',
|
||||||
|
'type': 'RSM40-8-410M'},
|
||||||
|
'pv2': {'manufacturer': 'Risen',
|
||||||
|
'type': 'RSM40-8-410M'},
|
||||||
|
'sensor_list': 0,
|
||||||
|
'suggested_area': ''
|
||||||
|
}
|
||||||
|
},
|
||||||
'inverters': {
|
'inverters': {
|
||||||
'allow_all': False,
|
'allow_all': False,
|
||||||
'R170000000000001': {
|
'R170000000000001': {
|
||||||
@@ -353,7 +435,7 @@ def test_read_cnf4():
|
|||||||
'type': 'RSM40-8-395M'},
|
'type': 'RSM40-8-395M'},
|
||||||
'pv2': {'manufacturer': 'Risen',
|
'pv2': {'manufacturer': 'Risen',
|
||||||
'type': 'RSM40-8-395M'},
|
'type': 'RSM40-8-395M'},
|
||||||
'sensor_list': 688
|
'sensor_list': 0
|
||||||
},
|
},
|
||||||
'Y170000000000001': {
|
'Y170000000000001': {
|
||||||
'modbus_polling': True,
|
'modbus_polling': True,
|
||||||
@@ -368,7 +450,7 @@ def test_read_cnf4():
|
|||||||
'type': 'RSM40-8-410M'},
|
'type': 'RSM40-8-410M'},
|
||||||
'pv4': {'manufacturer': 'Risen',
|
'pv4': {'manufacturer': 'Risen',
|
||||||
'type': 'RSM40-8-410M'},
|
'type': 'RSM40-8-410M'},
|
||||||
'sensor_list': 688
|
'sensor_list': 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -382,6 +382,20 @@ def test_full_config(ConfigComplete):
|
|||||||
"sensor_list": 688
|
"sensor_list": 688
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"batteries": [
|
||||||
|
{
|
||||||
|
"serial": "4100000000000001",
|
||||||
|
"modbus_polling": true,
|
||||||
|
"monitor_sn": 3000000000,
|
||||||
|
"node_id": "Bat-Garage3",
|
||||||
|
"suggested_area": "Garage3",
|
||||||
|
"pv1.manufacturer": "man5",
|
||||||
|
"pv1.type": "type5",
|
||||||
|
"pv2.manufacturer": "man6",
|
||||||
|
"pv2.type": "type6",
|
||||||
|
"sensor_list": 12326
|
||||||
|
}
|
||||||
|
],
|
||||||
"tsun.enabled": true,
|
"tsun.enabled": true,
|
||||||
"solarman.enabled": true,
|
"solarman.enabled": true,
|
||||||
"inverters.allow_all": false,
|
"inverters.allow_all": false,
|
||||||
|
|||||||
@@ -138,6 +138,7 @@ def test_build_4210(inverter_data: bytes):
|
|||||||
def test_build_ha_conf1():
|
def test_build_ha_conf1():
|
||||||
i = InfosG3P(client_mode=False)
|
i = InfosG3P(client_mode=False)
|
||||||
i.static_init() # initialize counter
|
i.static_init() # initialize counter
|
||||||
|
i.set_db_def_value(Register.SENSOR_LIST, "02b0")
|
||||||
|
|
||||||
tests = 0
|
tests = 0
|
||||||
for d_json, comp, node_id, id in i.ha_confs(ha_prfx="tsun/", node_id="garagendach/", snr='123'):
|
for d_json, comp, node_id, id in i.ha_confs(ha_prfx="tsun/", node_id="garagendach/", snr='123'):
|
||||||
@@ -212,6 +213,7 @@ def test_build_ha_conf2():
|
|||||||
def test_build_ha_conf3():
|
def test_build_ha_conf3():
|
||||||
i = InfosG3P(client_mode=True)
|
i = InfosG3P(client_mode=True)
|
||||||
i.static_init() # initialize counter
|
i.static_init() # initialize counter
|
||||||
|
i.set_db_def_value(Register.SENSOR_LIST, "02b0")
|
||||||
|
|
||||||
tests = 0
|
tests = 0
|
||||||
for d_json, comp, node_id, id in i.ha_confs(ha_prfx="tsun/", node_id="garagendach/", snr='123'):
|
for d_json, comp, node_id, id in i.ha_confs(ha_prfx="tsun/", node_id="garagendach/", snr='123'):
|
||||||
@@ -283,6 +285,35 @@ def test_build_ha_conf4():
|
|||||||
|
|
||||||
assert tests==1
|
assert tests==1
|
||||||
|
|
||||||
|
def test_build_ha_conf5():
|
||||||
|
i = InfosG3P(client_mode=True)
|
||||||
|
i.static_init() # initialize counter
|
||||||
|
i.set_db_def_value(Register.SENSOR_LIST, "3026")
|
||||||
|
|
||||||
|
tests = 0
|
||||||
|
for d_json, comp, node_id, id in i.ha_confs(ha_prfx="tsun/", node_id="garagendach/", snr='123'):
|
||||||
|
|
||||||
|
if id == 'out_power_123':
|
||||||
|
assert False
|
||||||
|
elif id == 'daily_gen_123':
|
||||||
|
assert False
|
||||||
|
elif id == 'power_pv1_123':
|
||||||
|
assert False
|
||||||
|
elif id == 'power_pv2_123':
|
||||||
|
assert False
|
||||||
|
elif id == 'power_pv3_123':
|
||||||
|
assert False
|
||||||
|
elif id == 'power_pv4_123':
|
||||||
|
assert False
|
||||||
|
elif id == 'signal_123':
|
||||||
|
assert comp == 'sensor'
|
||||||
|
assert d_json == json.dumps({})
|
||||||
|
tests +=1
|
||||||
|
elif id == 'inv_count_456':
|
||||||
|
assert False
|
||||||
|
|
||||||
|
assert tests==1
|
||||||
|
|
||||||
def test_exception_and_calc(inverter_data: bytes):
|
def test_exception_and_calc(inverter_data: bytes):
|
||||||
|
|
||||||
# patch table to convert temperature from °F to °C
|
# patch table to convert temperature from °F to °C
|
||||||
|
|||||||
@@ -130,6 +130,12 @@ def get_sn() -> bytes:
|
|||||||
def get_sn_int() -> int:
|
def get_sn_int() -> int:
|
||||||
return 2070233889
|
return 2070233889
|
||||||
|
|
||||||
|
def get_dcu_sn() -> bytes:
|
||||||
|
return b'\x20\x43\x65\x7b'
|
||||||
|
|
||||||
|
def get_dcu_sn_int() -> int:
|
||||||
|
return 2070233888
|
||||||
|
|
||||||
def get_inv_no() -> bytes:
|
def get_inv_no() -> bytes:
|
||||||
return b'T170000000000001'
|
return b'T170000000000001'
|
||||||
|
|
||||||
@@ -672,6 +678,65 @@ def msg_unknown_cmd_rsp(): # 0x1510
|
|||||||
msg += b'\x15'
|
msg += b'\x15'
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def dcu_dev_ind_msg(): # 0x4110
|
||||||
|
msg = b'\xa5\x3a\x01\x10\x41\x91\x01' +get_dcu_sn() +b'\x02\xc6\xde\x2d\x32'
|
||||||
|
msg += b'\x27\x00\x00\x00\x00\x00\x00\x00\x05\x3c\x78\x01\x5c\x01\x4c\x53'
|
||||||
|
msg += b'\x57\x35\x5f\x30\x31\x5f\x33\x30\x32\x36\x5f\x4e\x53\x5f\x30\x35'
|
||||||
|
msg += b'\x5f\x30\x31\x2e\x30\x30\x2e\x30\x30\x2e\x30\x30\x00\x00\x00\x00'
|
||||||
|
msg += b'\x00\x00\x00\x00\x00\x00\xd4\x27\x87\x12\xad\xc0\x31\x39\x32\x2e'
|
||||||
|
msg += b'\x31\x36\x38\x2e\x39\x2e\x31\x34\x00\x00\x00\x00\x01\x00\x01\x26'
|
||||||
|
msg += b'\x30\x0f\x00\xff\x56\x31\x2e\x31\x2e\x30\x30\x2e\x30\x42\x00\x00'
|
||||||
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xfe\xfe\x00\x00'
|
||||||
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x7a\x75\x68\x61\x75\x73\x65\x00\x00'
|
||||||
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x08\x01\x01\x01\x00\x00\x00\x00'
|
||||||
|
msg += b'\x00\x00\x00\x00\x01'
|
||||||
|
msg += correct_checksum(msg)
|
||||||
|
msg += b'\x15'
|
||||||
|
return msg
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def dcu_dev_rsp_msg(): # 0x1110
|
||||||
|
msg = b'\xa5\x0a\x00\x10\x11\x92\x01' +get_dcu_sn() +b'\x02\x01'
|
||||||
|
msg += total()
|
||||||
|
msg += hb()
|
||||||
|
msg += correct_checksum(msg)
|
||||||
|
msg += b'\x15'
|
||||||
|
return msg
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def dcu_data_ind_msg(): # 0x4210
|
||||||
|
msg = b'\xa5\x6f\x00\x10\x42\x92\x02' +get_dcu_sn() +b'\x01\x26\x30\xc7\xde'
|
||||||
|
msg += b'\x2d\x32\x28\x00\x00\x00\x84\x17\x79\x35\x01\x00\x4c\x12\x00\x00'
|
||||||
|
msg += b'\x34\x31\x30\x31\x32\x34\x30\x37\x30\x31\x34\x39\x30\x33\x31\x34'
|
||||||
|
msg += b'\x0d\x3a\x00\x00\x0d\x2c\x00\x00\x00\x00\x08\x20\x00\x00\x00\x00'
|
||||||
|
msg += b'\x14\x0e\xff\xfe\x03\xe8\x0c\x89\x0c\x89\x0c\x89\x0c\x8a\x0c\x89'
|
||||||
|
msg += b'\x0c\x89\x0c\x8a\x0c\x89\x0c\x89\x0c\x8a\x0c\x8a\x0c\x89\x0c\x89'
|
||||||
|
msg += b'\x0c\x89\x0c\x89\x0c\x88\x00\x0f\x00\x0f\x00\x0f\x00\x0e\x00\x00'
|
||||||
|
msg += b'\x00\x00\x00\x0f\x00\x00\x02\x05\x02\x01'
|
||||||
|
msg += correct_checksum(msg)
|
||||||
|
msg += b'\x15'
|
||||||
|
return msg
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def dcu_data_rsp_msg(): # 0x1210
|
||||||
|
msg = b'\xa5\x0a\x00\x10\x12\x93\x02' +get_dcu_sn() +b'\x01\x01'
|
||||||
|
msg += total()
|
||||||
|
msg += hb()
|
||||||
|
msg += correct_checksum(msg)
|
||||||
|
msg += b'\x15'
|
||||||
|
return msg
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def config_tsun_allow_all():
|
def config_tsun_allow_all():
|
||||||
Config.act_config = {'solarman':{'enabled': True}, 'inverters':{'allow_all':True}}
|
Config.act_config = {'solarman':{'enabled': True}, 'inverters':{'allow_all':True}}
|
||||||
@@ -682,7 +747,11 @@ def config_no_tsun_inv1():
|
|||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def config_tsun_inv1():
|
def config_tsun_inv1():
|
||||||
Config.act_config = {'solarman':{'enabled': True},'inverters':{'Y170000000000001':{'monitor_sn': 2070233889, 'node_id':'inv1', 'modbus_polling': True, 'suggested_area':'roof', 'sensor_list': 688}}}
|
Config.act_config = {'solarman':{'enabled': True},'inverters':{'Y170000000000001':{'monitor_sn': 2070233889, 'node_id':'inv1', 'modbus_polling': True, 'suggested_area':'roof', 'sensor_list': 0}}}
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def config_tsun_dcu1():
|
||||||
|
Config.act_config = {'solarman':{'enabled': True},'batteries':{'4100000000000001':{'monitor_sn': 2070233888, 'node_id':'inv1', 'modbus_polling': True, 'suggested_area':'roof', 'sensor_list': 0}}}
|
||||||
|
|
||||||
def test_read_message(device_ind_msg):
|
def test_read_message(device_ind_msg):
|
||||||
Config.act_config = {'solarman':{'enabled': True}}
|
Config.act_config = {'solarman':{'enabled': True}}
|
||||||
@@ -963,6 +1032,34 @@ def test_read_two_messages3(config_tsun_allow_all, device_ind_msg2, device_rsp_m
|
|||||||
assert m.ifc.tx_fifo.get()==b''
|
assert m.ifc.tx_fifo.get()==b''
|
||||||
m.close()
|
m.close()
|
||||||
|
|
||||||
|
def test_read_two_messages4(config_tsun_dcu1, dcu_dev_ind_msg, dcu_dev_rsp_msg, dcu_data_ind_msg, dcu_data_rsp_msg):
|
||||||
|
_ = config_tsun_dcu1
|
||||||
|
m = MemoryStream(dcu_dev_ind_msg, (0,))
|
||||||
|
m.append_msg(dcu_data_ind_msg)
|
||||||
|
assert 0 == m.sensor_list
|
||||||
|
m._init_new_client_conn()
|
||||||
|
m.read() # read complete msg, and dispatch msg
|
||||||
|
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
||||||
|
assert not m.header_valid # must be invalid, since msg was handled and buffer flushed
|
||||||
|
assert m.msg_count == 2
|
||||||
|
assert m.header_len==11
|
||||||
|
assert m.snr == 2070233888
|
||||||
|
assert m.unique_id == '2070233888'
|
||||||
|
assert m.msg_recvd[0]['control']==0x4110
|
||||||
|
assert m.msg_recvd[0]['seq']=='01:92'
|
||||||
|
assert m.msg_recvd[0]['data_len']==314
|
||||||
|
assert m.msg_recvd[1]['control']==0x4210
|
||||||
|
assert m.msg_recvd[1]['seq']=='02:93'
|
||||||
|
assert m.msg_recvd[1]['data_len']==111
|
||||||
|
assert '3026' == m.db.get_db_value(Register.SENSOR_LIST, None)
|
||||||
|
assert 0x3026 == m.sensor_list
|
||||||
|
assert m.ifc.fwd_fifo.get()==dcu_dev_ind_msg+dcu_data_ind_msg
|
||||||
|
assert m.ifc.tx_fifo.get()==dcu_dev_rsp_msg+dcu_data_rsp_msg
|
||||||
|
|
||||||
|
m._init_new_client_conn()
|
||||||
|
assert m.ifc.tx_fifo.get()==b''
|
||||||
|
m.close()
|
||||||
|
|
||||||
def test_unkown_frame_code(config_tsun_inv1, inverter_ind_msg_81, inverter_rsp_msg_81):
|
def test_unkown_frame_code(config_tsun_inv1, inverter_ind_msg_81, inverter_rsp_msg_81):
|
||||||
_ = config_tsun_inv1
|
_ = config_tsun_inv1
|
||||||
m = MemoryStream(inverter_ind_msg_81, (0,))
|
m = MemoryStream(inverter_ind_msg_81, (0,))
|
||||||
|
|||||||
@@ -8,19 +8,65 @@ JINJA = jinja2
|
|||||||
IMAGE = tsun-gen3-addon
|
IMAGE = tsun-gen3-addon
|
||||||
|
|
||||||
|
|
||||||
# Folders
|
# Source folders for building the local add-on
|
||||||
SRC=../app
|
SRC=../app
|
||||||
SRC_PROXY=$(SRC)/src
|
SRC_PROXY=$(SRC)/src
|
||||||
CNF_PROXY=$(SRC)/config
|
CNF_PROXY=$(SRC)/config
|
||||||
|
|
||||||
|
# Target folders for building the local add-on and the docker container
|
||||||
ADDON_PATH = ha_addon
|
ADDON_PATH = ha_addon
|
||||||
DST=$(ADDON_PATH)/rootfs
|
DST=$(ADDON_PATH)/rootfs
|
||||||
DST_PROXY=$(DST)/home/proxy
|
DST_PROXY=$(DST)/home/proxy
|
||||||
|
|
||||||
|
# base director of the add-on repro for installing the add-on git repros
|
||||||
INST_BASE=../../ha-addons/ha-addons
|
INST_BASE=../../ha-addons/ha-addons
|
||||||
|
|
||||||
|
# Template folder for build the config.yaml variants
|
||||||
TEMPL=templates
|
TEMPL=templates
|
||||||
|
|
||||||
|
# help variable STAGE determine the target to build
|
||||||
|
dev: STAGE=dev
|
||||||
|
debug : STAGE=debug
|
||||||
|
rc : STAGE=rc
|
||||||
|
rel : STAGE=rel
|
||||||
|
|
||||||
|
|
||||||
|
export BUILD_DATE := ${shell date -Iminutes}
|
||||||
|
BUILD_ID := ${shell date +'%y%m%d%H%M'}
|
||||||
|
VERSION := $(shell cat $(SRC)/.version)
|
||||||
|
export MAJOR := $(shell echo $(VERSION) | cut -f1 -d.)
|
||||||
|
|
||||||
|
PUBLIC_URL := $(shell echo $(PUBLIC_CONTAINER_REGISTRY) | cut -f1 -d/)
|
||||||
|
PUBLIC_USER :=$(shell echo $(PUBLIC_CONTAINER_REGISTRY) | cut -f2 -d/)
|
||||||
|
|
||||||
|
|
||||||
|
dev debug: local_add_on
|
||||||
|
@echo version: $(VERSION) build-date: $(BUILD_DATE) image: $(PRIVAT_CONTAINER_REGISTRY)$(IMAGE)
|
||||||
|
export VERSION=$(VERSION)-$@-$(BUILD_ID) && \
|
||||||
|
export IMAGE=$(PRIVAT_CONTAINER_REGISTRY)$(IMAGE) && \
|
||||||
|
docker buildx bake -f docker-bake.hcl $@
|
||||||
|
|
||||||
|
rc rel: local_add_on
|
||||||
|
@echo version: $(VERSION) build-date: $(BUILD_DATE) image: $(PUBLIC_CONTAINER_REGISTRY)$(IMAGE)
|
||||||
|
@echo login at $(PUBLIC_URL) as $(PUBLIC_USER)
|
||||||
|
@DO_LOGIN="$(shell echo $(PUBLIC_CR_KEY) | docker login $(PUBLIC_URL) -u $(PUBLIC_USER) --password-stdin)"
|
||||||
|
export VERSION=$(VERSION)-$@ && \
|
||||||
|
export IMAGE=$(PUBLIC_CONTAINER_REGISTRY)$(IMAGE) && \
|
||||||
|
docker buildx bake -f docker-bake.hcl $@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -r -f $(DST_PROXY)
|
||||||
|
rm -f $(DST)/requirements.txt
|
||||||
|
rm -f $(ADDON_PATH)/config.yaml
|
||||||
|
rm -f $(TEMPL)/.data.json
|
||||||
|
docker logout ghcr.io
|
||||||
|
|
||||||
|
#############
|
||||||
|
# Build the local add-on with a rootfs and config.yaml
|
||||||
|
# The rootfs is needed to build the add-on Docker container
|
||||||
|
#
|
||||||
|
local_add_on: rootfs $(ADDON_PATH)/config.yaml
|
||||||
|
|
||||||
# collect source files
|
# collect source files
|
||||||
SRC_FILES := $(wildcard $(SRC_PROXY)/*.py)\
|
SRC_FILES := $(wildcard $(SRC_PROXY)/*.py)\
|
||||||
$(wildcard $(SRC_PROXY)/*.ini)\
|
$(wildcard $(SRC_PROXY)/*.ini)\
|
||||||
@@ -34,50 +80,8 @@ CNF_FILES := $(wildcard $(CNF_PROXY)/*.toml)
|
|||||||
TARGET_FILES = $(SRC_FILES:$(SRC_PROXY)/%=$(DST_PROXY)/%)
|
TARGET_FILES = $(SRC_FILES:$(SRC_PROXY)/%=$(DST_PROXY)/%)
|
||||||
CONFIG_FILES = $(CNF_FILES:$(CNF_PROXY)/%=$(DST_PROXY)/%)
|
CONFIG_FILES = $(CNF_FILES:$(CNF_PROXY)/%=$(DST_PROXY)/%)
|
||||||
|
|
||||||
export BUILD_DATE := ${shell date -Iminutes}
|
|
||||||
VERSION := $(shell cat $(SRC)/.version)
|
|
||||||
export MAJOR := $(shell echo $(VERSION) | cut -f1 -d.)
|
|
||||||
|
|
||||||
PUBLIC_URL := $(shell echo $(PUBLIC_CONTAINER_REGISTRY) | cut -f1 -d/)
|
|
||||||
PUBLIC_USER :=$(shell echo $(PUBLIC_CONTAINER_REGISTRY) | cut -f2 -d/)
|
|
||||||
|
|
||||||
|
|
||||||
dev debug: build
|
|
||||||
@echo version: $(VERSION) build-date: $(BUILD_DATE) image: $(PRIVAT_CONTAINER_REGISTRY)$(IMAGE)
|
|
||||||
export VERSION=$(VERSION)-$@ && \
|
|
||||||
export IMAGE=$(PRIVAT_CONTAINER_REGISTRY)$(IMAGE) && \
|
|
||||||
docker buildx bake -f docker-bake.hcl $@
|
|
||||||
|
|
||||||
rc rel: build
|
|
||||||
@echo version: $(VERSION) build-date: $(BUILD_DATE) image: $(PUBLIC_CONTAINER_REGISTRY)$(IMAGE)
|
|
||||||
@echo login at $(PUBLIC_URL) as $(PUBLIC_USER)
|
|
||||||
@DO_LOGIN="$(shell echo $(PUBLIC_CR_KEY) | docker login $(PUBLIC_URL) -u $(PUBLIC_USER) --password-stdin)"
|
|
||||||
export VERSION=$(VERSION)-$@ && \
|
|
||||||
export IMAGE=$(PUBLIC_CONTAINER_REGISTRY)$(IMAGE) && \
|
|
||||||
docker buildx bake -f docker-bake.hcl $@
|
|
||||||
|
|
||||||
|
|
||||||
build: rootfs $(ADDON_PATH)/config.yaml repro
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -r -f $(DST_PROXY)
|
|
||||||
rm -f $(DST)/requirements.txt
|
|
||||||
rm -f $(ADDON_PATH)/config.yaml
|
|
||||||
rm -f $(TEMPL)/.data.json
|
|
||||||
docker logout ghcr.io
|
|
||||||
|
|
||||||
#
|
|
||||||
# Build rootfs and config.yaml as local add-on
|
|
||||||
# The rootfs is needed to build the add-on Dockercontainers
|
|
||||||
#
|
|
||||||
|
|
||||||
rootfs: $(TARGET_FILES) $(CONFIG_FILES) $(DST)/requirements.txt
|
rootfs: $(TARGET_FILES) $(CONFIG_FILES) $(DST)/requirements.txt
|
||||||
|
|
||||||
STAGE=dev
|
|
||||||
debug : STAGE=debug
|
|
||||||
rc : STAGE=rc
|
|
||||||
rel : STAGE=rel
|
|
||||||
|
|
||||||
$(CONFIG_FILES): $(DST_PROXY)/% : $(CNF_PROXY)/%
|
$(CONFIG_FILES): $(DST_PROXY)/% : $(CNF_PROXY)/%
|
||||||
@echo Copy $< to $@
|
@echo Copy $< to $@
|
||||||
@mkdir -p $(@D)
|
@mkdir -p $(@D)
|
||||||
@@ -93,21 +97,22 @@ $(DST)/requirements.txt : $(SRC)/requirements.txt
|
|||||||
@cp $< $@
|
@cp $< $@
|
||||||
|
|
||||||
$(ADDON_PATH)/%.yaml: $(TEMPL)/%.jinja $(TEMPL)/.data.json
|
$(ADDON_PATH)/%.yaml: $(TEMPL)/%.jinja $(TEMPL)/.data.json
|
||||||
$(JINJA) --strict -D AppVersion=$(VERSION) --format=json $^ -o $@
|
$(JINJA) --strict -D AppVersion=$(VERSION) -D BuildID=$(BUILD_ID) --format=json $^ -o $@
|
||||||
|
|
||||||
|
# build a common data.json file from STAGE depending source files
|
||||||
|
# don't touch the destination if the checksum of src and dst is equal
|
||||||
$(TEMPL)/.data.json: FORCE
|
$(TEMPL)/.data.json: FORCE
|
||||||
rsync --checksum $(TEMPL)/$(STAGE)_data.json $@
|
rsync --checksum $(TEMPL)/$(STAGE)_data.json $@
|
||||||
|
|
||||||
FORCE : ;
|
FORCE : ;
|
||||||
|
|
||||||
|
|
||||||
#
|
#############
|
||||||
# Build repository for Home Assistant Add-On
|
# Build repository for Home Assistant Add-Onx
|
||||||
#
|
#
|
||||||
|
|
||||||
INST=$(INST_BASE)/ha_addon_dev
|
|
||||||
repro_files = DOCS.md icon.png logo.png translations/de.yaml translations/en.yaml rootfs/run.sh
|
repro_files = DOCS.md icon.png logo.png translations/de.yaml translations/en.yaml rootfs/run.sh
|
||||||
repro_root = CHANGELOG.md
|
repro_root = CHANGELOG.md LICENSE.md
|
||||||
repro_templates = config.yaml
|
repro_templates = config.yaml
|
||||||
repro_subdirs = translations rootfs
|
repro_subdirs = translations rootfs
|
||||||
repro_vers = debug dev rc rel
|
repro_vers = debug dev rc rel
|
||||||
@@ -117,16 +122,42 @@ repro_root_files := $(foreach dir,$(repro_vers), $(foreach file,$(repro_root),$(
|
|||||||
repro_all_templates := $(foreach dir,$(repro_vers), $(foreach file,$(repro_templates),$(INST_BASE)/ha_addon_$(dir)/$(file)))
|
repro_all_templates := $(foreach dir,$(repro_vers), $(foreach file,$(repro_templates),$(INST_BASE)/ha_addon_$(dir)/$(file)))
|
||||||
repro_all_subdirs := $(foreach dir,$(repro_vers), $(foreach file,$(repro_subdirs),$(INST_BASE)/ha_addon_$(dir)/$(file)))
|
repro_all_subdirs := $(foreach dir,$(repro_vers), $(foreach file,$(repro_subdirs),$(INST_BASE)/ha_addon_$(dir)/$(file)))
|
||||||
|
|
||||||
repro: $(repro_all_subdirs) $(repro_all_templates) $(repro_all_files) $(repro_root_files)
|
debug: $(foreach file,$(repro_subdirs),$(INST_BASE)/ha_addon_debug/$(file)) \
|
||||||
|
$(foreach file,$(repro_templates),$(INST_BASE)/ha_addon_debug/$(file)) \
|
||||||
|
$(foreach file,$(repro_files),$(INST_BASE)/ha_addon_debug/$(file)) \
|
||||||
|
$(foreach file,$(repro_root),$(INST_BASE)/ha_addon_debug/$(file))
|
||||||
|
|
||||||
|
dev: $(foreach file,$(repro_subdirs),$(INST_BASE)/ha_addon_dev/$(file)) \
|
||||||
|
$(foreach file,$(repro_templates),$(INST_BASE)/ha_addon_dev/$(file)) \
|
||||||
|
$(foreach file,$(repro_files),$(INST_BASE)/ha_addon_dev/$(file)) \
|
||||||
|
$(foreach file,$(repro_root),$(INST_BASE)/ha_addon_dev/$(file))
|
||||||
|
|
||||||
|
rc: $(foreach file,$(repro_subdirs),$(INST_BASE)/ha_addon_rc/$(file)) \
|
||||||
|
$(foreach file,$(repro_templates),$(INST_BASE)/ha_addon_rc/$(file)) \
|
||||||
|
$(foreach file,$(repro_files),$(INST_BASE)/ha_addon_rc/$(file)) \
|
||||||
|
$(foreach file,$(repro_root),$(INST_BASE)/ha_addon_rc/$(file))
|
||||||
|
|
||||||
|
rel: $(foreach file,$(repro_subdirs),$(INST_BASE)/ha_addon_rel/$(file)) \
|
||||||
|
$(foreach file,$(repro_templates),$(INST_BASE)/ha_addon_rel/$(file)) \
|
||||||
|
$(foreach file,$(repro_files),$(INST_BASE)/ha_addon_rel/$(file)) \
|
||||||
|
$(foreach file,$(repro_root),$(INST_BASE)/ha_addon_rel/$(file))
|
||||||
|
|
||||||
$(repro_all_subdirs) :
|
$(repro_all_subdirs) :
|
||||||
mkdir -p $@
|
mkdir -p $@
|
||||||
|
|
||||||
$(repro_all_templates) : $(INST_BASE)/ha_addon_%/config.yaml: $(TEMPL)/config.jinja $(TEMPL)/%_data.json $(SRC)/.version
|
$(repro_all_templates) : $(INST_BASE)/ha_addon_%/config.yaml: $(TEMPL)/config.jinja $(TEMPL)/%_data.json $(SRC)/.version FORCE
|
||||||
$(JINJA) --strict -D AppVersion=$(VERSION)-$* $< $(filter %.json,$^) -o $@
|
$(JINJA) --strict -D AppVersion=$(VERSION)-$* -D BuildID=$(BUILD_ID) $< $(filter %.json,$^) -o $@
|
||||||
|
|
||||||
$(repro_root_files) : %/CHANGELOG.md : ../CHANGELOG.md
|
|
||||||
|
$(filter $(INST_BASE)/ha_addon_debug/%,$(repro_root_files)) : $(INST_BASE)/ha_addon_debug/% : ../%
|
||||||
cp $< $@
|
cp $< $@
|
||||||
|
$(filter $(INST_BASE)/ha_addon_dev/%,$(repro_root_files)) : $(INST_BASE)/ha_addon_dev/% : ../%
|
||||||
|
cp $< $@
|
||||||
|
$(filter $(INST_BASE)/ha_addon_rc/%,$(repro_root_files)) : $(INST_BASE)/ha_addon_rc/% : ../%
|
||||||
|
cp $< $@
|
||||||
|
$(filter $(INST_BASE)/ha_addon_rel/%,$(repro_root_files)) : $(INST_BASE)/ha_addon_rel/% : ../%
|
||||||
|
cp $< $@
|
||||||
|
|
||||||
|
|
||||||
$(filter $(INST_BASE)/ha_addon_debug/%,$(repro_all_files)) : $(INST_BASE)/ha_addon_debug/% : ha_addon/%
|
$(filter $(INST_BASE)/ha_addon_debug/%,$(repro_all_files)) : $(INST_BASE)/ha_addon_debug/% : ha_addon/%
|
||||||
cp $< $@
|
cp $< $@
|
||||||
@@ -136,5 +167,3 @@ $(filter $(INST_BASE)/ha_addon_rc/%,$(repro_all_files)) : $(INST_BASE)/ha_addon_
|
|||||||
cp $< $@
|
cp $< $@
|
||||||
$(filter $(INST_BASE)/ha_addon_rel/%,$(repro_all_files)) : $(INST_BASE)/ha_addon_rel/% : ha_addon/%
|
$(filter $(INST_BASE)/ha_addon_rel/%,$(repro_all_files)) : $(INST_BASE)/ha_addon_rel/% : ha_addon/%
|
||||||
cp $< $@
|
cp $< $@
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -74,12 +74,12 @@ target "_prod" {
|
|||||||
}
|
}
|
||||||
target "debug" {
|
target "debug" {
|
||||||
inherits = ["_common", "_debug"]
|
inherits = ["_common", "_debug"]
|
||||||
tags = ["${IMAGE}:debug"]
|
tags = ["${IMAGE}:debug", "${IMAGE}:${VERSION}"]
|
||||||
}
|
}
|
||||||
|
|
||||||
target "dev" {
|
target "dev" {
|
||||||
inherits = ["_common"]
|
inherits = ["_common"]
|
||||||
tags = ["${IMAGE}:dev"]
|
tags = ["${IMAGE}:dev", "${IMAGE}:${VERSION}"]
|
||||||
}
|
}
|
||||||
|
|
||||||
target "preview" {
|
target "preview" {
|
||||||
|
|||||||
@@ -68,8 +68,8 @@ Example add-on configuration for GEN3PLUS inverters:
|
|||||||
inverters:
|
inverters:
|
||||||
- serial: Y17000000000000
|
- serial: Y17000000000000
|
||||||
monitor_sn: 2000000000
|
monitor_sn: 2000000000
|
||||||
node_id: PV-Garage
|
node_id: inv_1
|
||||||
suggested_area: Garage
|
suggested_area: Roof
|
||||||
modbus_polling: true
|
modbus_polling: true
|
||||||
client_mode.host: 192.168.x.x
|
client_mode.host: 192.168.x.x
|
||||||
client_mode.port: 8899
|
client_mode.port: 8899
|
||||||
@@ -84,6 +84,21 @@ inverters:
|
|||||||
pv4.type: SF-M18/144550
|
pv4.type: SF-M18/144550
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Example add-on configuration for GEN3PLUS energie storages:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
batteries:
|
||||||
|
- serial: 4100000000000000
|
||||||
|
monitor_sn: 2300000000
|
||||||
|
node_id: bat_1
|
||||||
|
suggested_area: Garage
|
||||||
|
modbus_polling: false
|
||||||
|
pv1.manufacturer: Shinefar
|
||||||
|
pv1.type: SF-M18/144550
|
||||||
|
pv2.manufacturer: Shinefar
|
||||||
|
pv2.type: SF-M18/144550
|
||||||
|
```
|
||||||
|
|
||||||
**Note**: _This is just an example, you need to replace the values with your own!_
|
**Note**: _This is just an example, you need to replace the values with your own!_
|
||||||
|
|
||||||
more information about the configuration can be found in the [configuration details page][configdetails].
|
more information about the configuration can be found in the [configuration details page][configdetails].
|
||||||
|
|||||||
@@ -11,7 +11,20 @@ configuration:
|
|||||||
Konfigurationsblock gesetzt werden.
|
Konfigurationsblock gesetzt werden.
|
||||||
|
|
||||||
Die Seriennummer der GEN3 Wechselrichter beginnen mit `R17` und die der GEN3PLUS
|
Die Seriennummer der GEN3 Wechselrichter beginnen mit `R17` und die der GEN3PLUS
|
||||||
Wechselrichter mir `Y17`oder `47`!
|
Wechselrichter mit `Y17`oder `Y47`!
|
||||||
|
|
||||||
|
Siehe Beispielkonfiguration im Dokumentations-Tab
|
||||||
|
batteries:
|
||||||
|
name: Batterien
|
||||||
|
description: >+
|
||||||
|
Für jeden Energiespeicher muss die Seriennummer des Speichers einer MQTT
|
||||||
|
Definition zugeordnet werden. Dazu wird der entsprechende Konfigurationsblock mit der
|
||||||
|
16-stellige Seriennummer gestartet, so dass alle nachfolgenden Parameter diesem
|
||||||
|
Speicher zugeordnet sind.
|
||||||
|
Weitere speicherspezifische Parameter (z.B. Polling Mode) können im
|
||||||
|
Konfigurationsblock gesetzt werden.
|
||||||
|
|
||||||
|
Die Seriennummer der GEN3PLUS Batteriespeicher beginnen mit `410`!
|
||||||
|
|
||||||
Siehe Beispielkonfiguration im Dokumentations-Tab
|
Siehe Beispielkonfiguration im Dokumentations-Tab
|
||||||
|
|
||||||
@@ -25,14 +38,14 @@ configuration:
|
|||||||
ein => normaler Proxy-Betrieb.
|
ein => normaler Proxy-Betrieb.
|
||||||
aus => Der Wechselrichter wird vom Internet isoliert.
|
aus => Der Wechselrichter wird vom Internet isoliert.
|
||||||
solarman.enabled:
|
solarman.enabled:
|
||||||
name: Verbindung zur Solarman Cloud - nur für GEN3PLUS Wechselrichter
|
name: Verbindung zur Solarman/TSUN Cloud - nur für GEN3PLUS Wechselrichter
|
||||||
description: >+
|
description: >+
|
||||||
Schaltet die Verbindung zur Solarman Cloud ein/aus.
|
Schaltet die Verbindung zur Solarman oder TSUN Cloud ein/aus.
|
||||||
Diese Verbindung ist erforderlich, wenn Sie Daten an die Solarman Cloud senden möchten,
|
Diese Verbindung ist erforderlich, wenn Sie Daten an die Cloud senden möchten,
|
||||||
z.B. um die Solarman Apps zu nutzen oder Firmware-Updates zu erhalten.
|
z.B. um die Solarman App oder TSUN Smart App zu nutzen oder Firmware-Updates zu erhalten.
|
||||||
|
|
||||||
ein => normaler Proxy-Betrieb.
|
ein => normaler Proxy-Betrieb.
|
||||||
aus => Der Wechselrichter wird vom Internet isoliert.
|
aus => Die GEN3PLUS Geräte werden vom Internet isoliert.
|
||||||
inverters.allow_all:
|
inverters.allow_all:
|
||||||
name: Erlaube Verbindungen von sämtlichen Wechselrichtern
|
name: Erlaube Verbindungen von sämtlichen Wechselrichtern
|
||||||
description: >-
|
description: >-
|
||||||
|
|||||||
@@ -7,13 +7,27 @@ configuration:
|
|||||||
definition. To do this, the corresponding configuration block is started with
|
definition. To do this, the corresponding configuration block is started with
|
||||||
16-digit serial number so that all subsequent parameters are assigned
|
16-digit serial number so that all subsequent parameters are assigned
|
||||||
to this inverter. Further inverter-specific parameters (e.g. polling mode) can be set
|
to this inverter. Further inverter-specific parameters (e.g. polling mode) can be set
|
||||||
in the configuration block
|
in the configuration block.
|
||||||
|
|
||||||
The serial numbers of all GEN3 inverters start with `R17` and that of the GEN3PLUS
|
The serial numbers of all GEN3 inverters start with `R17` and that of the GEN3PLUS
|
||||||
inverters with ‘Y17’ or ‘47’!
|
inverters with ‘Y17’ or ‘Y47’!
|
||||||
|
|
||||||
For reference see example configuration in Documentation Tab
|
For reference see example configuration in Documentation Tab
|
||||||
|
|
||||||
|
batteries:
|
||||||
|
name: Energy Storages
|
||||||
|
description: >+
|
||||||
|
For each energy storage device, the serial number of the storage device must be
|
||||||
|
assigned to an MQTT definition. To do this, the corresponding configuration block
|
||||||
|
is started with the 16-digit serial number so that all subsequent parameters are
|
||||||
|
assigned to this energy storage. Further inverter-specific parameters (e.g. polling
|
||||||
|
mode) can be set in the configuration block.
|
||||||
|
|
||||||
|
The serial numbers of all GEN3PLUS energy storages start with ‘410’!
|
||||||
|
|
||||||
|
For reference see example configuration in Documentation Tab
|
||||||
|
|
||||||
|
|
||||||
tsun.enabled:
|
tsun.enabled:
|
||||||
name: Connection to TSUN Cloud - for GEN3 inverter only
|
name: Connection to TSUN Cloud - for GEN3 inverter only
|
||||||
description: >+
|
description: >+
|
||||||
@@ -24,14 +38,14 @@ configuration:
|
|||||||
on => normal proxy operation.
|
on => normal proxy operation.
|
||||||
off => The Inverter become isolated from Internet.
|
off => The Inverter become isolated from Internet.
|
||||||
solarman.enabled:
|
solarman.enabled:
|
||||||
name: Connection to Solarman Cloud - for GEN3PLUS inverter only
|
name: Connection to Solarman/TSUN Cloud - for GEN3PLUS inverter only
|
||||||
description: >+
|
description: >+
|
||||||
switch on/off connection to the Solarman cloud.
|
switch on/off connection to the Solarman or TSUN cloud.
|
||||||
This connection is only required if you want send data to the Solarman cloud
|
This connection is only required if you want send data to the cloud
|
||||||
eg. to use the Solarman APPs or receive firmware updates.
|
eg. to use the Solarman APP, the TSUN Smart APP or receive firmware updates.
|
||||||
|
|
||||||
on => normal proxy operation.
|
on => normal proxy operation.
|
||||||
off => The Inverter become isolated from Internet
|
off => The GEN3PLUS devices become isolated from Internet
|
||||||
inverters.allow_all:
|
inverters.allow_all:
|
||||||
name: Allow all connections from all inverters
|
name: Allow all connections from all inverters
|
||||||
description: >-
|
description: >-
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
name: {{name}}
|
name: {{name}}
|
||||||
description: {{description}}
|
description: {{description}}
|
||||||
version: {% if version is defined and version|length %} {{version}} {% else %} {{AppVersion}} {% endif %}
|
version: {% if version is defined and version|length %} {{version}} {% elif BuildID is defined and BuildID|length %} {{AppVersion}}-{{BuildID}} {% else %} {{AppVersion}} {% endif %}
|
||||||
image: {{image}}
|
image: {{image}}
|
||||||
url: https://github.com/s-allius/tsun-gen3-proxy
|
url: https://github.com/s-allius/tsun-gen3-proxy
|
||||||
slug: {{slug}}
|
slug: {{slug}}
|
||||||
@@ -30,7 +30,6 @@ ports:
|
|||||||
# Definition of parameters in the configuration tab of the addon
|
# Definition of parameters in the configuration tab of the addon
|
||||||
# parameters are available within the container as /data/options.json
|
# parameters are available within the container as /data/options.json
|
||||||
# and should become picked up by the proxy - current workaround as a transfer script
|
# and should become picked up by the proxy - current workaround as a transfer script
|
||||||
# TODO: check again for multi hierarchie parameters
|
|
||||||
|
|
||||||
schema:
|
schema:
|
||||||
inverters:
|
inverters:
|
||||||
@@ -42,11 +41,6 @@ schema:
|
|||||||
client_mode.host: match(\b((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\b)?
|
client_mode.host: match(\b((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\b)?
|
||||||
client_mode.port: port?
|
client_mode.port: port?
|
||||||
client_mode.forward: bool?
|
client_mode.forward: bool?
|
||||||
#strings: # leider funktioniert es nicht die folgenden 3 parameter im schema aufzulisten. möglicherweise wird die verschachtelung nicht unterstützt.
|
|
||||||
# - string: str
|
|
||||||
# type: str
|
|
||||||
# manufacturer: str
|
|
||||||
# daher diese variante
|
|
||||||
pv1.manufacturer: str?
|
pv1.manufacturer: str?
|
||||||
pv1.type: str?
|
pv1.type: str?
|
||||||
pv2.manufacturer: str?
|
pv2.manufacturer: str?
|
||||||
@@ -62,6 +56,19 @@ schema:
|
|||||||
tsun.enabled: bool
|
tsun.enabled: bool
|
||||||
solarman.enabled: bool
|
solarman.enabled: bool
|
||||||
inverters.allow_all: bool
|
inverters.allow_all: bool
|
||||||
|
batteries:
|
||||||
|
- serial: match(^(410).{13}$)
|
||||||
|
monitor_sn: int
|
||||||
|
node_id: str
|
||||||
|
suggested_area: str
|
||||||
|
modbus_polling: bool
|
||||||
|
client_mode.host: match(\b((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\b)?
|
||||||
|
client_mode.port: port?
|
||||||
|
client_mode.forward: bool?
|
||||||
|
pv1.manufacturer: str?
|
||||||
|
pv1.type: str?
|
||||||
|
pv2.manufacturer: str?
|
||||||
|
pv2.type: str?
|
||||||
|
|
||||||
# optionale parameter
|
# optionale parameter
|
||||||
|
|
||||||
@@ -90,17 +97,21 @@ schema:
|
|||||||
# If any default value is given, the option becomes a required value.
|
# If any default value is given, the option becomes a required value.
|
||||||
options:
|
options:
|
||||||
inverters:
|
inverters:
|
||||||
- serial: R17E760702080400
|
- serial: R17E000000000000
|
||||||
node_id: PV-Garage
|
monitor_sn: 0
|
||||||
|
node_id: inv_1
|
||||||
|
suggested_area: Roof
|
||||||
|
modbus_polling: false
|
||||||
|
pv1.manufacturer: Shinefar
|
||||||
|
pv1.type: SF-M18/144550
|
||||||
|
pv2.manufacturer: Shinefar
|
||||||
|
pv2.type: SF-M18/144550
|
||||||
|
batteries:
|
||||||
|
- serial: 4100000000000000
|
||||||
|
monitor_sn: 0
|
||||||
|
node_id: bat_1
|
||||||
suggested_area: Garage
|
suggested_area: Garage
|
||||||
modbus_polling: false
|
modbus_polling: false
|
||||||
# strings:
|
|
||||||
# - string: PV1
|
|
||||||
# type: SF-M18/144550
|
|
||||||
# manufacturer: Shinefar
|
|
||||||
# - string: PV2
|
|
||||||
# type: SF-M18/144550
|
|
||||||
# manufacturer: Shinefar
|
|
||||||
pv1.manufacturer: Shinefar
|
pv1.manufacturer: Shinefar
|
||||||
pv1.type: SF-M18/144550
|
pv1.type: SF-M18/144550
|
||||||
pv2.manufacturer: Shinefar
|
pv2.manufacturer: Shinefar
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
{
|
{
|
||||||
"name": "TSUN-Proxy (Debug)",
|
"name": "TSUN-Proxy (Debug)",
|
||||||
"description": "MQTT Proxy for TSUN Photovoltaic Inverters with Debug Logging",
|
"description": "MQTT Proxy for TSUN Photovoltaic Inverters with Debug Logging",
|
||||||
"version": "debug",
|
|
||||||
"image": "docker.io/sallius/tsun-gen3-addon",
|
"image": "docker.io/sallius/tsun-gen3-addon",
|
||||||
"slug": "tsun-proxy-debug",
|
"slug": "tsun-proxy-debug",
|
||||||
"advanced": true,
|
"advanced": true,
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
{
|
{
|
||||||
"name": "TSUN-Proxy (Dev)",
|
"name": "TSUN-Proxy (Dev)",
|
||||||
"description": "MQTT Proxy for TSUN Photovoltaic Inverters",
|
"description": "MQTT Proxy for TSUN Photovoltaic Inverters",
|
||||||
"version": "dev",
|
|
||||||
"image": "docker.io/sallius/tsun-gen3-addon",
|
"image": "docker.io/sallius/tsun-gen3-addon",
|
||||||
"slug": "tsun-proxy-dev",
|
"slug": "tsun-proxy-dev",
|
||||||
"advanced": false,
|
"advanced": false,
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ sonar.sources=app/src/
|
|||||||
sonar.python.version=3.12
|
sonar.python.version=3.12
|
||||||
sonar.tests=system_tests/,app/tests/
|
sonar.tests=system_tests/,app/tests/
|
||||||
sonar.exclusions=**/.vscode/**/*
|
sonar.exclusions=**/.vscode/**/*
|
||||||
|
|
||||||
|
# disable code dupication check for config grammar
|
||||||
|
sonar.cpd.exclusions=app/src/cnf/config.py
|
||||||
|
|
||||||
# Name your criteria
|
# Name your criteria
|
||||||
sonar.issue.ignore.multicriteria=e1,e2
|
sonar.issue.ignore.multicriteria=e1,e2
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,12 @@ SOLARMAN_SNR = os.getenv('SOLARMAN_SNR', '00000080')
|
|||||||
def get_sn() -> bytes:
|
def get_sn() -> bytes:
|
||||||
return bytes.fromhex(SOLARMAN_SNR)
|
return bytes.fromhex(SOLARMAN_SNR)
|
||||||
|
|
||||||
|
def get_dcu_sn() -> bytes:
|
||||||
|
return b'\x20\x43\x65\x7b'
|
||||||
|
|
||||||
|
def get_dcu_no() -> bytes:
|
||||||
|
return b'4100000000000001'
|
||||||
|
|
||||||
def get_inv_no() -> bytes:
|
def get_inv_no() -> bytes:
|
||||||
return b'T170000000000001'
|
return b'T170000000000001'
|
||||||
|
|
||||||
@@ -105,6 +111,62 @@ def MsgInvalidInfo(): # Contact Info message wrong start byte
|
|||||||
msg += b'\x15'
|
msg += b'\x15'
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def dcu_dev_ind_msg(): # 0x4110
|
||||||
|
msg = b'\xa5\x3a\x01\x10\x41\x00\x01' +get_dcu_sn() +b'\x02\xc6\xde\x2d\x32'
|
||||||
|
msg += b'\x27\x00\x00\x00\x00\x00\x00\x00\x05\x3c\x78\x01\x5c\x01\x4c\x53'
|
||||||
|
msg += b'\x57\x35\x5f\x30\x31\x5f\x33\x30\x32\x36\x5f\x4e\x53\x5f\x30\x35'
|
||||||
|
msg += b'\x5f\x30\x31\x2e\x30\x30\x2e\x30\x30\x2e\x30\x30\x00\x00\x00\x00'
|
||||||
|
msg += b'\x00\x00\x00\x00\x00\x00\xd4\x27\x87\x12\xad\xc0\x31\x39\x32\x2e'
|
||||||
|
msg += b'\x31\x36\x38\x2e\x39\x2e\x31\x34\x00\x00\x00\x00\x01\x00\x01\x26'
|
||||||
|
msg += b'\x30\x0f\x00\xff\x56\x31\x2e\x31\x2e\x30\x30\x2e\x30\x42\x00\x00'
|
||||||
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xfe\xfe\x00\x00'
|
||||||
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x7a\x75\x68\x61\x75\x73\x65\x00\x00'
|
||||||
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x08\x01\x01\x01\x00\x00\x00\x00'
|
||||||
|
msg += b'\x00\x00\x00\x00\x01'
|
||||||
|
msg += correct_checksum(msg)
|
||||||
|
msg += b'\x15'
|
||||||
|
return msg
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def dcu_dev_rsp_msg(): # 0x1110
|
||||||
|
msg = b'\xa5\x0a\x00\x10\x11\x92\x01' +get_dcu_sn() +b'\x02\x01\x4a\xf6\xa6'
|
||||||
|
msg += b'\x67\x3c\x00\x00\x00'
|
||||||
|
msg += correct_checksum(msg)
|
||||||
|
msg += b'\x15'
|
||||||
|
return msg
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def dcu_data_ind_msg(): # 0x4210
|
||||||
|
msg = b'\xa5\x6f\x00\x10\x42\x92\x02' +get_dcu_sn() +b'\x01\x26\x30\xc7\xde'
|
||||||
|
msg += b'\x2d\x32\x28\x00\x00\x00\x84\x17\x79\x35\x01\x00\x4c\x12\x00\x00'
|
||||||
|
msg += get_dcu_no()
|
||||||
|
msg += b'\x0d\x3a\x00\x00\x0d\x2c\x00\x00\x00\x00\x08\x20\x00\x00\x00\x00'
|
||||||
|
msg += b'\x14\x0e\xff\xfe\x03\xe8\x0c\x89\x0c\x89\x0c\x89\x0c\x8a\x0c\x89'
|
||||||
|
msg += b'\x0c\x89\x0c\x8a\x0c\x89\x0c\x89\x0c\x8a\x0c\x8a\x0c\x89\x0c\x89'
|
||||||
|
msg += b'\x0c\x89\x0c\x89\x0c\x88\x00\x0f\x00\x0f\x00\x0f\x00\x0e\x00\x00'
|
||||||
|
msg += b'\x00\x00\x00\x0f\x00\x00\x02\x05\x02\x01'
|
||||||
|
msg += correct_checksum(msg)
|
||||||
|
msg += b'\x15'
|
||||||
|
return msg
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def dcu_data_rsp_msg(): # 0x1210
|
||||||
|
msg = b'\xa5\x0a\x00\x10\x12\x93\x02' +get_dcu_sn() +b'\x01\x01\xd1\x96\x04'
|
||||||
|
msg += b'\x66\x3c\x00\x00\x00'
|
||||||
|
msg += correct_checksum(msg)
|
||||||
|
msg += b'\x15'
|
||||||
|
return msg
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
def ClientConnection():
|
def ClientConnection():
|
||||||
@@ -181,4 +243,24 @@ def test_inavlid_msg(ClientConnection,MsgInvalidInfo,MsgContactInfo, MsgContactR
|
|||||||
# time.sleep(2.5)
|
# time.sleep(2.5)
|
||||||
checkResponse(data, MsgContactResp)
|
checkResponse(data, MsgContactResp)
|
||||||
|
|
||||||
|
def test_dcu_dev(ClientConnection,dcu_dev_ind_msg, dcu_dev_rsp_msg):
|
||||||
|
s = ClientConnection
|
||||||
|
try:
|
||||||
|
s.sendall(dcu_dev_ind_msg)
|
||||||
|
# time.sleep(2.5)
|
||||||
|
data = s.recv(1024)
|
||||||
|
except TimeoutError:
|
||||||
|
pass
|
||||||
|
# time.sleep(2.5)
|
||||||
|
checkResponse(data, dcu_dev_rsp_msg)
|
||||||
|
|
||||||
|
def test_dcu_ind(ClientConnection,dcu_data_ind_msg, dcu_data_rsp_msg):
|
||||||
|
s = ClientConnection
|
||||||
|
try:
|
||||||
|
s.sendall(dcu_data_ind_msg)
|
||||||
|
# time.sleep(2.5)
|
||||||
|
data = s.recv(1024)
|
||||||
|
except TimeoutError:
|
||||||
|
pass
|
||||||
|
# time.sleep(2.5)
|
||||||
|
checkResponse(data, dcu_data_rsp_msg)
|
||||||
|
|||||||
Reference in New Issue
Block a user