From 54de2aecfea88e6760481fca5112b945edf373ca Mon Sep 17 00:00:00 2001 From: Stefan Allius <122395479+s-allius@users.noreply.github.com> Date: Fri, 16 Aug 2024 21:07:08 +0200 Subject: [PATCH] Sonar qube 3 (#165) * cleanup * Add support for TSUN Titan inverter Fixes #161 * fix SonarQube warnings * fix error * rename field "config" * SonarQube reads flake8 output * don't stop on flake8 errors * flake8 scan only app/src for SonarQube * update flake8 run * ignore flake8 C901 * cleanup * fix linter warnings * ignore changed *.yml files * read sensor list solarman data packets * catch 'No route to' error and log only in debug mode * fix unit tests * add sensor_list configuration * adapt unit tests * fix SonarQube warnings --- .github/workflows/python-app.yml | 11 ++- app/src/config.py | 77 ++++++++++--------- app/src/gen3plus/infos_g3p.py | 4 +- app/src/gen3plus/solarman_v5.py | 37 +++++---- app/src/infos.py | 127 +++++++++++++++++-------------- app/src/modbus_tcp.py | 8 +- app/tests/test_config.py | 22 +++--- app/tests/test_infos_g3p.py | 4 +- app/tests/test_solarman.py | 14 +++- app/tests/test_talent.py | 12 +-- sonar-project.properties | 2 - 11 files changed, 179 insertions(+), 139 deletions(-) diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml index c2a0b1d..deb3530 100644 --- a/.github/workflows/python-app.yml +++ b/.github/workflows/python-app.yml @@ -53,10 +53,9 @@ jobs: # stop the build if there are Python syntax errors or undefined names flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide - flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + flake8 --exit-zero --ignore=C901,E121,E123,E126,E133,E226,E241,E242,E704,W503,W504,W505 --format=pylint --output-file=output_flake.txt --exclude=*.pyc app/src/ - name: Test with pytest run: | - #pytest app --doctest-modules --junitxml=junit/test-results.xml --cov=com --cov-report=xml --cov-report=html python -m pytest app --cov=app/src --cov-report=xml coverage report - name: Analyze with SonarCloud @@ -65,4 +64,10 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} with: - projectBaseDir: . \ No newline at end of file + projectBaseDir: . + args: + -Dsonar.projectKey=s-allius_tsun-gen3-proxy + -Dsonar.python.coverage.reportPaths=coverage.xml + -Dsonar.python.flake8.reportPaths=output_flake.txt + # -Dsonar.docker.hadolint.reportPaths= + \ No newline at end of file diff --git a/app/src/config.py b/app/src/config.py index 6f16049..02138e7 100644 --- a/app/src/config.py +++ b/app/src/config.py @@ -12,81 +12,84 @@ class Config(): Read config.toml file and sanitize it with read(). Get named parts of the config with get()''' - config = {} + act_config = {} def_config = {} conf_schema = Schema({ 'tsun': { 'enabled': Use(bool), - 'host': Use(str), - 'port': And(Use(int), lambda n: 1024 <= n <= 65535) - }, + 'host': Use(str), + 'port': And(Use(int), lambda n: 1024 <= n <= 65535) + }, 'solarman': { 'enabled': Use(bool), - 'host': Use(str), - 'port': And(Use(int), lambda n: 1024 <= n <= 65535) - }, + 'host': Use(str), + 'port': And(Use(int), lambda n: 1024 <= n <= 65535) + }, 'mqtt': { - 'host': Use(str), - 'port': And(Use(int), lambda n: 1024 <= n <= 65535), - 'user': And(Use(str), Use(lambda s: s if len(s) > 0 else None)), - 'passwd': And(Use(str), Use(lambda s: s if len(s) > 0 else None)) - }, + 'host': Use(str), + 'port': And(Use(int), lambda n: 1024 <= n <= 65535), + 'user': And(Use(str), Use(lambda s: s if len(s) > 0 else None)), + 'passwd': And(Use(str), Use(lambda s: s if len(s) > 0 else None)) + }, 'ha': { 'auto_conf_prefix': Use(str), 'discovery_prefix': Use(str), - 'entity_prefix': Use(str), - 'proxy_node_id': Use(str), - 'proxy_unique_id': Use(str) - }, + 'entity_prefix': Use(str), + 'proxy_node_id': Use(str), + 'proxy_unique_id': Use(str) + }, 'gen3plus': { 'at_acl': { Or('mqtt', 'tsun'): { 'allow': [str], Optional('block', default=[]): [str] - } } - }, + } + }, 'inverters': { 'allow_all': Use(bool), 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)), + 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('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('pv1'): { Optional('type'): Use(str), Optional('manufacturer'): Use(str), - }, + }, Optional('pv2'): { Optional('type'): Use(str), Optional('manufacturer'): Use(str), - }, + }, Optional('pv3'): { Optional('type'): Use(str), Optional('manufacturer'): Use(str), - }, + }, Optional('pv4'): { Optional('type'): Use(str), Optional('manufacturer'): Use(str), - }, + }, Optional('pv5'): { Optional('type'): Use(str), Optional('manufacturer'): Use(str), - }, + }, Optional('pv6'): { Optional('type'): Use(str), Optional('manufacturer'): Use(str), - } - }} - }, ignore_extra_keys=True - ) + } + } + } + }, ignore_extra_keys=True + ) @classmethod def class_init(cls) -> None | str: # pragma: no cover @@ -146,17 +149,17 @@ class Config(): config[key] |= usr_config[key] try: - cls.config = cls.conf_schema.validate(config) + cls.act_config = cls.conf_schema.validate(config) except Exception as error: err = f'Config.read: {error}' logging.error(err) - # logging.debug(f'Readed config: "{cls.config}" ') + # logging.debug(f'Readed config: "{cls.act_config}" ') except Exception as error: err = f'Config.read: {error}' logger.error(err) - cls.config = {} + cls.act_config = {} return err @@ -166,12 +169,12 @@ class Config(): None it returns the complete config dict''' if member: - return cls.config.get(member, {}) + return cls.act_config.get(member, {}) else: - return cls.config + return cls.act_config @classmethod def is_default(cls, member: str) -> bool: '''Check if the member is the default value''' - return cls.config.get(member) == cls.def_config.get(member) + return cls.act_config.get(member) == cls.def_config.get(member) diff --git a/app/src/gen3plus/infos_g3p.py b/app/src/gen3plus/infos_g3p.py index 277b8c7..2d6a2fc 100644 --- a/app/src/gen3plus/infos_g3p.py +++ b/app/src/gen3plus/infos_g3p.py @@ -20,9 +20,11 @@ class RegisterMap: 0x4102001c: {'reg': Register.SIGNAL_STRENGTH, 'fmt': '>12)}.{(result>>8)&0xf}.{(result>>4)&0xf}{result&0xf}'"}, # noqa: E501 diff --git a/app/src/gen3plus/solarman_v5.py b/app/src/gen3plus/solarman_v5.py index 7feccd5..0cbdfab 100644 --- a/app/src/gen3plus/solarman_v5.py +++ b/app/src/gen3plus/solarman_v5.py @@ -61,7 +61,7 @@ class SolarmanV5(Message): '''format string for packing of the header''' def __init__(self, server_side: bool, client_mode: bool): - super().__init__(server_side, self.send_modbus_cb, mb_timeout=5) + super().__init__(server_side, self.send_modbus_cb, mb_timeout=8) self.header_len = 11 # overwrite construcor in class Message self.control = 0 @@ -138,6 +138,7 @@ class SolarmanV5(Message): self.mb_first_timeout = self.MB_START_TIMEOUT '''timer value for next Modbus polling request''' self.modbus_polling = False + self.sensor_list = 0x0000 ''' Our puplic methods @@ -186,12 +187,19 @@ class SolarmanV5(Message): self.db.set_db_def_value(Register.POLLING_INTERVAL, self.mb_timeout) + def __set_config_parms(self, inv: dict): + '''init connection with params from the configuration''' + self.node_id = inv['node_id'] + self.sug_area = inv['suggested_area'] + self.modbus_polling = inv['modbus_polling'] + self.sensor_list = inv['sensor_list'] + def __set_serial_no(self, snr: int): + '''check the serial number and configure the inverter connection''' serial_no = str(snr) if self.unique_id == serial_no: logger.debug(f'SerialNo: {serial_no}') else: - found = False inverters = Config.get('inverters') # logger.debug(f'Inverters: {inverters}') @@ -199,14 +207,11 @@ class SolarmanV5(Message): # logger.debug(f'key: {key} -> {inv}') if (type(inv) is dict and 'monitor_sn' in inv and inv['monitor_sn'] == snr): - found = True - self.node_id = inv['node_id'] - self.sug_area = inv['suggested_area'] - self.modbus_polling = inv['modbus_polling'] - logger.debug(f'SerialNo {serial_no} allowed! area:{self.sug_area}') # noqa: E501 + self.__set_config_parms(inv) self.db.set_pv_module_details(inv) - - if not found: + logger.debug(f'SerialNo {serial_no} allowed! area:{self.sug_area}') # noqa: E501 + break + else: self.node_id = '' self.sug_area = '' if 'allow_all' not in inverters or not inverters['allow_all']: @@ -214,7 +219,7 @@ class SolarmanV5(Message): self.unique_id = None logger.warning(f'ignore message from unknow inverter! (SerialNo: {serial_no})') # noqa: E501 return - logger.debug(f'SerialNo {serial_no} not known but accepted!') + logger.warning(f'SerialNo {serial_no} not known but accepted!') self.unique_id = serial_no @@ -405,7 +410,7 @@ class SolarmanV5(Message): return self.__build_header(0x4510) self._send_buffer += struct.pack(' dict: - return cls.config + return cls.act_config def test_empty_config(): @@ -30,7 +30,7 @@ def test_default_config(): validated = Config.conf_schema.validate(cnf) except Exception: assert False - 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'}, 'inverters': {'allow_all': True, 'R170000000000001': {'node_id': '', 'modbus_polling': False, 'monitor_sn': 0, 'suggested_area': ''}, 'Y170000000000001': {'modbus_polling': True, 'monitor_sn': 2000000000, 'node_id': '', 'suggested_area': ''}}} + 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'}, 'inverters': {'allow_all': True, 'R170000000000001': {'node_id': '', 'modbus_polling': False, 'monitor_sn': 0, 'suggested_area': '', 'sensor_list': 688}, 'Y170000000000001': {'modbus_polling': True, 'monitor_sn': 2000000000, 'node_id': '', 'suggested_area': '', 'sensor_list': 688}}} def test_full_config(): cnf = {'tsun': {'enabled': True, 'host': 'logger.talent-monitoring.com', 'port': 5005}, @@ -40,13 +40,13 @@ def test_full_config(): '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'}, 'inverters': {'allow_all': True, - 'R170000000000001': {'modbus_polling': True, 'node_id': '', 'suggested_area': '', 'pv1': {'type': 'type1', 'manufacturer': 'man1'}, 'pv2': {'type': 'type2', 'manufacturer': 'man2'}, 'pv3': {'type': 'type3', 'manufacturer': 'man3'}}, - 'Y170000000000001': {'modbus_polling': True, 'monitor_sn': 2000000000, 'node_id': '', 'suggested_area': ''}}} + 'R170000000000001': {'modbus_polling': True, 'node_id': '', 'sensor_list': 0, 'suggested_area': '', 'pv1': {'type': 'type1', 'manufacturer': 'man1'}, 'pv2': {'type': 'type2', 'manufacturer': 'man2'}, 'pv3': {'type': 'type3', 'manufacturer': 'man3'}}, + 'Y170000000000001': {'modbus_polling': True, 'monitor_sn': 2000000000, 'node_id': '', 'sensor_list': 0x1511, 'suggested_area': ''}}} try: validated = Config.conf_schema.validate(cnf) except Exception: assert False - 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'}, 'inverters': {'allow_all': True, 'R170000000000001': {'node_id': '', 'modbus_polling': True, 'monitor_sn': 0, 'pv1': {'manufacturer': 'man1','type': 'type1'},'pv2': {'manufacturer': 'man2','type': 'type2'},'pv3': {'manufacturer': 'man3','type': 'type3'}, 'suggested_area': ''}, 'Y170000000000001': {'modbus_polling': True, 'monitor_sn': 2000000000, 'node_id': '', 'suggested_area': ''}}} + 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'}, 'inverters': {'allow_all': True, 'R170000000000001': {'node_id': '', 'modbus_polling': True, 'monitor_sn': 0, 'pv1': {'manufacturer': 'man1','type': 'type1'},'pv2': {'manufacturer': 'man2','type': 'type2'},'pv3': {'manufacturer': 'man3','type': 'type3'}, 'suggested_area': '', 'sensor_list': 0}, 'Y170000000000001': {'modbus_polling': True, 'monitor_sn': 2000000000, 'node_id': '', 'suggested_area': '', 'sensor_list': 5393}}} def test_mininum_config(): cnf = {'tsun': {'enabled': True, 'host': 'logger.talent-monitoring.com', 'port': 5005}, @@ -63,7 +63,7 @@ def test_mininum_config(): validated = Config.conf_schema.validate(cnf) except Exception: assert False - 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'}, 'inverters': {'allow_all': True, 'R170000000000001': {'node_id': '', 'modbus_polling': True, 'monitor_sn': 0, 'suggested_area': ''}}} + 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'}, 'inverters': {'allow_all': True, 'R170000000000001': {'node_id': '', 'modbus_polling': True, 'monitor_sn': 0, 'suggested_area': '', 'sensor_list': 688}}} def test_read_empty(): cnf = {} @@ -71,7 +71,7 @@ def test_read_empty(): err = TstConfig.read('app/config/') assert err == None cnf = TstConfig.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'}, 'inverters': {'allow_all': True, 'R170000000000001': {'suggested_area': '', 'modbus_polling': False, 'monitor_sn': 0, 'node_id': ''}, 'Y170000000000001': {'modbus_polling': True, 'monitor_sn': 2000000000, 'suggested_area': '', 'node_id': ''}}} + 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'}, 'inverters': {'allow_all': True, 'R170000000000001': {'suggested_area': '', 'modbus_polling': False, 'monitor_sn': 0, 'node_id': '', 'sensor_list': 688}, 'Y170000000000001': {'modbus_polling': True, 'monitor_sn': 2000000000, 'suggested_area': '', 'node_id': '', 'sensor_list': 688}}} defcnf = TstConfig.def_config.get('solarman') assert defcnf == {'enabled': True, 'host': 'iot.talent-monitoring.com', 'port': 10000} @@ -93,7 +93,7 @@ def test_read_cnf1(): err = TstConfig.read('app/config/') assert err == None cnf = TstConfig.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'}, 'inverters': {'allow_all': True, 'R170000000000001': {'suggested_area': '', 'modbus_polling': False, 'monitor_sn': 0, 'node_id': ''}, 'Y170000000000001': {'modbus_polling': True, 'monitor_sn': 2000000000, 'suggested_area': '', 'node_id': ''}}} + 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'}, 'inverters': {'allow_all': True, 'R170000000000001': {'suggested_area': '', 'modbus_polling': False, 'monitor_sn': 0, 'node_id': '', 'sensor_list': 688}, 'Y170000000000001': {'modbus_polling': True, 'monitor_sn': 2000000000, 'suggested_area': '', 'node_id': '', 'sensor_list': 688}}} cnf = TstConfig.get('solarman') assert cnf == {'enabled': False, 'host': 'iot.talent-monitoring.com', 'port': 10000} defcnf = TstConfig.def_config.get('solarman') @@ -106,7 +106,7 @@ def test_read_cnf2(): err = TstConfig.read('app/config/') assert err == None cnf = TstConfig.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'}, 'inverters': {'allow_all': True, 'R170000000000001': {'suggested_area': '', 'modbus_polling': False, 'monitor_sn': 0, 'node_id': ''}, 'Y170000000000001': {'modbus_polling': True, 'monitor_sn': 2000000000, 'suggested_area': '', 'node_id': ''}}} + 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'}, 'inverters': {'allow_all': True, 'R170000000000001': {'suggested_area': '', 'modbus_polling': False, 'monitor_sn': 0, 'node_id': '', 'sensor_list': 688}, 'Y170000000000001': {'modbus_polling': True, 'monitor_sn': 2000000000, 'suggested_area': '', 'node_id': '', 'sensor_list': 688}}} assert True == TstConfig.is_default('solarman') def test_read_cnf3(): @@ -123,7 +123,7 @@ def test_read_cnf4(): err = TstConfig.read('app/config/') assert err == None cnf = TstConfig.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'}, 'inverters': {'allow_all': True, 'R170000000000001': {'suggested_area': '', 'modbus_polling': False, 'monitor_sn': 0, 'node_id': ''}, 'Y170000000000001': {'modbus_polling': True, 'monitor_sn': 2000000000, 'suggested_area': '', 'node_id': ''}}} + 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'}, 'inverters': {'allow_all': True, 'R170000000000001': {'suggested_area': '', 'modbus_polling': False, 'monitor_sn': 0, 'node_id': '', 'sensor_list': 688}, 'Y170000000000001': {'modbus_polling': True, 'monitor_sn': 2000000000, 'suggested_area': '', 'node_id': '', 'sensor_list': 688}}} assert False == TstConfig.is_default('solarman') def test_read_cnf5(): diff --git a/app/tests/test_infos_g3p.py b/app/tests/test_infos_g3p.py index 549c8d3..21ef570 100644 --- a/app/tests/test_infos_g3p.py +++ b/app/tests/test_infos_g3p.py @@ -70,7 +70,7 @@ def test_parse_4110(device_data: bytes): pass # side effect is calling generator i.parse() assert json.dumps(i.db) == json.dumps({ - 'controller': {"Data_Up_Interval": 300, "Collect_Interval": 1, "Heartbeat_Interval": 120, "Signal_Strength": 100, "IP_Address": "192.168.80.49"}, + 'controller': {"Data_Up_Interval": 300, "Collect_Interval": 1, "Heartbeat_Interval": 120, "Signal_Strength": 100, "IP_Address": "192.168.80.49", "Sensor_List": "02b0"}, 'collector': {"Chip_Model": "LSW5BLE_17_02B0_1.05", "Collector_Fw_Version": "V1.1.00.0B"}, }) @@ -82,7 +82,7 @@ def test_parse_4210(inverter_data: bytes): pass # side effect is calling generator i.parse() assert json.dumps(i.db) == json.dumps({ - "controller": {"Power_On_Time": 2051}, + "controller": {"Sensor_List": "02b0", "Power_On_Time": 2051}, "inverter": {"Serial_Number": "Y17E00000000000E", "Version": "V4.0.10", "Rated_Power": 600, "Max_Designed_Power": 2000}, "env": {"Inverter_Status": 1, "Inverter_Temp": 14}, "grid": {"Voltage": 224.8, "Current": 0.73, "Frequency": 50.05, "Output_Power": 165.8}, diff --git a/app/tests/test_solarman.py b/app/tests/test_solarman.py index 7ead651..66d17f0 100644 --- a/app/tests/test_solarman.py +++ b/app/tests/test_solarman.py @@ -633,15 +633,15 @@ def msg_unknown_cmd_rsp(): # 0x1510 @pytest.fixture def config_tsun_allow_all(): - Config.config = {'solarman':{'enabled': True}, 'inverters':{'allow_all':True}} + Config.act_config = {'solarman':{'enabled': True}, 'inverters':{'allow_all':True}} @pytest.fixture def config_no_tsun_inv1(): - Config.config = {'solarman':{'enabled': False},'inverters':{'Y170000000000001':{'monitor_sn': 2070233889, 'node_id':'inv1', 'modbus_polling': True, 'suggested_area':'roof'}}} + Config.act_config = {'solarman':{'enabled': False},'inverters':{'Y170000000000001':{'monitor_sn': 2070233889, 'node_id':'inv1', 'modbus_polling': True, 'suggested_area':'roof', 'sensor_list': 688}}} @pytest.fixture def config_tsun_inv1(): - Config.config = {'solarman':{'enabled': True},'inverters':{'Y170000000000001':{'monitor_sn': 2070233889, 'node_id':'inv1', 'modbus_polling': True, 'suggested_area':'roof'}}} + Config.act_config = {'solarman':{'enabled': True},'inverters':{'Y170000000000001':{'monitor_sn': 2070233889, 'node_id':'inv1', 'modbus_polling': True, 'suggested_area':'roof', 'sensor_list': 688}}} def test_read_message(device_ind_msg): m = MemoryStream(device_ind_msg, (0,)) @@ -843,7 +843,7 @@ def test_read_two_messages(config_tsun_allow_all, device_ind_msg, device_rsp_msg config_tsun_allow_all m = MemoryStream(device_ind_msg, (0,)) m.append_msg(inverter_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 @@ -858,6 +858,8 @@ def test_read_two_messages(config_tsun_allow_all, device_ind_msg, device_rsp_msg assert m.msg_recvd[1]['control']==0x4210 assert m.msg_recvd[1]['seq']=='02:02' assert m.msg_recvd[1]['data_len']==0x199 + assert '02b0' == m.db.get_db_value(Register.SENSOR_LIST, None) + assert 0x02b0 == m.sensor_list assert m._forward_buffer==device_ind_msg+inverter_ind_msg assert m._send_buffer==device_rsp_msg+inverter_rsp_msg @@ -1078,6 +1080,7 @@ def test_sync_end_rsp(config_tsun_inv1, sync_end_rsp_msg): def test_build_modell_600(config_tsun_allow_all, inverter_ind_msg): config_tsun_allow_all m = MemoryStream(inverter_ind_msg, (0,)) + assert 0 == m.sensor_list assert 0 == m.db.get_db_value(Register.MAX_DESIGNED_POWER, 0) assert None == m.db.get_db_value(Register.RATED_POWER, None) assert None == m.db.get_db_value(Register.INVERTER_TEMP, None) @@ -1085,6 +1088,8 @@ def test_build_modell_600(config_tsun_allow_all, inverter_ind_msg): assert 2000 == m.db.get_db_value(Register.MAX_DESIGNED_POWER, 0) assert 600 == m.db.get_db_value(Register.RATED_POWER, 0) assert 'TSOL-MS2000(600)' == m.db.get_db_value(Register.EQUIPMENT_MODEL, 0) + assert '02b0' == m.db.get_db_value(Register.SENSOR_LIST, None) + assert 0 == m.sensor_list # must not been set by an inverter data ind m._send_buffer = bytearray(0) # clear send buffer for next test m._init_new_client_conn() @@ -1420,6 +1425,7 @@ def test_msg_modbus_req(config_tsun_inv1, msg_modbus_cmd, msg_modbus_cmd_fwd): config_tsun_inv1 m = MemoryStream(b'') m.snr = get_sn_int() + m.sensor_list = 0x2b0 m.state = State.up c = m.createClientStream(msg_modbus_cmd) diff --git a/app/tests/test_talent.py b/app/tests/test_talent.py index cf70cd1..5aadf8d 100644 --- a/app/tests/test_talent.py +++ b/app/tests/test_talent.py @@ -98,12 +98,12 @@ class MemoryStream(Talent): @pytest.fixture def msg_contact_info(): # Contact Info message - Config.config = {'tsun':{'enabled': True}} + Config.act_config = {'tsun':{'enabled': True}} return b'\x00\x00\x00\x2c\x10R170000000000001\x91\x00\x08solarhub\x0fsolarhub\x40123456' @pytest.fixture def msg_contact_info_long_id(): # Contact Info message with longer ID - Config.config = {'tsun':{'enabled': True}} + Config.act_config = {'tsun':{'enabled': True}} return b'\x00\x00\x00\x2d\x11R1700000000000011\x91\x00\x08solarhub\x0fsolarhub\x40123456' @pytest.fixture @@ -353,19 +353,19 @@ def msg_unknown(): # Get Time Request message @pytest.fixture def config_tsun_allow_all(): - Config.config = {'tsun':{'enabled': True}, 'inverters':{'allow_all':True}} + Config.act_config = {'tsun':{'enabled': True}, 'inverters':{'allow_all':True}} @pytest.fixture def config_no_tsun_inv1(): - Config.config = {'tsun':{'enabled': False},'inverters':{'R170000000000001':{'node_id':'inv1', 'modbus_polling': True, 'suggested_area':'roof'}}} + Config.act_config = {'tsun':{'enabled': False},'inverters':{'R170000000000001':{'node_id':'inv1', 'modbus_polling': True, 'suggested_area':'roof'}}} @pytest.fixture def config_tsun_inv1(): - Config.config = {'tsun':{'enabled': True},'inverters':{'R170000000000001':{'node_id':'inv1', 'modbus_polling': True, 'suggested_area':'roof'}}} + Config.act_config = {'tsun':{'enabled': True},'inverters':{'R170000000000001':{'node_id':'inv1', 'modbus_polling': True, 'suggested_area':'roof'}}} @pytest.fixture def config_no_modbus_poll(): - Config.config = {'tsun':{'enabled': True},'inverters':{'R170000000000001':{'node_id':'inv1', 'modbus_polling': False, 'suggested_area':'roof'}}} + Config.act_config = {'tsun':{'enabled': True},'inverters':{'R170000000000001':{'node_id':'inv1', 'modbus_polling': False, 'suggested_area':'roof'}}} @pytest.fixture def msg_ota_req(): # Over the air update request from tsun cloud diff --git a/sonar-project.properties b/sonar-project.properties index d2b9b66..61d8dbd 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -13,10 +13,8 @@ sonar.sources=app/src/ #sonar.sourceEncoding=UTF-8 sonar.python.version=3.12 -sonar.python.coverage.reportPaths=coverage.xml sonar.tests=system_tests/,app/tests/ sonar.exclusions=**/.vscode/**/* - # Name your criteria sonar.issue.ignore.multicriteria=e1,e2