From 264e09651f4a88f9c8635d8556988ee27135b114 Mon Sep 17 00:00:00 2001 From: Stefan Allius <122395479+s-allius@users.noreply.github.com> Date: Mon, 16 Dec 2024 21:09:32 +0100 Subject: [PATCH] ignore inverter replays which a older than 1 day (#246) --- app/src/gen3/talent.py | 12 +++- app/tests/test_talent.py | 115 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+), 3 deletions(-) diff --git a/app/src/gen3/talent.py b/app/src/gen3/talent.py index efb080a..73fdae7 100644 --- a/app/src/gen3/talent.py +++ b/app/src/gen3/talent.py @@ -449,7 +449,7 @@ class Talent(Message): self.__build_header(0x99) self.ifc.tx_add(b'\x01') self.__finish_send_msg() - self.__process_data() + self.__process_data(False) elif self.ctrl.is_resp(): return # ignore received response @@ -464,7 +464,7 @@ class Talent(Message): self.__build_header(0x99) self.ifc.tx_add(b'\x01') self.__finish_send_msg() - self.__process_data() + self.__process_data(True) self.state = State.up # allow MODBUS cmds if (self.modbus_polling): self.mb_timer.start(self.mb_first_timeout) @@ -479,8 +479,14 @@ class Talent(Message): self.forward() - def __process_data(self): + def __process_data(self, ignore_replay: bool): msg_hdr_len, ts = self.parse_msg_header() + if ignore_replay: + age = self._utc() - self._utcfromts(ts) + age = age/(3600*24) + logger.debug(f"Age: {age} days") + if age > 1: + return for key, update in self.db.parse(self.ifc.rx_peek(), self.header_len + msg_hdr_len, self.node_id): diff --git a/app/tests/test_talent.py b/app/tests/test_talent.py index 2b1ef6c..b5d6e36 100644 --- a/app/tests/test_talent.py +++ b/app/tests/test_talent.py @@ -328,6 +328,90 @@ def msg_inverter_ind_new(): # Data indication from DSP V5.0.17 msg += b'\x00\x00\x00\x00' return msg +@pytest.fixture +def msg_inverter_ind_new2(): # Data indication from DSP V5.0.17 + msg = b'\x00\x00\x04\xf4\x10R170000000000001\x91\x04\x01\x90\x00\x01\x10R170000000000001' + msg += b'\x01\x00\x00\x01' + msg += b'\x86\x98\x55\xe7\x48\x00\x00\x00\xa3\x00\x00\x01\x93\x53\x00\x00' + msg += b'\x00\x00\x01\x94\x53\x00\x00\x00\x00\x01\x95\x53\x00\x00\x00\x00' + msg += b'\x01\x96\x53\x00\x00\x00\x00\x01\x97\x53\x00\x00\x00\x00\x01\x98' + msg += b'\x53\x00\x00\x00\x00\x01\x99\x53\x00\x00\x00\x00\x01\x9a\x53\x00' + msg += b'\x00\x00\x00\x01\x9b\x53\x00\x00\x00\x00\x01\x9c\x53\x00\x00\x00' + msg += b'\x00\x01\x9d\x53\x00\x00\x00\x00\x01\x9e\x53\x00\x00\x00\x00\x01' + msg += b'\x9f\x53\x00\x00\x00\x00\x01\xa0\x53\x00\x00\x00\x00\x01\xf4\x49' + msg += b'\x00\x00\x00\x00\x00\x00\x01\xf5\x53\x00\x00\x00\x00\x01\xf6\x53' + msg += b'\x00\x00\x00\x00\x01\xf7\x53\x00\x00\x00\x00\x01\xf8\x53\x00\x00' + msg += b'\x00\x00\x01\xf9\x53\x00\x00\x00\x00\x01\xfa\x53\x00\x00\x00\x00' + msg += b'\x01\xfb\x53\x00\x00\x00\x00\x01\xfc\x53\x00\x00\x00\x00\x01\xfd' + msg += b'\x53\x00\x00\x00\x00\x01\xfe\x53\x00\x00\x00\x00\x01\xff\x53\x00' + msg += b'\x00\x00\x00\x02\x00\x53\x00\x00\x00\x00\x02\x01\x53\x00\x00\x00' + msg += b'\x00\x02\x02\x53\x00\x00\x00\x00\x02\x03\x53\x00\x00\x00\x00\x02' + msg += b'\x04\x53\x00\x00\x00\x00\x02\x58\x49\x00\x00\x00\x00\x00\x00\x02' + msg += b'\x59\x53\x00\x00\x00\x00\x02\x5a\x53\x00\x00\x00\x00\x02\x5b\x53' + msg += b'\x00\x00\x00\x00\x02\x5c\x53\x00\x00\x00\x00\x02\x5d\x53\x00\x00' + msg += b'\x00\x00\x02\x5e\x53\x00\x00\x00\x00\x02\x5f\x53\x00\x00\x00\x00' + msg += b'\x02\x60\x53\x00\x00\x00\x00\x02\x61\x53\x00\x00\x00\x00\x02\x62' + msg += b'\x53\x00\x00\x00\x00\x02\x63\x53\x00\x00\x00\x00\x02\x64\x53\x00' + msg += b'\x00\x00\x00\x02\x65\x53\x00\x00\x00\x00\x02\x66\x53\x00\x00\x00' + msg += b'\x00\x02\x67\x53\x00\x00\x00\x00\x02\x68\x53\x00\x00\x00\x00\x02' + msg += b'\xbc\x49\x00\x00\x00\x00\x00\x00\x02\xbd\x53\x00\x00\x00\x00\x02' + msg += b'\xbe\x53\x00\x00\x00\x00\x02\xbf\x53\x00\x00\x00\x00\x02\xc0\x53' + msg += b'\x00\x00\x00\x00\x02\xc1\x53\x00\x00\x00\x00\x02\xc2\x53\x00\x00' + msg += b'\x00\x00\x02\xc3\x53\x00\x00\x00\x00\x02\xc4\x53\x00\x00\x00\x00' + msg += b'\x02\xc5\x53\x00\x00\x00\x00\x02\xc6\x53\x00\x00\x00\x00\x02\xc7' + msg += b'\x53\x00\x00\x00\x00\x02\xc8\x53\x00\x00\x00\x00\x02\xc9\x53\x00' + msg += b'\x00\x00\x00\x02\xca\x53\x00\x00\x00\x00\x02\xcb\x53\x00\x00\x00' + msg += b'\x00\x02\xcc\x53\x00\x00\x00\x00\x03\x20\x53\x00\x00\x00\x00\x03' + msg += b'\x84\x53\x50\x11\x00\x00\x03\xe8\x46\x43\x65\xcc\xcd\x00\x00\x04' + msg += b'\x4c\x46\x40\x0c\xcc\xcd\x00\x00\x04\xb0\x46\x42\x47\xd7\x0a\x00' + msg += b'\x00\x05\x14\x53\x00\x35\x00\x00\x05\x78\x53\x00\x00\x00\x00\x05' + msg += b'\xdc\x53\x03\x20\x00\x00\x06\x40\x46\x43\xfd\x4c\xcd\x00\x00\x06' + msg += b'\xa4\x46\x42\x18\x00\x00\x00\x00\x07\x08\x46\x40\xde\x14\x7b\x00' + msg += b'\x00\x07\x6c\x46\x43\x84\x33\x33\x00\x00\x07\xd0\x46\x42\x1a\x00' + msg += b'\x00\x00\x00\x08\x34\x46\x40\xda\x8f\x5c\x00\x00\x08\x98\x46\x43' + msg += b'\x83\xb3\x33\x00\x00\x08\xfc\x46\x00\x00\x00\x00\x00\x00\x09\x60' + msg += b'\x46\x00\x00\x00\x00\x00\x00\x09\xc4\x46\x00\x00\x00\x00\x00\x00' + msg += b'\x0a\x28\x46\x00\x00\x00\x00\x00\x00\x0a\x8c\x46\x00\x00\x00\x00' + msg += b'\x00\x00\x0a\xf0\x46\x00\x00\x00\x00\x00\x00\x0b\x54\x46\x40\x9c' + msg += b'\xcc\xcd\x00\x00\x0b\xb8\x46\x43\xea\xb5\xc3\x00\x00\x0c\x1c\x46' + msg += b'\x40\x1e\xb8\x52\x00\x00\x0c\x80\x46\x43\x6d\x2b\x85\x00\x00\x0c' + msg += b'\xe4\x46\x40\x1a\xe1\x48\x00\x00\x0d\x48\x46\x43\x68\x40\x00\x00' + msg += b'\x00\x0d\xac\x46\x00\x00\x00\x00\x00\x00\x0e\x10\x46\x00\x00\x00' + msg += b'\x00\x00\x00\x0e\x74\x46\x00\x00\x00\x00\x00\x00\x0e\xd8\x46\x00' + msg += b'\x00\x00\x00\x00\x00\x0f\x3c\x53\x00\x00\x00\x00\x0f\xa0\x53\x00' + msg += b'\x00\x00\x00\x10\x04\x53\x55\xaa\x00\x00\x10\x68\x53\x00\x01\x00' + msg += b'\x00\x10\xcc\x53\x00\x00\x00\x00\x11\x30\x53\x00\x00\x00\x00\x11' + msg += b'\x94\x53\x00\x00\x00\x00\x11\xf8\x53\xff\xff\x00\x00\x12\x5c\x53' + msg += b'\xff\xff\x00\x00\x12\xc0\x53\x00\x00\x00\x00\x13\x24\x53\xff\xff' + msg += b'\x00\x00\x13\x88\x53\xff\xff\x00\x00\x13\xec\x53\xff\xff\x00\x00' + msg += b'\x14\x50\x53\xff\xff\x00\x00\x14\xb4\x53\xff\xff\x00\x00\x15\x18' + msg += b'\x53\xff\xff\x00\x00\x15\x7c\x53\x00\x00\x00\x00\x27\x10\x53\x00' + msg += b'\x02\x00\x00\x27\x74\x53\x00\x3c\x00\x00\x27\xd8\x53\x00\x68\x00' + msg += b'\x00\x28\x3c\x53\x05\x00\x00\x00\x28\xa0\x46\x43\x79\x00\x00\x00' + msg += b'\x00\x29\x04\x46\x43\x48\x00\x00\x00\x00\x29\x68\x46\x42\x48\x33' + msg += b'\x33\x00\x00\x29\xcc\x46\x42\x3e\x3d\x71\x00\x00\x2a\x30\x53\x00' + msg += b'\x01\x00\x00\x2a\x94\x46\x43\x37\x00\x00\x00\x00\x2a\xf8\x46\x42' + msg += b'\xce\x00\x00\x00\x00\x2b\x5c\x53\x00\x96\x00\x00\x2b\xc0\x53\x00' + msg += b'\x10\x00\x00\x2c\x24\x46\x43\x90\x00\x00\x00\x00\x2c\x88\x46\x43' + msg += b'\x95\x00\x00\x00\x00\x2c\xec\x53\x00\x06\x00\x00\x2d\x50\x53\x00' + msg += b'\x06\x00\x00\x2d\xb4\x46\x43\x7d\x00\x00\x00\x00\x2e\x18\x46\x42' + msg += b'\x3d\xeb\x85\x00\x00\x2e\x7c\x46\x42\x3d\xeb\x85\x00\x00\x2e\xe0' + msg += b'\x53\x00\x03\x00\x00\x2f\x44\x53\x00\x03\x00\x00\x2f\xa8\x46\x42' + msg += b'\x4d\xeb\x85\x00\x00\x30\x0c\x46\x42\x4d\xeb\x85\x00\x00\x30\x70' + msg += b'\x53\x00\x03\x00\x00\x30\xd4\x53\x00\x03\x00\x00\x31\x38\x46\x42' + msg += b'\x08\x00\x00\x00\x00\x31\x9c\x53\x00\x05\x00\x00\x32\x00\x53\x04' + msg += b'\x00\x00\x00\x32\x64\x53\x00\x01\x00\x00\x32\xc8\x53\x13\x9c\x00' + msg += b'\x00\x33\x2c\x53\x0f\xa0\x00\x00\x33\x90\x53\x00\x4f\x00\x00\x33' + msg += b'\xf4\x53\x00\x66\x00\x00\x34\x58\x53\x03\xe8\x00\x00\x34\xbc\x53' + msg += b'\x04\x00\x00\x00\x35\x20\x53\x00\x00\x00\x00\x35\x84\x53\x00\x00' + msg += b'\x00\x00\x35\xe8\x53\x00\x00\x00\x00\x36\x4c\x53\x00\x00\x00\x01' + msg += b'\x38\x80\x53\x00\x02\x00\x01\x38\x81\x53\x00\x01\x00\x01\x38\x82' + msg += b'\x53\x00\x01\x00\x01\x38\x83\x53\x00\x00\x00\x00\x00\x0a\x08\x00' + msg += b'\x00\x00\x00\x00\x00\x00\x00\x14\x04\x00\x00\x00\x00\x00\x00\x00' + msg += b'\x00\x1e\x07\x00\x00\x00\x00\x00' + return msg + + @pytest.fixture def msg_inverter_ind_0w(): # Data indication with 0.5W grid output msg = b'\x00\x00\x05\x02\x10R170000000000001\x91\x04\x01\x90\x00\x01\x10R170000000000001' @@ -2151,3 +2235,34 @@ def test_timeout(config_tsun_inv1): m.modbus_polling = False assert Talent.MAX_DEF_IDLE_TIME == m._timeout() m.close() + +def test_msg_inv_replay(config_tsun_inv1, msg_inverter_ind_0w, msg_inverter_ind_new2): + '''replay must be ignores, since HA only supports realtime values''' + _ = config_tsun_inv1 + + m = MemoryStream(msg_inverter_ind_0w, (0,)) # realtime msg with 0.5W Output Power + m.append_msg(msg_inverter_ind_new2) # replay msg with 506.6W Output Power + m.db.db['grid'] = {'Output_Power': 100} + m.db.stat['proxy']['Unknown_Ctrl'] = 0 + m.db.stat['proxy']['Invalid_Data_Type'] = 0 + m.read() # read complete msg, and dispatch msg + assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 + assert m.db.stat['proxy']['Invalid_Data_Type'] == 0 + assert not m.header_valid # must be invalid, since msg was handled and buffer flushed + assert m.msg_count == 2 + assert m.msg_recvd[0]['ctrl']==145 + assert m.msg_recvd[0]['msg_id']==4 + assert m.msg_recvd[0]['header_len']==23 + assert m.msg_recvd[0]['data_len']==1263 + assert m.msg_recvd[1]['ctrl']==145 + assert m.msg_recvd[1]['msg_id']==4 + assert m.msg_recvd[1]['header_len']==23 + assert m.msg_recvd[1]['data_len']==1249 + assert m.id_str == b"R170000000000001" + assert m.unique_id == 'R170000000000001' + + assert m.db.get_db_value(Register.INVERTER_STATUS) == 1 + assert isclose(m.db.db['grid']['Output_Power'], 0.5) # must be 0.5W not 100W nor 506.6W + + m.close() + assert m.db.get_db_value(Register.INVERTER_STATUS) == 0