S allius/issue128 (#130)
* set Register.NO_INPUTS fix to 4 for GEN3PLUS * don't set Register.NO_INPUTS per MODBUS * fix unit tests * register OUTPUT_COEFFICIENT at HA * update changelog * - Home Assistant: improve inverter status value texts * - GEN3: add inverter status * on closing send outstanding MQTT data to the broker * force MQTT publish on every conn open and close * reset inverter state on close - workaround which reset the inverter status to offline when the inverter has a very low output power on connection close * improve client modified - reduce the polling cadence to 30s - set controller statistics for HA * client mode set controller IP for HA
This commit is contained in:
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## [unreleased]
|
## [unreleased]
|
||||||
|
|
||||||
|
- Home Assistant: improve inverter status value texts
|
||||||
|
- GEN3: add inverter status
|
||||||
|
- fix flapping registers [#128](https://github.com/s-allius/tsun-gen3-proxy/issues/128)
|
||||||
|
- register OUTPUT_COEFFICIENT at HA
|
||||||
|
- GEN3: INVERTER_STATUS,
|
||||||
- add config option to disable the MODBUS polling [#120](https://github.com/s-allius/tsun-gen3-proxy/issues/120)
|
- add config option to disable the MODBUS polling [#120](https://github.com/s-allius/tsun-gen3-proxy/issues/120)
|
||||||
- make the maximum output coefficient configurable [#123](https://github.com/s-allius/tsun-gen3-proxy/issues/123)
|
- make the maximum output coefficient configurable [#123](https://github.com/s-allius/tsun-gen3-proxy/issues/123)
|
||||||
- cleanup shutdown
|
- cleanup shutdown
|
||||||
|
|||||||
@@ -44,13 +44,24 @@ class AsyncStream():
|
|||||||
to = self.MAX_CLOUD_IDLE_TIME
|
to = self.MAX_CLOUD_IDLE_TIME
|
||||||
return to
|
return to
|
||||||
|
|
||||||
|
async def __publish_outstanding_mqtt(self):
|
||||||
|
'''Publish all outstanding MQTT topics'''
|
||||||
|
try:
|
||||||
|
if self.unique_id:
|
||||||
|
await self.async_publ_mqtt()
|
||||||
|
await self._async_publ_mqtt_proxy_stat('proxy')
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
async def server_loop(self, addr: str) -> None:
|
async def server_loop(self, addr: str) -> None:
|
||||||
'''Loop for receiving messages from the inverter (server-side)'''
|
'''Loop for receiving messages from the inverter (server-side)'''
|
||||||
logger.info(f'[{self.node_id}:{self.conn_no}] '
|
logger.info(f'[{self.node_id}:{self.conn_no}] '
|
||||||
f'Accept connection from {addr}')
|
f'Accept connection from {addr}')
|
||||||
self.inc_counter('Inverter_Cnt')
|
self.inc_counter('Inverter_Cnt')
|
||||||
|
await self.__publish_outstanding_mqtt()
|
||||||
await self.loop()
|
await self.loop()
|
||||||
self.dec_counter('Inverter_Cnt')
|
self.dec_counter('Inverter_Cnt')
|
||||||
|
await self.__publish_outstanding_mqtt()
|
||||||
logger.info(f'[{self.node_id}:{self.conn_no}] Server loop stopped for'
|
logger.info(f'[{self.node_id}:{self.conn_no}] Server loop stopped for'
|
||||||
f' r{self.r_addr}')
|
f' r{self.r_addr}')
|
||||||
|
|
||||||
@@ -61,10 +72,6 @@ class AsyncStream():
|
|||||||
f'connection: [{self.remoteStream.node_id}:'
|
f'connection: [{self.remoteStream.node_id}:'
|
||||||
f'{self.remoteStream.conn_no}]')
|
f'{self.remoteStream.conn_no}]')
|
||||||
await self.remoteStream.disc()
|
await self.remoteStream.disc()
|
||||||
try:
|
|
||||||
await self._async_publ_mqtt_proxy_stat('proxy')
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
async def client_loop(self, addr: str) -> None:
|
async def client_loop(self, addr: str) -> None:
|
||||||
'''Loop for receiving messages from the TSUN cloud (client-side)'''
|
'''Loop for receiving messages from the TSUN cloud (client-side)'''
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ class RegisterMap:
|
|||||||
0xffffff06: Register.OTA_START_MSG,
|
0xffffff06: Register.OTA_START_MSG,
|
||||||
0xffffff07: Register.SW_EXCEPTION,
|
0xffffff07: Register.SW_EXCEPTION,
|
||||||
0xffffff08: Register.MAX_DESIGNED_POWER,
|
0xffffff08: Register.MAX_DESIGNED_POWER,
|
||||||
|
0xffffff09: Register.OUTPUT_COEFFICIENT,
|
||||||
|
0xffffff0a: Register.INVERTER_STATUS,
|
||||||
0xfffffffe: Register.TEST_REG1,
|
0xfffffffe: Register.TEST_REG1,
|
||||||
0xffffffff: Register.TEST_REG2,
|
0xffffffff: Register.TEST_REG2,
|
||||||
0x00000640: Register.OUTPUT_POWER,
|
0x00000640: Register.OUTPUT_POWER,
|
||||||
|
|||||||
@@ -9,12 +9,14 @@ if __name__ == "app.src.gen3.talent":
|
|||||||
from app.src.my_timer import Timer
|
from app.src.my_timer import Timer
|
||||||
from app.src.config import Config
|
from app.src.config import Config
|
||||||
from app.src.gen3.infos_g3 import InfosG3
|
from app.src.gen3.infos_g3 import InfosG3
|
||||||
|
from app.src.infos import Register
|
||||||
else: # pragma: no cover
|
else: # pragma: no cover
|
||||||
from messages import hex_dump_memory, Message, State
|
from messages import hex_dump_memory, Message, State
|
||||||
from modbus import Modbus
|
from modbus import Modbus
|
||||||
from my_timer import Timer
|
from my_timer import Timer
|
||||||
from config import Config
|
from config import Config
|
||||||
from gen3.infos_g3 import InfosG3
|
from gen3.infos_g3 import InfosG3
|
||||||
|
from infos import Register
|
||||||
|
|
||||||
logger = logging.getLogger('msg')
|
logger = logging.getLogger('msg')
|
||||||
|
|
||||||
@@ -78,6 +80,14 @@ class Talent(Message):
|
|||||||
'''
|
'''
|
||||||
def close(self) -> None:
|
def close(self) -> None:
|
||||||
logging.debug('Talent.close()')
|
logging.debug('Talent.close()')
|
||||||
|
if self.server_side:
|
||||||
|
# set inverter state to offline, if output power is very low
|
||||||
|
logging.debug('close power: '
|
||||||
|
f'{self.db.get_db_value(Register.OUTPUT_POWER, -1)}')
|
||||||
|
if self.db.get_db_value(Register.OUTPUT_POWER, 999) < 2:
|
||||||
|
self.db.set_db_def_value(Register.INVERTER_STATUS, 0)
|
||||||
|
self.new_data['env'] = True
|
||||||
|
|
||||||
# we have references to methods of this class in self.switch
|
# we have references to methods of this class in self.switch
|
||||||
# so we have to erase self.switch, otherwise this instance can't be
|
# so we have to erase self.switch, otherwise this instance can't be
|
||||||
# deallocated by the garbage collector ==> we get a memory leak
|
# deallocated by the garbage collector ==> we get a memory leak
|
||||||
@@ -181,7 +191,7 @@ class Talent(Message):
|
|||||||
def mb_timout_cb(self, exp_cnt):
|
def mb_timout_cb(self, exp_cnt):
|
||||||
self.mb_timer.start(self.MB_REGULAR_TIMEOUT)
|
self.mb_timer.start(self.MB_REGULAR_TIMEOUT)
|
||||||
|
|
||||||
if 0 == (exp_cnt % 30):
|
if 2 == (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.DEBUG)
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -56,8 +56,8 @@ class RegisterMap:
|
|||||||
0x42010110: {'reg': Register.PV4_DAILY_GENERATION, 'fmt': '!H', 'ratio': 0.01}, # noqa: E501
|
0x42010110: {'reg': Register.PV4_DAILY_GENERATION, 'fmt': '!H', 'ratio': 0.01}, # noqa: E501
|
||||||
0x42010112: {'reg': Register.PV4_TOTAL_GENERATION, 'fmt': '!L', 'ratio': 0.01}, # noqa: E501
|
0x42010112: {'reg': Register.PV4_TOTAL_GENERATION, 'fmt': '!L', 'ratio': 0.01}, # noqa: E501
|
||||||
0x42010126: {'reg': Register.MAX_DESIGNED_POWER, 'fmt': '!H', 'ratio': 1}, # noqa: E501
|
0x42010126: {'reg': Register.MAX_DESIGNED_POWER, 'fmt': '!H', 'ratio': 1}, # noqa: E501
|
||||||
0x42010170: {'reg': Register.NO_INPUTS, 'fmt': '!B'}, # noqa: E501
|
|
||||||
|
|
||||||
|
0xffffff01: {'reg': Register.OUTPUT_COEFFICIENT},
|
||||||
# 0x4281001c: {'reg': Register.POWER_ON_TIME, 'fmt': '<H', 'ratio': 1}, # noqa: E501
|
# 0x4281001c: {'reg': Register.POWER_ON_TIME, 'fmt': '<H', 'ratio': 1}, # noqa: E501
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -69,6 +69,7 @@ class InfosG3P(Infos):
|
|||||||
self.set_db_def_value(Register.MANUFACTURER, 'TSUN')
|
self.set_db_def_value(Register.MANUFACTURER, 'TSUN')
|
||||||
self.set_db_def_value(Register.EQUIPMENT_MODEL, 'TSOL-MSxx00')
|
self.set_db_def_value(Register.EQUIPMENT_MODEL, 'TSOL-MSxx00')
|
||||||
self.set_db_def_value(Register.CHIP_TYPE, 'IGEN TECH')
|
self.set_db_def_value(Register.CHIP_TYPE, 'IGEN TECH')
|
||||||
|
self.set_db_def_value(Register.NO_INPUTS, 4)
|
||||||
|
|
||||||
def ha_confs(self, ha_prfx: str, node_id: str, snr: str,
|
def ha_confs(self, ha_prfx: str, node_id: str, snr: str,
|
||||||
sug_area: str = '') \
|
sug_area: str = '') \
|
||||||
|
|||||||
@@ -54,7 +54,11 @@ class SolarmanV5(Message):
|
|||||||
AT_CMD = 1
|
AT_CMD = 1
|
||||||
MB_RTU_CMD = 2
|
MB_RTU_CMD = 2
|
||||||
MB_START_TIMEOUT = 40
|
MB_START_TIMEOUT = 40
|
||||||
|
'''start delay for Modbus polling in server mode'''
|
||||||
MB_REGULAR_TIMEOUT = 60
|
MB_REGULAR_TIMEOUT = 60
|
||||||
|
'''regular Modbus polling time in server mode'''
|
||||||
|
MB_CLIENT_DATA_UP = 30
|
||||||
|
'''Data up time in client mode'''
|
||||||
|
|
||||||
def __init__(self, server_side: bool):
|
def __init__(self, server_side: bool):
|
||||||
super().__init__(server_side, self.send_modbus_cb, mb_timeout=5)
|
super().__init__(server_side, self.send_modbus_cb, mb_timeout=5)
|
||||||
@@ -130,6 +134,8 @@ class SolarmanV5(Message):
|
|||||||
|
|
||||||
self.node_id = 'G3P' # will be overwritten in __set_serial_no
|
self.node_id = 'G3P' # will be overwritten in __set_serial_no
|
||||||
self.mb_timer = Timer(self.mb_timout_cb, self.node_id)
|
self.mb_timer = Timer(self.mb_timout_cb, self.node_id)
|
||||||
|
self.mb_timeout = self.MB_REGULAR_TIMEOUT
|
||||||
|
'''timer value for next Modbus polling request'''
|
||||||
self.modbus_polling = False
|
self.modbus_polling = False
|
||||||
|
|
||||||
'''
|
'''
|
||||||
@@ -146,18 +152,25 @@ class SolarmanV5(Message):
|
|||||||
self.mb_timer.close()
|
self.mb_timer.close()
|
||||||
super().close()
|
super().close()
|
||||||
|
|
||||||
async def send_start_cmd(self, snr: int):
|
async def send_start_cmd(self, snr: int, host: str):
|
||||||
self.no_forwarding = True
|
self.no_forwarding = True
|
||||||
self.snr = snr
|
self.snr = snr
|
||||||
self.__set_serial_no(snr)
|
self.__set_serial_no(snr)
|
||||||
|
self.mb_timeout = self.MB_CLIENT_DATA_UP
|
||||||
|
self.db.set_db_def_value(Register.IP_ADDRESS, host)
|
||||||
|
self.db.set_db_def_value(Register.DATA_UP_INTERVAL,
|
||||||
|
self.mb_timeout)
|
||||||
|
self.db.set_db_def_value(Register.HEARTBEAT_INTERVAL,
|
||||||
|
120) # fixme
|
||||||
|
self.new_data['controller'] = True
|
||||||
|
|
||||||
self.__send_ack_rsp(0x1710, ftype=0)
|
self.__send_ack_rsp(0x1710, ftype=0)
|
||||||
await self.async_write('Send Start Command:')
|
await self.async_write('Send Start Command:')
|
||||||
self._send_buffer = bytearray(0)
|
self._send_buffer = bytearray(0)
|
||||||
|
|
||||||
self.state = State.up
|
self.state = State.up
|
||||||
self._send_modbus_cmd(Modbus.READ_REGS, 0x2000, 96, logging.INFO)
|
self._send_modbus_cmd(Modbus.READ_REGS, 0x3000, 48, logging.DEBUG)
|
||||||
self.mb_timer.start(self.MB_START_TIMEOUT)
|
self.mb_timer.start(self.mb_timeout)
|
||||||
|
|
||||||
def new_state_up(self):
|
def new_state_up(self):
|
||||||
if self.state is not State.up:
|
if self.state is not State.up:
|
||||||
@@ -403,11 +416,11 @@ class SolarmanV5(Message):
|
|||||||
self._send_modbus_cmd(func, addr, val, log_lvl)
|
self._send_modbus_cmd(func, addr, val, log_lvl)
|
||||||
|
|
||||||
def mb_timout_cb(self, exp_cnt):
|
def mb_timout_cb(self, exp_cnt):
|
||||||
self.mb_timer.start(self.MB_REGULAR_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.DEBUG)
|
||||||
|
|
||||||
if 0 == (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.DEBUG)
|
||||||
|
|
||||||
|
|||||||
@@ -178,7 +178,21 @@ class Infos:
|
|||||||
}
|
}
|
||||||
|
|
||||||
__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
|
||||||
__status_type_val_tpl = "{%set inv_status = ['n/a', 'Online', 'Offline'] %}{{inv_status[value_json['Inverter_Status']|int(0)]|default(value_json['Inverter_Status'])}}" # noqa: E501
|
__status_type_val_tpl = "{%set inv_status = ['Off-line', 'On-grid', 'Off-grid'] %}{{inv_status[value_json['Inverter_Status']|int(0)]|default(value_json['Inverter_Status'])}}" # noqa: E501
|
||||||
|
__rated_power_val_tpl = "{% if 'Rated_Power' in value_json and value_json['Rated_Power'] != None %}{{value_json['Rated_Power']|string() +' W'}}{% else %}{{ this.state }}{% endif %}" # noqa: E501
|
||||||
|
__designed_power_val_tpl = '''
|
||||||
|
{% if 'Max_Designed_Power' in value_json and
|
||||||
|
value_json['Max_Designed_Power'] != None %}
|
||||||
|
{% if value_json['Max_Designed_Power'] | int(0xffff) < 0x8000 %}
|
||||||
|
{{value_json['Max_Designed_Power']|string() +' W'}}
|
||||||
|
{% else %}
|
||||||
|
n/a
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
{{ this.state }}
|
||||||
|
{% endif %}
|
||||||
|
'''
|
||||||
|
__output_coef_val_tpl = "{% if 'Output_Coefficient' in value_json and value_json['Output_Coefficient'] != None %}{{value_json['Output_Coefficient']|string() +' %'}}{% else %}{{ this.state }}{% endif %}" # noqa: E501
|
||||||
|
|
||||||
__info_defs = {
|
__info_defs = {
|
||||||
# collector values used for device registration:
|
# collector values used for device registration:
|
||||||
@@ -195,9 +209,9 @@ class Infos:
|
|||||||
Register.SERIAL_NUMBER: {'name': ['inverter', 'Serial_Number'], 'level': logging.DEBUG, 'unit': ''}, # noqa: E501
|
Register.SERIAL_NUMBER: {'name': ['inverter', 'Serial_Number'], 'level': logging.DEBUG, 'unit': ''}, # noqa: E501
|
||||||
Register.EQUIPMENT_MODEL: {'name': ['inverter', 'Equipment_Model'], 'level': logging.DEBUG, 'unit': ''}, # noqa: E501
|
Register.EQUIPMENT_MODEL: {'name': ['inverter', 'Equipment_Model'], 'level': logging.DEBUG, 'unit': ''}, # noqa: E501
|
||||||
Register.NO_INPUTS: {'name': ['inverter', 'No_Inputs'], 'level': logging.DEBUG, 'unit': ''}, # noqa: E501
|
Register.NO_INPUTS: {'name': ['inverter', 'No_Inputs'], 'level': logging.DEBUG, 'unit': ''}, # noqa: E501
|
||||||
Register.MAX_DESIGNED_POWER: {'name': ['inverter', 'Max_Designed_Power'], 'level': logging.INFO, 'unit': 'W', 'ha': {'dev': 'inverter', 'dev_cla': None, 'stat_cla': None, 'id': 'designed_power_', 'fmt': '| string + " W"', 'name': 'Max Designed Power', 'icon': 'mdi:lightning-bolt', 'ent_cat': 'diagnostic'}}, # noqa: E501
|
Register.MAX_DESIGNED_POWER: {'name': ['inverter', 'Max_Designed_Power'], 'level': logging.INFO, 'unit': 'W', 'ha': {'dev': 'inverter', 'dev_cla': None, 'stat_cla': None, 'id': 'designed_power_', 'val_tpl': __designed_power_val_tpl, 'name': 'Max Designed Power', 'icon': 'mdi:lightning-bolt', 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
Register.RATED_POWER: {'name': ['inverter', 'Rated_Power'], 'level': logging.DEBUG, 'unit': 'W', 'ha': {'dev': 'inverter', 'dev_cla': None, 'stat_cla': None, 'id': 'rated_power_', 'fmt': '| string + " W"', 'name': 'Rated Power', 'icon': 'mdi:lightning-bolt', 'ent_cat': 'diagnostic'}}, # noqa: E501
|
Register.RATED_POWER: {'name': ['inverter', 'Rated_Power'], 'level': logging.DEBUG, 'unit': 'W', 'ha': {'dev': 'inverter', 'dev_cla': None, 'stat_cla': None, 'id': 'rated_power_', 'val_tpl': __rated_power_val_tpl, 'name': 'Rated Power', 'icon': 'mdi:lightning-bolt', 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
Register.OUTPUT_COEFFICIENT: {'name': ['inverter', 'Output_Coefficient'], 'level': logging.INFO, 'unit': '%', 'ha': {'dev': 'inverter', 'dev_cla': None, 'stat_cla': 'measurement', 'id': 'output_coef_', 'fmt': '| int', 'name': 'Output Coefficient', 'icon': 'mdi:lightning-bolt', 'ent_cat': 'diagnostic'}}, # noqa: E501
|
Register.OUTPUT_COEFFICIENT: {'name': ['inverter', 'Output_Coefficient'], 'level': logging.INFO, 'unit': '%', 'ha': {'dev': 'inverter', 'dev_cla': None, 'stat_cla': None, 'id': 'output_coef_', 'val_tpl': __output_coef_val_tpl, 'name': 'Output Coefficient', 'icon': 'mdi:lightning-bolt', 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
Register.PV1_MANUFACTURER: {'name': ['inverter', 'PV1_Manufacturer'], 'level': logging.DEBUG, 'unit': ''}, # noqa: E501
|
Register.PV1_MANUFACTURER: {'name': ['inverter', 'PV1_Manufacturer'], 'level': logging.DEBUG, 'unit': ''}, # noqa: E501
|
||||||
Register.PV1_MODEL: {'name': ['inverter', 'PV1_Model'], 'level': logging.DEBUG, 'unit': ''}, # noqa: E501
|
Register.PV1_MODEL: {'name': ['inverter', 'PV1_Model'], 'level': logging.DEBUG, 'unit': ''}, # noqa: E501
|
||||||
Register.PV2_MANUFACTURER: {'name': ['inverter', 'PV2_Manufacturer'], 'level': logging.DEBUG, 'unit': ''}, # noqa: E501
|
Register.PV2_MANUFACTURER: {'name': ['inverter', 'PV2_Manufacturer'], 'level': logging.DEBUG, 'unit': ''}, # noqa: E501
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ class Modbus():
|
|||||||
map = {
|
map = {
|
||||||
0x2007: {'reg': Register.MAX_DESIGNED_POWER, 'fmt': '!H', 'ratio': 1}, # noqa: E501
|
0x2007: {'reg': Register.MAX_DESIGNED_POWER, 'fmt': '!H', 'ratio': 1}, # noqa: E501
|
||||||
0x202c: {'reg': Register.OUTPUT_COEFFICIENT, 'fmt': '!H', 'ratio': 100/1024}, # noqa: E501
|
0x202c: {'reg': Register.OUTPUT_COEFFICIENT, 'fmt': '!H', 'ratio': 100/1024}, # noqa: E501
|
||||||
0x203e: {'reg': Register.NO_INPUTS, 'fmt': '!H', 'ratio': 1/256}, # noqa: E501
|
|
||||||
|
|
||||||
0x3000: {'reg': Register.INVERTER_STATUS, 'fmt': '!H'}, # noqa: E501
|
0x3000: {'reg': Register.INVERTER_STATUS, 'fmt': '!H'}, # noqa: E501
|
||||||
0x3008: {'reg': Register.VERSION, 'fmt': '!H', 'eval': "f'V{(result>>12)}.{(result>>8)&0xf}.{(result>>4)&0xf}{result&0xf:1X}'"}, # noqa: E501
|
0x3008: {'reg': Register.VERSION, 'fmt': '!H', 'eval': "f'V{(result>>12)}.{(result>>8)&0xf}.{(result>>4)&0xf}{result&0xf:1X}'"}, # noqa: E501
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ class ModbusTcp():
|
|||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
async with ModbusConn(host, port) as stream:
|
async with ModbusConn(host, port) as stream:
|
||||||
await stream.send_start_cmd(snr)
|
await stream.send_start_cmd(snr, host)
|
||||||
await stream.loop()
|
await stream.loop()
|
||||||
logger.info(f'[{stream.node_id}:{stream.conn_no}] '
|
logger.info(f'[{stream.node_id}:{stream.conn_no}] '
|
||||||
f'Connection closed - Shutdown: '
|
f'Connection closed - Shutdown: '
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ def test_default_db():
|
|||||||
i = InfosG3P()
|
i = InfosG3P()
|
||||||
|
|
||||||
assert json.dumps(i.db) == json.dumps({
|
assert json.dumps(i.db) == json.dumps({
|
||||||
"inverter": {"Manufacturer": "TSUN", "Equipment_Model": "TSOL-MSxx00"},
|
"inverter": {"Manufacturer": "TSUN", "Equipment_Model": "TSOL-MSxx00", "No_Inputs": 4},
|
||||||
"collector": {"Chip_Type": "IGEN TECH"},
|
"collector": {"Chip_Type": "IGEN TECH"},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -83,7 +83,7 @@ def test_parse_4210(InverterData: bytes):
|
|||||||
|
|
||||||
assert json.dumps(i.db) == json.dumps({
|
assert json.dumps(i.db) == json.dumps({
|
||||||
"controller": {"Power_On_Time": 2051},
|
"controller": {"Power_On_Time": 2051},
|
||||||
"inverter": {"Serial_Number": "Y17E00000000000E", "Version": "V4.0.10", "Rated_Power": 600, "Max_Designed_Power": 2000, "No_Inputs": 4},
|
"inverter": {"Serial_Number": "Y17E00000000000E", "Version": "V4.0.10", "Rated_Power": 600, "Max_Designed_Power": 2000},
|
||||||
"env": {"Inverter_Status": 1, "Inverter_Temp": 14},
|
"env": {"Inverter_Status": 1, "Inverter_Temp": 14},
|
||||||
"grid": {"Voltage": 224.8, "Current": 0.73, "Frequency": 50.05, "Output_Power": 165.8},
|
"grid": {"Voltage": 224.8, "Current": 0.73, "Frequency": 50.05, "Output_Power": 165.8},
|
||||||
"input": {"pv1": {"Voltage": 35.3, "Current": 1.68, "Power": 59.6, "Daily_Generation": 0.04, "Total_Generation": 30.76},
|
"input": {"pv1": {"Voltage": 35.3, "Current": 1.68, "Power": 59.6, "Daily_Generation": 0.04, "Total_Generation": 30.76},
|
||||||
@@ -116,8 +116,19 @@ def test_build_ha_conf1():
|
|||||||
tests +=1
|
tests +=1
|
||||||
|
|
||||||
elif id == 'power_pv2_123':
|
elif id == 'power_pv2_123':
|
||||||
assert False # if we haven't received and parsed a control data msg, we don't know the number of inputs. In this case we only register the first one!!
|
assert comp == 'sensor'
|
||||||
|
assert d_json == json.dumps({"name": "Power", "stat_t": "tsun/garagendach/input", "dev_cla": "power", "stat_cla": "measurement", "uniq_id": "power_pv2_123", "val_tpl": "{{ (value_json['pv2']['Power'] | float)}}", "unit_of_meas": "W", "dev": {"name": "Module PV2", "sa": "Module PV2", "via_device": "inverter_123", "ids": ["input_pv2_123"]}, "o": {"name": "proxy", "sw": "unknown"}})
|
||||||
|
tests +=1
|
||||||
|
|
||||||
|
elif id == 'power_pv3_123':
|
||||||
|
assert comp == 'sensor'
|
||||||
|
assert d_json == json.dumps({"name": "Power", "stat_t": "tsun/garagendach/input", "dev_cla": "power", "stat_cla": "measurement", "uniq_id": "power_pv3_123", "val_tpl": "{{ (value_json['pv3']['Power'] | float)}}", "unit_of_meas": "W", "dev": {"name": "Module PV3", "sa": "Module PV3", "via_device": "inverter_123", "ids": ["input_pv3_123"]}, "o": {"name": "proxy", "sw": "unknown"}})
|
||||||
|
tests +=1
|
||||||
|
|
||||||
|
elif id == 'power_pv4_123':
|
||||||
|
assert comp == 'sensor'
|
||||||
|
assert d_json == json.dumps({"name": "Power", "stat_t": "tsun/garagendach/input", "dev_cla": "power", "stat_cla": "measurement", "uniq_id": "power_pv4_123", "val_tpl": "{{ (value_json['pv4']['Power'] | float)}}", "unit_of_meas": "W", "dev": {"name": "Module PV4", "sa": "Module PV4", "via_device": "inverter_123", "ids": ["input_pv4_123"]}, "o": {"name": "proxy", "sw": "unknown"}})
|
||||||
|
tests +=1
|
||||||
|
|
||||||
elif id == 'signal_123':
|
elif id == 'signal_123':
|
||||||
assert comp == 'sensor'
|
assert comp == 'sensor'
|
||||||
@@ -126,7 +137,7 @@ def test_build_ha_conf1():
|
|||||||
elif id == 'inv_count_456':
|
elif id == 'inv_count_456':
|
||||||
assert False
|
assert False
|
||||||
|
|
||||||
assert tests==4
|
assert tests==7
|
||||||
|
|
||||||
|
|
||||||
for d_json, comp, node_id, id in i.ha_proxy_confs(ha_prfx="tsun/", node_id = 'proxy/', snr = '456'):
|
for d_json, comp, node_id, id in i.ha_proxy_confs(ha_prfx="tsun/", node_id = 'proxy/', snr = '456'):
|
||||||
@@ -138,8 +149,11 @@ def test_build_ha_conf1():
|
|||||||
elif id == 'power_pv1_123':
|
elif id == 'power_pv1_123':
|
||||||
assert False
|
assert False
|
||||||
elif id == 'power_pv2_123':
|
elif id == 'power_pv2_123':
|
||||||
assert False # if we haven't received and parsed a control data msg, we don't know the number of inputs. In this case we only register the first one!!
|
assert False
|
||||||
|
elif id == 'power_pv3_123':
|
||||||
|
assert False
|
||||||
|
elif id == 'power_pv4_123':
|
||||||
|
assert False
|
||||||
elif id == 'signal_123':
|
elif id == 'signal_123':
|
||||||
assert False
|
assert False
|
||||||
elif id == 'inv_count_456':
|
elif id == 'inv_count_456':
|
||||||
@@ -147,7 +161,7 @@ def test_build_ha_conf1():
|
|||||||
assert d_json == json.dumps({"name": "Active Inverter Connections", "stat_t": "tsun/proxy/proxy", "dev_cla": None, "stat_cla": None, "uniq_id": "inv_count_456", "val_tpl": "{{value_json['Inverter_Cnt'] | int}}", "ic": "mdi:counter", "dev": {"name": "Proxy", "sa": "Proxy", "mdl": "proxy", "mf": "Stefan Allius", "sw": "unknown", "ids": ["proxy"]}, "o": {"name": "proxy", "sw": "unknown"}})
|
assert d_json == json.dumps({"name": "Active Inverter Connections", "stat_t": "tsun/proxy/proxy", "dev_cla": None, "stat_cla": None, "uniq_id": "inv_count_456", "val_tpl": "{{value_json['Inverter_Cnt'] | int}}", "ic": "mdi:counter", "dev": {"name": "Proxy", "sa": "Proxy", "mdl": "proxy", "mf": "Stefan Allius", "sw": "unknown", "ids": ["proxy"]}, "o": {"name": "proxy", "sw": "unknown"}})
|
||||||
tests +=1
|
tests +=1
|
||||||
|
|
||||||
assert tests==5
|
assert tests==8
|
||||||
|
|
||||||
def test_exception_and_eval(InverterData: bytes):
|
def test_exception_and_eval(InverterData: bytes):
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user