diff --git a/.env_example b/.env_example index 4d28078..cd6a572 100644 --- a/.env_example +++ b/.env_example @@ -6,4 +6,9 @@ PRIVAT_CONTAINER_REGISTRY=docker.io// # registry for official container (preview, rc, rel) PUBLIC_CONTAINER_REGISTRY=ghcr.io// -PUBLIC_CR_KEY= \ No newline at end of file +PUBLIC_CR_KEY= + +# define serial number of GEN3PLUS devices for systemtests +# the serialnumber are coded as 4-byte hex-strings +SOLARMAN_INV_SNR='00000000' +SOLARMAN_DCU_SNR='00000000' \ No newline at end of file diff --git a/app/Makefile b/app/Makefile index fb96df2..dff4631 100644 --- a/app/Makefile +++ b/app/Makefile @@ -22,7 +22,16 @@ dev debug: export IMAGE=$(PRIVAT_CONTAINER_REGISTRY)$(IMAGE) && \ docker buildx bake -f docker-bake.hcl $@ -preview rc rel: +rc: + @[ "${RC}" ] || ( echo ">> RC is not set"; exit 1 ) + @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)-$@$(RC) && \ + export IMAGE=$(PUBLIC_CONTAINER_REGISTRY)$(IMAGE) && \ + docker buildx bake -f docker-bake.hcl $@ + +preview rel: @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)" diff --git a/app/docu/proxy_2.svg b/app/docu/proxy_2.svg index 232983f..0298327 100644 --- a/app/docu/proxy_2.svg +++ b/app/docu/proxy_2.svg @@ -4,368 +4,380 @@ - + G - + A0 - - - -Example of -instantiation for a -GEN3 inverter! + + + +Example of +instantiation for a +GEN3 inverter! A1 - -<<AbstractIterMeta>> - - -__iter__() - - - -A14 - -<<ProtocolIfc>> - -_registry - -close() - - - -A1->A14 - - - - - -A2 - -InverterG3 - -addr -remote:StreamPtr -local:StreamPtr - -create_remote() -close() - - - -A3 - -local:StreamPtr - - - -A2->A3 - - - - - - -A4 - -remote:StreamPtr - - - -A2->A4 - - - - - - -A8 - -AsyncStreamServer - -create_remote - -<async>server_loop() -<async>_async_forward() -<async>publish_outstanding_mqtt() -close() - - - -A3->A8 - - - - - - -A9 - -AsyncStreamClient - - -<async>client_loop() -<async>_async_forward()) - - - -A4->A9 - - -0..1 - - - -A5 - -<<AsyncIfc>> - - -set_node_id() -get_conn_no() -tx_add() -tx_flush() -tx_get() -tx_peek() -tx_log() -tx_clear() -tx_len() -fwd_add() -fwd_log() -rx_get() -rx_peek() -rx_log() -rx_clear() -rx_len() -rx_set_cb() -prot_set_timeout_cb() - - - -A6 - -AsyncIfcImpl - -fwd_fifo:ByteFifo -tx_fifo:ByteFifo -rx_fifo:ByteFifo -conn_no:Count -node_id -timeout_cb - - - -A5->A6 - - - - - -A7 - -AsyncStream - -reader -writer -addr -r_addr -l_addr - -<async>loop -disc() -close() -healthy() -__async_read() -__async_write() -__async_forward() - - - -A6->A7 - - - - - -A7->A8 - - - - - -A7->A9 - - - - - -A10 - -Talent - -conn_no -addr -await_conn_resp_cnt -id_str -contact_name -contact_mail -db:InfosG3 -mb:Modbus -switch - -msg_contact_info() -msg_ota_update() -msg_get_time() -msg_collector_data() -msg_inverter_data() -msg_unknown() -healthy() -close() - - - -A10->A3 - - - - - - -A10->A4 - - -0..1 - - - -A12 - -InfosG3 - - -ha_confs() -parse() - - - -A10->A12 - - - - - -A11 - -Infos - -stat -new_stat_data -info_dev - -static_init() -dev_value() -inc_counter() -dec_counter() -ha_proxy_conf -ha_conf -ha_remove -update_db -set_db_def_value -get_db_value -ignore_this_device - - - -A11->A12 - - - - - -A13 - -Message - -server_side:bool -mb:Modbus -ifc:AsyncIfc -node_id -header_valid:bool -header_len -data_len -unique_id -sug_area:str -new_data:dict -state:State -shutdown_started:bool -modbus_elms -mb_timer:Timer -mb_timeout -mb_first_timeout -modbus_polling:bool - -_set_mqtt_timestamp() -_timeout() -_send_modbus_cmd() -<async> end_modbus_cmd() -close() -inc_counter() -dec_counter() - - - -A13->A5 - - -use - - - -A13->A10 - - - - - -A14->A13 - - + +<<AbstractIterMeta>> + + +__iter__() A15 - -Modbus - -que -snd_handler -rsp_handler -timeout -max_retires -last_xxx -err -retry_cnt -req_pend -tim - -build_msg() -recv_req() -recv_resp() -close() + +<<ProtocolIfc>> + +_registry + +close() - + + +A1->A15 + + + + + +A2 + +InverterBase + +addr +remote:StreamPtr +local:StreamPtr + +create_remote() +close() + + + +A3 + +InverterG3 + + + +A2->A3 + + + + + +A4 + +local:StreamPtr + + + +A2->A4 + + + + + + +A5 + +remote:StreamPtr + + + +A2->A5 + + + + + + +A9 + +AsyncStreamServer + +create_remote + +<async>server_loop() +<async>_async_forward() +<async>publish_outstanding_mqtt() +close() + + + +A4->A9 + + + + + + +A10 + +AsyncStreamClient + + +<async>client_loop() +<async>_async_forward()) + + + +A5->A10 + + +0..1 + + + +A6 + +<<AsyncIfc>> + + +set_node_id() +get_conn_no() +tx_add() +tx_flush() +tx_get() +tx_peek() +tx_log() +tx_clear() +tx_len() +fwd_add() +fwd_log() +rx_get() +rx_peek() +rx_log() +rx_clear() +rx_len() +rx_set_cb() +prot_set_timeout_cb() + + + +A7 + +AsyncIfcImpl + +fwd_fifo:ByteFifo +tx_fifo:ByteFifo +rx_fifo:ByteFifo +conn_no:Count +node_id +timeout_cb + + + +A6->A7 + + + + + +A8 + +AsyncStream + +reader +writer +addr +r_addr +l_addr + +<async>loop +disc() +close() +healthy() +__async_read() +__async_write() +__async_forward() + + + +A7->A8 + + + + + +A8->A9 + + + + + +A8->A10 + + + + + +A11 + +Talent + +conn_no +addr +await_conn_resp_cnt +id_str +contact_name +contact_mail +db:InfosG3 +mb:Modbus +switch + +msg_contact_info() +msg_ota_update() +msg_get_time() +msg_collector_data() +msg_inverter_data() +msg_unknown() +healthy() +close() + + + +A11->A4 + + + + + + +A11->A5 + + +0..1 + + + +A13 + +InfosG3 + + +ha_confs() +parse() + + + +A11->A13 + + + + + +A12 + +Infos + +stat +new_stat_data +info_dev + +static_init() +dev_value() +inc_counter() +dec_counter() +ha_proxy_conf +ha_conf +ha_remove +update_db +set_db_def_value +get_db_value +ignore_this_device + + + +A12->A13 + + + + + +A14 + +Message + +server_side:bool +mb:Modbus +ifc:AsyncIfc +node_id +header_valid:bool +header_len +data_len +unique_id +sug_area:str +new_data:dict +state:State +shutdown_started:bool +modbus_elms +mb_timer:Timer +mb_timeout +mb_first_timeout +modbus_polling:bool + +_set_mqtt_timestamp() +_timeout() +_send_modbus_cmd() +<async> end_modbus_cmd() +close() +inc_counter() +dec_counter() + + + +A14->A6 + + +use + + -A15->A13 - - -has -0..1 +A14->A11 + + + + + +A15->A14 + + + + + +A16 + +Modbus + +que +snd_handler +rsp_handler +timeout +max_retires +last_xxx +err +retry_cnt +req_pend +tim + +build_msg() +recv_req() +recv_resp() +close() + + + +A16->A14 + + +has +0..1 diff --git a/app/docu/proxy_2.yuml b/app/docu/proxy_2.yuml index 5138428..6c50022 100644 --- a/app/docu/proxy_2.yuml +++ b/app/docu/proxy_2.yuml @@ -5,10 +5,10 @@ [note: Example of instantiation for a GEN3 inverter!{bg:cornsilk}] [<>||__iter__()] -[InverterG3|addr;remote:StreamPtr;local:StreamPtr|create_remote();;close()] -[InverterG3]++->[local:StreamPtr] -[InverterG3]++->[remote:StreamPtr] - +[InverterBase|addr;remote:StreamPtr;local:StreamPtr|create_remote();;close()] +[InverterBase]^[InverterG3] +[InverterBase]++->[local:StreamPtr] +[InverterBase]++->[remote:StreamPtr] [<>||set_node_id();get_conn_no();;tx_add();tx_flush();tx_get();tx_peek();tx_log();tx_clear();tx_len();;fwd_add();fwd_log();rx_get();rx_peek();rx_log();rx_clear();rx_len();rx_set_cb();;prot_set_timeout_cb()] [AsyncIfcImpl|fwd_fifo:ByteFifo;tx_fifo:ByteFifo;rx_fifo:ByteFifo;conn_no:Count;node_id;timeout_cb] [AsyncStream|reader;writer;addr;r_addr;l_addr|;loop;disc();close();healthy();;__async_read();__async_write();__async_forward()] diff --git a/app/docu/proxy_3.svg b/app/docu/proxy_3.svg index 8be4969..d429bed 100644 --- a/app/docu/proxy_3.svg +++ b/app/docu/proxy_3.svg @@ -4,364 +4,379 @@ - + G - + A0 - - - -Example of -instantiation for a -GEN3PLUS inverter! + + + +Example of +instantiation for a +GEN3PLUS inverter! A1 - -<<AbstractIterMeta>> - - -__iter__() - - - -A14 - -<<ProtocolIfc>> - -_registry - -close() - - - -A1->A14 - - - - - -A2 - -InverterG3P - -addr -remote:StreamPtr -local:StreamPtr - -create_remote() -close() - - - -A3 - -local:StreamPtr - - - -A2->A3 - - - - - - -A4 - -remote:StreamPtr - - - -A2->A4 - - - - - - -A8 - -AsyncStreamServer - -create_remote - -<async>server_loop() -<async>_async_forward() -<async>publish_outstanding_mqtt() -close() - - - -A3->A8 - - - - - - -A9 - -AsyncStreamClient - - -<async>client_loop() -<async>_async_forward()) - - - -A4->A9 - - -0..1 - - - -A5 - -<<AsyncIfc>> - - -set_node_id() -get_conn_no() -tx_add() -tx_flush() -tx_get() -tx_peek() -tx_log() -tx_clear() -tx_len() -fwd_add() -fwd_log() -rx_get() -rx_peek() -rx_log() -rx_clear() -rx_len() -rx_set_cb() -prot_set_timeout_cb() - - - -A6 - -AsyncIfcImpl - -fwd_fifo:ByteFifo -tx_fifo:ByteFifo -rx_fifo:ByteFifo -conn_no:Count -node_id -timeout_cb - - - -A5->A6 - - - - - -A7 - -AsyncStream - -reader -writer -addr -r_addr -l_addr - -<async>loop -disc() -close() -healthy() -__async_read() -__async_write() -__async_forward() - - - -A6->A7 - - - - - -A7->A8 - - - - - -A7->A9 - - - - - -A10 - -SolarmanV5 - -conn_no -addr -control -serial -snr -db:InfosG3P -switch - -msg_unknown() -healthy() -close() - - - -A10->A3 - - - - - - -A10->A4 - - -0..1 - - - -A12 - -InfosG3P - -client_mode:bool - -ha_confs() -parse() -calc() -build() - - - -A10->A12 - - - - - -A11 - -Infos - -stat -new_stat_data -info_dev - -static_init() -dev_value() -inc_counter() -dec_counter() -ha_proxy_conf -ha_conf -ha_remove -update_db -set_db_def_value -get_db_value -ignore_this_device - - - -A11->A12 - - - - - -A13 - -Message - -server_side:bool -mb:Modbus -ifc:AsyncIfc -node_id -header_valid:bool -header_len -data_len -unique_id -sug_area:str -new_data:dict -state:State -shutdown_started:bool -modbus_elms -mb_timer:Timer -mb_timeout -mb_first_timeout -modbus_polling:bool - -_set_mqtt_timestamp() -_timeout() -_send_modbus_cmd() -<async> end_modbus_cmd() -close() -inc_counter() -dec_counter() - - - -A13->A5 - - -use - - - -A13->A10 - - - - - -A14->A13 - - + +<<AbstractIterMeta>> + + +__iter__() A15 - -Modbus - -que -snd_handler -rsp_handler -timeout -max_retires -last_xxx -err -retry_cnt -req_pend -tim - -build_msg() -recv_req() -recv_resp() -close() + +<<ProtocolIfc>> + +_registry + +close() - + + +A1->A15 + + + + + +A2 + +InverterBase + +addr +remote:StreamPtr +local:StreamPtr + +create_remote() +close() + + + +A3 + +InverterG3P + +forward_at_cmd_resp + + + +A2->A3 + + + + + +A4 + +local:StreamPtr + + + +A2->A4 + + + + + + +A5 + +remote:StreamPtr + + + +A2->A5 + + + + + + +A9 + +AsyncStreamServer + +create_remote + +<async>server_loop() +<async>_async_forward() +<async>publish_outstanding_mqtt() +close() + + + +A4->A9 + + + + + + +A10 + +AsyncStreamClient + + +<async>client_loop() +<async>_async_forward()) + + + +A5->A10 + + +0..1 + + + +A6 + +<<AsyncIfc>> + + +set_node_id() +get_conn_no() +tx_add() +tx_flush() +tx_get() +tx_peek() +tx_log() +tx_clear() +tx_len() +fwd_add() +fwd_log() +rx_get() +rx_peek() +rx_log() +rx_clear() +rx_len() +rx_set_cb() +prot_set_timeout_cb() + + + +A7 + +AsyncIfcImpl + +fwd_fifo:ByteFifo +tx_fifo:ByteFifo +rx_fifo:ByteFifo +conn_no:Count +node_id +timeout_cb + + + +A6->A7 + + + + + +A8 + +AsyncStream + +reader +writer +addr +r_addr +l_addr + +<async>loop +disc() +close() +healthy() +__async_read() +__async_write() +__async_forward() + + + +A7->A8 + + + + + +A8->A9 + + + + + +A8->A10 + + + + + +A11 + +SolarmanV5 + +conn_no +addr +inverter:InverterG3P +control +serial +snr +db:InfosG3P +switch + +msg_unknown() +healthy() +close() + + + +A11->A4 + + + + + + +A11->A5 + + +0..1 + + + +A13 + +InfosG3P + +client_mode:bool + +ha_confs() +parse() +calc() +build() + + + +A11->A13 + + + + + +A12 + +Infos + +stat +new_stat_data +info_dev + +static_init() +dev_value() +inc_counter() +dec_counter() +ha_proxy_conf +ha_conf +ha_remove +update_db +set_db_def_value +get_db_value +ignore_this_device + + + +A12->A13 + + + + + +A14 + +Message + +server_side:bool +mb:Modbus +ifc:AsyncIfc +node_id +header_valid:bool +header_len +data_len +unique_id +sug_area:str +new_data:dict +state:State +shutdown_started:bool +modbus_elms +mb_timer:Timer +mb_timeout +mb_first_timeout +modbus_polling:bool + +_set_mqtt_timestamp() +_timeout() +_send_modbus_cmd() +<async> end_modbus_cmd() +close() +inc_counter() +dec_counter() + + + +A14->A6 + + +use + + -A15->A13 - - -has -0..1 +A14->A11 + + + + + +A15->A14 + + + + + +A16 + +Modbus + +que +snd_handler +rsp_handler +timeout +max_retires +last_xxx +err +retry_cnt +req_pend +tim + +build_msg() +recv_req() +recv_resp() +close() + + + +A16->A14 + + +has +0..1 diff --git a/app/docu/proxy_3.yuml b/app/docu/proxy_3.yuml index 3703658..18bcec1 100644 --- a/app/docu/proxy_3.yuml +++ b/app/docu/proxy_3.yuml @@ -5,9 +5,10 @@ [note: Example of instantiation for a GEN3PLUS inverter!{bg:cornsilk}] [<>||__iter__()] -[InverterG3P|addr;remote:StreamPtr;local:StreamPtr|create_remote();;close()] -[InverterG3P]++->[local:StreamPtr] -[InverterG3P]++->[remote:StreamPtr] +[InverterBase|addr;remote:StreamPtr;local:StreamPtr|create_remote();;close()] +[InverterBase]^[InverterG3P|forward_at_cmd_resp;] +[InverterBase]++->[local:StreamPtr] +[InverterBase]++->[remote:StreamPtr] [<>||set_node_id();get_conn_no();;tx_add();tx_flush();tx_get();tx_peek();tx_log();tx_clear();tx_len();;fwd_add();fwd_log();rx_get();rx_peek();rx_log();rx_clear();rx_len();rx_set_cb();;prot_set_timeout_cb()] [AsyncIfcImpl|fwd_fifo:ByteFifo;tx_fifo:ByteFifo;rx_fifo:ByteFifo;conn_no:Count;node_id;timeout_cb] @@ -19,7 +20,7 @@ [AsyncStream]^[AsyncStreamServer] [AsyncStream]^[AsyncStreamClient] -[SolarmanV5|conn_no;addr;;control;serial;snr;db:InfosG3P;switch|msg_unknown();;healthy();close()] +[SolarmanV5|conn_no;addr;inverter:InverterG3P;control;serial;snr;db:InfosG3P;switch|msg_unknown();;healthy();close()] [SolarmanV5]<-++[local:StreamPtr] [local:StreamPtr]++->[AsyncStreamServer] [SolarmanV5]<-0..1[remote:StreamPtr] diff --git a/app/requirements-test.txt b/app/requirements-test.txt index dd4dfbd..36de7d0 100644 --- a/app/requirements-test.txt +++ b/app/requirements-test.txt @@ -1,7 +1,7 @@ flake8==7.2.0 pytest==8.3.5 pytest-asyncio==0.26.0 - pytest-cov==6.1.0 + pytest-cov==6.1.1 python-dotenv==1.1.0 mock==5.2.0 coverage==7.8.0 diff --git a/app/src/gen3/talent.py b/app/src/gen3/talent.py index e799a36..292dfdf 100644 --- a/app/src/gen3/talent.py +++ b/app/src/gen3/talent.py @@ -34,10 +34,11 @@ class Control: class Talent(Message): TXT_UNKNOWN_CTRL = 'Unknown Ctrl' - def __init__(self, addr, ifc: "AsyncIfc", server_side: bool, + def __init__(self, inverter, addr, ifc: "AsyncIfc", server_side: bool, client_mode: bool = False, id_str=b''): super().__init__('G3', ifc, server_side, self.send_modbus_cb, mb_timeout=15) + _ = inverter ifc.rx_set_cb(self.read) ifc.prot_set_timeout_cb(self._timeout) ifc.prot_set_init_new_client_conn_cb(self._init_new_client_conn) diff --git a/app/src/gen3plus/infos_g3p.py b/app/src/gen3plus/infos_g3p.py index 4cbe08c..470c695 100644 --- a/app/src/gen3plus/infos_g3p.py +++ b/app/src/gen3plus/infos_g3p.py @@ -19,6 +19,19 @@ class RegisterFunc: result += prod return result + @staticmethod + def cmp_values(info: Infos, params: map) -> None | int: + try: + val = info.get_db_value(params['reg']) + if val < params['cmp_val']: + return params['res'][0] + if val == params['cmp_val']: + return params['res'][1] + return params['res'][2] + except Exception: + pass + return None + class RegisterMap: # make the class read/only by using __slots__ @@ -131,52 +144,54 @@ class RegisterMap: 0x4201000c: {'reg': Register.SENSOR_LIST, 'fmt': ' Batterie Status: <0(Discharging), 0(Static), 0>(Loading) 0x42010044: {'reg': Register.BATT_SOC, 'fmt': '!H', 'ratio': 0.01}, # noqa: E501, state of charge (SOC) in percent - 0x42010046: {'reg': Register.BATT_CELL1_VOLT, 'fmt': '!h', 'ratio': 0.001}, # noqa: E501 - 0x42010048: {'reg': Register.BATT_CELL2_VOLT, 'fmt': '!h', 'ratio': 0.001}, # noqa: E501 - 0x4201004a: {'reg': Register.BATT_CELL3_VOLT, 'fmt': '!h', 'ratio': 0.001}, # noqa: E501 - 0x4201004c: {'reg': Register.BATT_CELL4_VOLT, 'fmt': '!h', 'ratio': 0.001}, # noqa: E501 - 0x4201004e: {'reg': Register.BATT_CELL5_VOLT, 'fmt': '!h', 'ratio': 0.001}, # noqa: E501 - 0x42010050: {'reg': Register.BATT_CELL6_VOLT, 'fmt': '!h', 'ratio': 0.001}, # noqa: E501 - 0x42010052: {'reg': Register.BATT_CELL7_VOLT, 'fmt': '!h', 'ratio': 0.001}, # noqa: E501 - 0x42010054: {'reg': Register.BATT_CELL8_VOLT, 'fmt': '!h', 'ratio': 0.001}, # noqa: E501 - 0x42010056: {'reg': Register.BATT_CELL9_VOLT, 'fmt': '!h', 'ratio': 0.001}, # noqa: E501 - 0x42010058: {'reg': Register.BATT_CELL10_VOLT, 'fmt': '!h', 'ratio': 0.001}, # noqa: E501 - 0x4201005a: {'reg': Register.BATT_CELL11_VOLT, 'fmt': '!h', 'ratio': 0.001}, # noqa: E501 - 0x4201005c: {'reg': Register.BATT_CELL12_VOLT, 'fmt': '!h', 'ratio': 0.001}, # noqa: E501 - 0x4201005e: {'reg': Register.BATT_CELL13_VOLT, 'fmt': '!h', 'ratio': 0.001}, # noqa: E501 - 0x42010060: {'reg': Register.BATT_CELL14_VOLT, 'fmt': '!h', 'ratio': 0.001}, # noqa: E501 - 0x42010062: {'reg': Register.BATT_CELL15_VOLT, 'fmt': '!h', 'ratio': 0.001}, # noqa: E501 - 0x42010064: {'reg': Register.BATT_CELL16_VOLT, 'fmt': '!h', 'ratio': 0.001}, # noqa: E501 - - 0x42010066: {'reg': Register.BATT_TEMP_1, 'fmt': '!h'}, # noqa: E501 - 0x42010068: {'reg': Register.BATT_TEMP_2, 'fmt': '!h'}, # noqa: E501 - 0x4201006a: {'reg': Register.BATT_TEMP_3, 'fmt': '!h'}, # noqa: E501 - 0x4201006c: {'reg': Register.BATT_OUT_VOLT, 'fmt': '!h', 'ratio': 0.01}, # noqa: E501 - 0x4201006e: {'reg': Register.BATT_OUT_CUR, 'fmt': '!h', 'ratio': 0.01}, # noqa: E501 - 0x42010070: {'reg': Register.BATT_OUT_STATUS, 'fmt': '!h'}, # noqa: E501, state of output value 0 or 1 - 0x42010072: {'reg': Register.BATT_TEMP_4, 'fmt': '!h'}, # noqa: E501 controller temp - 0x42010074: {'reg': Register.BATT_74, 'fmt': '!h'}, # noqa: E501, control input 0..2048 - 0x42010076: {'reg': Register.BATT_76, 'fmt': '!h'}, # noqa: E501 - 0x42010078: {'reg': Register.BATT_78, 'fmt': '!h'}, # noqa: E501 + 0x42010046: {'reg': Register.BATT_CELL1_VOLT, 'fmt': '!H', 'ratio': 0.001}, # noqa: E501 + 0x42010048: {'reg': Register.BATT_CELL2_VOLT, 'fmt': '!H', 'ratio': 0.001}, # noqa: E501 + 0x4201004a: {'reg': Register.BATT_CELL3_VOLT, 'fmt': '!H', 'ratio': 0.001}, # noqa: E501 + 0x4201004c: {'reg': Register.BATT_CELL4_VOLT, 'fmt': '!H', 'ratio': 0.001}, # noqa: E501 + 0x4201004e: {'reg': Register.BATT_CELL5_VOLT, 'fmt': '!H', 'ratio': 0.001}, # noqa: E501 + 0x42010050: {'reg': Register.BATT_CELL6_VOLT, 'fmt': '!H', 'ratio': 0.001}, # noqa: E501 + 0x42010052: {'reg': Register.BATT_CELL7_VOLT, 'fmt': '!H', 'ratio': 0.001}, # noqa: E501 + 0x42010054: {'reg': Register.BATT_CELL8_VOLT, 'fmt': '!H', 'ratio': 0.001}, # noqa: E501 + 0x42010056: {'reg': Register.BATT_CELL9_VOLT, 'fmt': '!H', 'ratio': 0.001}, # noqa: E501 + 0x42010058: {'reg': Register.BATT_CELL10_VOLT, 'fmt': '!H', 'ratio': 0.001}, # noqa: E501 + 0x4201005a: {'reg': Register.BATT_CELL11_VOLT, 'fmt': '!H', 'ratio': 0.001}, # noqa: E501 + 0x4201005c: {'reg': Register.BATT_CELL12_VOLT, 'fmt': '!H', 'ratio': 0.001}, # noqa: E501 + 0x4201005e: {'reg': Register.BATT_CELL13_VOLT, 'fmt': '!H', 'ratio': 0.001}, # noqa: E501 + 0x42010060: {'reg': Register.BATT_CELL14_VOLT, 'fmt': '!H', 'ratio': 0.001}, # noqa: E501 + 0x42010062: {'reg': Register.BATT_CELL15_VOLT, 'fmt': '!H', 'ratio': 0.001}, # noqa: E501 + 0x42010064: {'reg': Register.BATT_CELL16_VOLT, 'fmt': '!H', 'ratio': 0.001}, # noqa: E501H + 0x42010066: {'reg': Register.BATT_TEMP_1, 'fmt': '!h'}, # noqa: E501 Cell Temperture 1 + 0x42010068: {'reg': Register.BATT_TEMP_2, 'fmt': '!h'}, # noqa: E501 Cell Temperture 2 + 0x4201006a: {'reg': Register.BATT_TEMP_3, 'fmt': '!h'}, # noqa: E501 Cell Temperture 3 + 0x4201006c: {'reg': Register.BATT_OUT_VOLT, 'fmt': '!H', 'ratio': 0.01}, # noqa: E501 Output Voltage + 0x4201006e: {'reg': Register.BATT_OUT_CUR, 'fmt': '!H', 'ratio': 0.01}, # noqa: E501 Output Current + 0x42010070: {'reg': Register.BATT_OUT_STATUS, 'fmt': '!H'}, # noqa: E501 Output Working Status: 0(Standby), 1(Work) + 0x42010072: {'reg': Register.BATT_TEMP_4, 'fmt': '!h'}, # noqa: E50, Environment temp + 0x42010074: {'reg': Register.BATT_ALARM, 'fmt': '!H'}, # noqa: E501 Warning Alarmcode 1, Bit 0..15 + 0x42010076: {'reg': Register.BATT_HW_VERS, 'fmt': '!h'}, # noqa: E501 hardware version + 0x42010078: {'reg': Register.BATT_SW_VERS, 'fmt': '!h'}, # noqa: E501 software main version 'calc': { - 1: {'reg': Register.BATT_PV_PWR, 'func': RegisterFunc.prod_sum, # noqa: E501 + 1: {'reg': Register.BATT_PV_PWR, 'func': RegisterFunc.prod_sum, # noqa: E501 Generated Power 'params': [[Register.BATT_PV1_VOLT, Register.BATT_PV1_CUR], [Register.BATT_PV2_VOLT, Register.BATT_PV2_CUR]]}, 2: {'reg': Register.BATT_PWR, 'func': RegisterFunc.prod_sum, # noqa: E501 'params': [[Register.BATT_VOLT, Register.BATT_CUR]]}, - 3: {'reg': Register.BATT_OUT_PWR, 'func': RegisterFunc.prod_sum, # noqa: E501 + 3: {'reg': Register.BATT_OUT_PWR, 'func': RegisterFunc.prod_sum, # noqa: E501 Supply Power => Power Supply State: 0(Idle), 0>(Power Supply) 'params': [[Register.BATT_OUT_VOLT, Register.BATT_OUT_CUR]]}, + 4: {'reg': Register.BATT_PWR_SUPL_STATE, 'func': RegisterFunc.cmp_values, # noqa: E501 + 'params': {'reg': Register.BATT_OUT_PWR, 'cmp_val': 0, 'res': [0, 0, 1]}}, # noqa: E501 + 5: {'reg': Register.BATT_STATUS, 'func': RegisterFunc.cmp_values, # noqa: E501 + 'params': {'reg': Register.BATT_CUR, 'cmp_val': 0.0, 'res': [0, 1, 2]}} # noqa: E501 } } diff --git a/app/src/gen3plus/inverter_g3p.py b/app/src/gen3plus/inverter_g3p.py index f3680c9..6a3f55d 100644 --- a/app/src/gen3plus/inverter_g3p.py +++ b/app/src/gen3plus/inverter_g3p.py @@ -8,6 +8,15 @@ from gen3plus.solarman_emu import SolarmanEmu class InverterG3P(InverterBase): def __init__(self, reader: StreamReader, writer: StreamWriter, client_mode: bool = False): + # shared value between both inverter connections + self.forward_at_cmd_resp = False + '''Flag if response for the last at command must be send to the cloud. + + False: send result only to the MQTT broker, cause the AT+ command + came from there + True: send response packet to the cloud, cause the AT+ command + came from the cloud''' + remote_prot = None if client_mode: remote_prot = SolarmanEmu diff --git a/app/src/gen3plus/solarman_emu.py b/app/src/gen3plus/solarman_emu.py index 7462388..93bb874 100644 --- a/app/src/gen3plus/solarman_emu.py +++ b/app/src/gen3plus/solarman_emu.py @@ -10,11 +10,12 @@ logger = logging.getLogger('msg') class SolarmanEmu(SolarmanBase): - def __init__(self, addr, ifc: "AsyncIfc", + def __init__(self, inverter, addr, ifc: "AsyncIfc", server_side: bool, client_mode: bool): super().__init__(addr, ifc, server_side=False, _send_modbus_cb=None, mb_timeout=8) + _ = inverter logging.debug('SolarmanEmu.init()') self.db = ifc.remote.stream.db self.snr = ifc.remote.stream.snr diff --git a/app/src/gen3plus/solarman_v5.py b/app/src/gen3plus/solarman_v5.py index 9b4bee8..7c8b997 100644 --- a/app/src/gen3plus/solarman_v5.py +++ b/app/src/gen3plus/solarman_v5.py @@ -253,13 +253,13 @@ class SolarmanV5(SolarmanBase): HDR_FMT = ' we get a memory leak + self.inverter = None self.switch.clear() self.log_lvl.clear() super().close() @@ -519,7 +520,7 @@ class SolarmanV5(SolarmanBase): await Proxy.mqtt.publish(f'{Proxy.entity_prfx}{node_id}{key}', data_json) # noqa: E501 return - self.forward_at_cmd_resp = False + self.inverter.forward_at_cmd_resp = False self._build_header(0x4510) self.ifc.tx_add(struct.pack(f' int: ftype = self.ifc.rx_peek()[self.header_len] - if ftype == self.AT_CMD: - if self.forward_at_cmd_resp: + if ftype == self.AT_CMD or \ + ftype == self.AT_CMD_RSP: + if self.inverter.forward_at_cmd_resp: return logging.INFO return logging.DEBUG elif ftype == self.MB_RTU_CMD \ @@ -680,7 +682,7 @@ class SolarmanV5(SolarmanBase): ftype = data[0] if ftype == self.AT_CMD or \ ftype == self.AT_CMD_RSP: - if not self.forward_at_cmd_resp: + if not self.inverter.forward_at_cmd_resp: data_json = data[14:].decode("utf-8") node_id = self.node_id key = 'at_resp' diff --git a/app/src/infos.py b/app/src/infos.py index 3bc6a4c..e0d4226 100644 --- a/app/src/infos.py +++ b/app/src/infos.py @@ -125,10 +125,9 @@ class Register(Enum): BATT_PV1_CUR = 1001 BATT_PV2_VOLT = 1002 BATT_PV2_CUR = 1003 - BATT_38 = 1004 - BATT_TOTAL_GEN = 1005 - BATT_STATUS_1 = 1006 - BATT_STATUS_2 = 1007 + BATT_TOTAL_CHARG = 1005 + BATT_PV1_STATUS = 1006 + BATT_PV2_STATUS = 1007 BATT_VOLT = 1010 BATT_CUR = 1011 BATT_SOC = 1012 @@ -155,12 +154,14 @@ class Register(Enum): BATT_OUT_CUR = 1033 BATT_OUT_STATUS = 1034 BATT_TEMP_4 = 1035 - BATT_74 = 1036 - BATT_76 = 1037 - BATT_78 = 1038 + BATT_ALARM = 1036 + BATT_HW_VERS = 1037 + BATT_SW_VERS = 1038 BATT_PV_PWR = 1040 BATT_PWR = 1041 BATT_OUT_PWR = 1042 + BATT_PWR_SUPL_STATE = 1043 + BATT_STATUS = 1044 TEST_VAL_0 = 2000 TEST_VAL_1 = 2001 @@ -325,9 +326,11 @@ class Infos: SOLAR_POWER_VAR = 'mdi:solar-power-variant' SOLAR_POWER = 'mdi:solar-power' WIFI = 'mdi:wifi' + ALARM_LIGHT = 'mdi:alarm-light' UPDATE = 'mdi:update' DAILY_GEN = 'Daily Generation' TOTAL_GEN = 'Total Generation' + TOTAL_CHARG = 'Total Charging Energy' FMT_INT = '| int' FMT_FLOAT = '| float' FMT_STRING_SEC = '| string + " s"' @@ -367,7 +370,7 @@ class Infos: '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 - 'batterie': {'via': 'controller', 'name': 'Batterie', 'mdl': Register.EQUIPMENT_MODEL, 'mf': Register.MANUFACTURER, 'sw': Register.VERSION, 'sn': Register.SERIAL_NUMBER}, # noqa: E501 + 'batterie': {'via': 'controller', 'name': 'Batterie', 'mdl': Register.EQUIPMENT_MODEL, 'mf': Register.MANUFACTURER, 'hw': Register.BATT_HW_VERS, 'sw': Register.BATT_SW_VERS, '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 } @@ -375,9 +378,11 @@ 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 __work_mode_val_tpl = "{%set mode = ['Normal-Mode', 'Aging-Mode', 'ATE-Mode', 'Shielding GFDI', 'DTU-Mode'] %}{{mode[value_json['Work_Mode']|int(0)]|default(value_json['Work_Mode'])}}" # 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 - __mppt1_status_type_val_tpl = "{%set mppt_status = ['Locked', 'On', 'Off'] %}{{mppt_status[value_json['Status_1']|int(0)]|default(value_json['Status_1'])}}" # noqa: E501 - __mppt2_status_type_val_tpl = "{%set mppt_status = ['Locked', 'On', 'Off'] %}{{mppt_status[value_json['Status_2']|int(0)]|default(value_json['Status_2'])}}" # noqa: E501 - __out_status_type_val_tpl = "{%set out_status = ['Off', 'On'] %}{{out_status[value_json['out']['Out_Status']|int(0)]|default(value_json['out']['Out_Status'])}}" # noqa: E501 + __mppt1_status_type_val_tpl = "{%set mppt_status = ['Standby', 'On', 'Off'] %}{{mppt_status[value_json['pv1']['MPPT-Status']|int(0)]|default(value_json['pv1']['MPPT-Status'])}}" # noqa: E501 + __mppt2_status_type_val_tpl = "{%set mppt_status = ['Standby', 'On', 'Off'] %}{{mppt_status[value_json['pv2']['MPPT-Status']|int(0)]|default(value_json['pv2']['MPPT-Status'])}}" # noqa: E501 + __supply_status_type_val_tpl = "{%set supply_status = ['Idle', 'Power-Supply'] %}{{supply_status[value_json['out']['Suppl_State']|int(0)]|default(value_json['out']['Suppl_State'])}}" # noqa: E501 + __batt_status_type_val_tpl = "{%set batt_status = ['Discharging', 'Static', 'Loading'] %}{{batt_status[value_json['batt']['Batt_State']|int(0)]|default(value_json['batt']['Batt_State'])}}" # noqa: E501 + __out_status_type_val_tpl = "{%set out_status = ['Standby', 'On'] %}{{out_status[value_json['out']['Out_Status']|int(0)]|default(value_json['out']['Out_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 @@ -399,52 +404,52 @@ class Infos: {% set result = 'noAlarm'%} {%else%} {% set result = '' %} - {% if val_int | bitwise_and(1)%} + {% if val_int | bitwise_and(0x0001)%} {% set result = result + 'HBridgeFault, '%} {% endif %} - {% if val_int | bitwise_and(2)%} + {% if val_int | bitwise_and(0x0002)%} {% set result = result + 'DriVoltageFault, '%} {% endif %} - {% if val_int | bitwise_and(3)%} + {% if val_int | bitwise_and(0x0004)%} {% set result = result + 'GFDI-Fault, '%} {% endif %} - {% if val_int | bitwise_and(4)%} + {% if val_int | bitwise_and(0x0008)%} {% set result = result + 'OverTemp, '%} {% endif %} - {% if val_int | bitwise_and(5)%} + {% if val_int | bitwise_and(0x0010)%} {% set result = result + 'CommLose, '%} {% endif %} - {% if val_int | bitwise_and(6)%} + {% if val_int | bitwise_and(0x0020)%} {% set result = result + 'Bit6, '%} {% endif %} - {% if val_int | bitwise_and(7)%} + {% if val_int | bitwise_and(0x0040)%} {% set result = result + 'Bit7, '%} {% endif %} - {% if val_int | bitwise_and(8)%} + {% if val_int | bitwise_and(0x0080)%} {% set result = result + 'EEPROM-Fault, '%} {% endif %} - {% if val_int | bitwise_and(9)%} + {% if val_int | bitwise_and(0x0100)%} {% set result = result + 'NoUtility, '%} {% endif %} - {% if val_int | bitwise_and(10)%} + {% if val_int | bitwise_and(0x0200)%} {% set result = result + 'VG_Offset, '%} {% endif %} - {% if val_int | bitwise_and(11)%} + {% if val_int | bitwise_and(0x0400)%} {% set result = result + 'Relais_Open, '%} {% endif %} - {% if val_int | bitwise_and(12)%} + {% if val_int | bitwise_and(0x0800)%} {% set result = result + 'Relais_Short, '%} {% endif %} - {% if val_int | bitwise_and(13)%} + {% if val_int | bitwise_and(0x1000)%} {% set result = result + 'GridVoltOverRating, '%} {% endif %} - {% if val_int | bitwise_and(14)%} + {% if val_int | bitwise_and(0x2000)%} {% set result = result + 'GridVoltUnderRating, '%} {% endif %} - {% if val_int | bitwise_and(15)%} + {% if val_int | bitwise_and(0x4000)%} {% set result = result + 'GridFreqOverRating, '%} {% endif %} - {% if val_int | bitwise_and(16)%} + {% if val_int | bitwise_and(0x8000)%} {% set result = result + 'GridFreqUnderRating, '%} {% endif %} {% endif %} @@ -461,42 +466,104 @@ class Infos: {% set result = 'noFault'%} {%else%} {% set result = '' %} - {% if val_int | bitwise_and(1)%} + {% if val_int | bitwise_and(0x0001)%} {% set result = result + 'PVOV-Fault (PV OverVolt), '%} {% endif %} - {% if val_int | bitwise_and(2)%} + {% if val_int | bitwise_and(0x0002)%} {% set result = result + 'PVLV-Fault (PV LowVolt), '%} {% endif %} - {% if val_int | bitwise_and(3)%} + {% if val_int | bitwise_and(0x0004)%} {% set result = result + 'PV OI-Fault (PV OverCurrent), '%} {% endif %} - {% if val_int | bitwise_and(4)%} + {% if val_int | bitwise_and(0x0008)%} {% set result = result + 'PV OFV-Fault, '%} {% endif %} - {% if val_int | bitwise_and(5)%} + {% if val_int | bitwise_and(0x0010)%} {% set result = result + 'DC ShortCircuitFault, '%} {% endif %} - {% if val_int | bitwise_and(6)%}{% set result = result + 'Bit6, '%} + {% if val_int | bitwise_and(0x0020)%}{% set result = result + 'Bit6, '%} {% endif %} - {% if val_int | bitwise_and(7)%}{% set result = result + 'Bit7, '%} + {% if val_int | bitwise_and(0x0040)%}{% set result = result + 'Bit7, '%} {% endif %} - {% if val_int | bitwise_and(8)%}{% set result = result + 'Bit8, '%} + {% if val_int | bitwise_and(0x0080)%}{% set result = result + 'Bit8, '%} {% endif %} - {% if val_int | bitwise_and(9)%}{% set result = result + 'Bit9, '%} + {% if val_int | bitwise_and(0x0100)%}{% set result = result + 'Bit9, '%} {% endif %} - {% if val_int | bitwise_and(10)%}{% set result = result + 'Bit10, '%} + {% if val_int | bitwise_and(0x0200)%}{% set result = result + 'Bit10, '%} {% endif %} - {% if val_int | bitwise_and(11)%}{% set result = result + 'Bit11, '%} + {% if val_int | bitwise_and(0x0400)%}{% set result = result + 'Bit11, '%} {% endif %} - {% if val_int | bitwise_and(12)%}{% set result = result + 'Bit12, '%} + {% if val_int | bitwise_and(0x0800)%}{% set result = result + 'Bit12, '%} {% endif %} - {% if val_int | bitwise_and(13)%}{% set result = result + 'Bit13, '%} + {% if val_int | bitwise_and(0x1000)%}{% set result = result + 'Bit13, '%} {% endif %} - {% if val_int | bitwise_and(14)%}{% set result = result + 'Bit14, '%} + {% if val_int | bitwise_and(0x2000)%}{% set result = result + 'Bit14, '%} {% endif %} - {% if val_int | bitwise_and(15)%}{% set result = result + 'Bit15, '%} + {% if val_int | bitwise_and(0x4000)%}{% set result = result + 'Bit15, '%} {% endif %} - {% if val_int | bitwise_and(16)%}{% set result = result + 'Bit16, '%} + {% if val_int | bitwise_and(0x8000)%}{% set result = result + 'Bit16, '%} + {% endif %} + {% endif %} + {{ result }} +{% else %} + {{ this.state }} +{% endif %} +''' + __batt_alarm_val_tpl = ''' +{% if 'Batterie_Alarm' in value_json and + value_json['Batterie_Alarm'] != None %} + {% set val_int = value_json['Batterie_Alarm'] | int %} + {% if val_int == 0 %} + {% set result = 'noAlarm'%} + {%else%} + {% set result = '' %} + {% if val_int | bitwise_and(0x0001)%} + {% set result = result + 'PV1-OverVoltage, '%} + {% endif %} + {% if val_int | bitwise_and(0x0002)%} + {% set result = result + 'PV2-OverVoltage, '%} + {% endif %} + {% if val_int | bitwise_and(0x0004)%} + {% set result = result + 'EquipmentOverheating, '%} + {% endif %} + {% if val_int | bitwise_and(0x0008)%} + {% set result = result + 'EquipmentLowTemp, '%} + {% endif %} + {% if val_int | bitwise_and(0x0010)%} + {% set result = result + 'BMS-CommFailed, '%} + {% endif %} + {% if val_int | bitwise_and(0x0020)%} + {% set result = result + 'UnderVoltageProt, '%} + {% endif %} + {% if val_int | bitwise_and(0x0040)%} + {% set result = result + 'ChargingHighTemp, '%} + {% endif %} + {% if val_int | bitwise_and(0x0080)%} + {% set result = result + 'ChargingLowTemp, '%} + {% endif %} + {% if val_int | bitwise_and(0x0100)%} + {% set result = result + 'DischargeHighTemp, '%} + {% endif %} + {% if val_int | bitwise_and(0x0200)%} + {% set result = result + 'DischargeLowTemp, '%} + {% endif %} + {% if val_int | bitwise_and(0x0400)%} + {% set result = result + 'BatterieOverVoltage, '%} + {% endif %} + {% if val_int | bitwise_and(0x0800)%} + {% set result = result + 'SingleCorePressureDifferenceIsTooLarge, '%} + {% endif %} + {% if val_int | bitwise_and(0x1000)%} + {% set result = result + 'Bit-12, '%} + {% endif %} + {% if val_int | bitwise_and(0x2000)%} + {% set result = result + 'Bit-13, '%} + {% endif %} + {% if val_int | bitwise_and(0x4000)%} + {% set result = result + 'Bit-14, '%} + {% endif %} + {% if val_int | bitwise_and(0x8000)%} + {% set result = result + 'Bit-15, '%} {% endif %} {% endif %} {{ result }} @@ -562,8 +629,8 @@ class Infos: # 0xffffff03: {'name':['proxy', 'Voltage'], 'level': logging.DEBUG, 'unit': 'V', 'ha':{'dev':'proxy', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id':'proxy_volt_', 'fmt':FMT_FLOAT,'name': 'Grid Voltage'}}, # noqa: E501 # events - Register.EVENT_ALARM: {'name': ['events', 'Inverter_Alarm'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'inverter', 'comp': 'sensor', 'dev_cla': None, 'stat_cla': None, 'id': 'inv_alarm_', 'name': 'Inverter Alarm', 'val_tpl': __inv_alarm_val_tpl, 'icon': 'mdi:alarm-light'}}, # noqa: E501 - Register.EVENT_FAULT: {'name': ['events', 'Inverter_Fault'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'inverter', 'comp': 'sensor', 'dev_cla': None, 'stat_cla': None, 'id': 'inv_fault_', 'name': 'Inverter Fault', 'val_tpl': __inv_fault_val_tpl, 'icon': 'mdi:alarm-light'}}, # noqa: E501 + Register.EVENT_ALARM: {'name': ['events', 'Inverter_Alarm'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'inverter', 'comp': 'sensor', 'dev_cla': None, 'stat_cla': None, 'id': 'inv_alarm_', 'name': 'Inverter Alarm', 'val_tpl': __inv_alarm_val_tpl, 'icon': ALARM_LIGHT}}, # noqa: E501 + Register.EVENT_FAULT: {'name': ['events', 'Inverter_Fault'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'inverter', 'comp': 'sensor', 'dev_cla': None, 'stat_cla': None, 'id': 'inv_fault_', 'name': 'Inverter Fault', 'val_tpl': __inv_fault_val_tpl, 'icon': ALARM_LIGHT}}, # noqa: E501 Register.EVENT_BF1: {'name': ['events', 'Inverter_Bitfield_1'], 'level': logging.INFO, 'unit': ''}, # noqa: E501 Register.EVENT_BF2: {'name': ['events', 'Inverter_bitfield_2'], 'level': logging.INFO, 'unit': ''}, # noqa: E501 # Register.EVENT_409: {'name': ['events', '409_No_Utility'], 'level': logging.DEBUG, 'unit': ''}, # noqa: E501 @@ -636,46 +703,53 @@ class Infos: 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 + # Batterie DC-1000: Electricity Genration + Register.BATT_PV1_STATUS: {'name': ['batterie', 'pv1', 'MPPT-Status'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'batterie', 'comp': 'sensor', 'dev_cla': None, 'stat_cla': None, 'id': 'status1_', 'name': 'MPPT-1 Status', 'val_tpl': __mppt1_status_type_val_tpl, 'icon': POWER}}, # 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_STATUS: {'name': ['batterie', 'pv2', 'MPPT-Status'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'batterie', 'comp': 'sensor', 'dev_cla': None, 'stat_cla': None, 'id': 'status2_', 'name': 'MPPT-2 Status', 'val_tpl': __mppt2_status_type_val_tpl, 'icon': POWER}}, # 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_TOTAL_GEN: {'name': ['batterie', 'Total_Generation'], 'level': logging.INFO, 'unit': 'kWh', 'ha': {'dev': 'batterie', 'dev_cla': 'energy', 'stat_cla': 'total', 'id': 'total_gen_', 'fmt': FMT_FLOAT, 'name': TOTAL_GEN, 'icon': SOLAR_POWER, 'must_incr': True}}, # noqa: E501 - Register.BATT_STATUS_1: {'name': ['batterie', 'Status_1'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'batterie', 'comp': 'sensor', 'dev_cla': None, 'stat_cla': None, 'id': 'status1_', 'name': 'MPPT-1 Status', 'val_tpl': __mppt1_status_type_val_tpl, 'icon': POWER}}, # noqa: E501 - Register.BATT_STATUS_2: {'name': ['batterie', 'Status_2'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'batterie', 'comp': 'sensor', 'dev_cla': None, 'stat_cla': None, 'id': 'status2_', 'name': 'MPPT-2 Status', 'val_tpl': __mppt2_status_type_val_tpl, 'icon': POWER}}, # noqa: E501 - Register.BATT_VOLT: {'name': ['batterie', 'Voltage'], 'level': logging.INFO, 'unit': 'V', 'ha': {'dev': 'batterie', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_bat_', 'fmt': FMT_FLOAT, 'name': 'Batterie Voltage', 'icon': GAUGE, 'ent_cat': 'diagnostic'}}, # noqa: E501 - Register.BATT_CUR: {'name': ['batterie', 'Current'], 'level': logging.INFO, 'unit': 'A', 'ha': {'dev': 'batterie', 'dev_cla': 'current', 'stat_cla': 'measurement', 'id': 'cur_bat_', 'fmt': FMT_FLOAT, 'name': 'Batterie Current', 'icon': GAUGE, '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 (SOC)', 'icon': 'mdi:battery-90'}}, # noqa: E501 - Register.BATT_CELL1_VOLT: {'name': ['batterie', 'Cell', 'Volt1'], 'level': logging.INFO, 'unit': 'V', 'ha': {'dev': 'batterie', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_cell1_', 'val_tpl': "{{ (value_json['Cell']['Volt1'] | float)}}", 'name': 'Cell-01 Voltage', 'icon': GAUGE, 'ent_cat': 'diagnostic'}}, # noqa: E501 - Register.BATT_CELL3_VOLT: {'name': ['batterie', 'Cell', 'Volt3'], 'level': logging.INFO, 'unit': 'V', 'ha': {'dev': 'batterie', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_cell3_', 'val_tpl': "{{ (value_json['Cell']['Volt2'] | float)}}", 'name': 'Cell-03 Voltage', 'icon': GAUGE, 'ent_cat': 'diagnostic'}}, # noqa: E501 - Register.BATT_CELL4_VOLT: {'name': ['batterie', 'Cell', 'Volt4'], 'level': logging.INFO, 'unit': 'V', 'ha': {'dev': 'batterie', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_cell4_', 'val_tpl': "{{ (value_json['Cell']['Volt3'] | float)}}", 'name': 'Cell-04 Voltage', 'icon': GAUGE, 'ent_cat': 'diagnostic'}}, # noqa: E501 - Register.BATT_CELL2_VOLT: {'name': ['batterie', 'Cell', 'Volt2'], 'level': logging.INFO, 'unit': 'V', 'ha': {'dev': 'batterie', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_cell2_', 'val_tpl': "{{ (value_json['Cell']['Volt4'] | float)}}", 'name': 'Cell-02 Voltage', 'icon': GAUGE, 'ent_cat': 'diagnostic'}}, # noqa: E501 - Register.BATT_CELL5_VOLT: {'name': ['batterie', 'Cell', 'Volt5'], 'level': logging.INFO, 'unit': 'V', 'ha': {'dev': 'batterie', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_cell5_', 'val_tpl': "{{ (value_json['Cell']['Volt5'] | float)}}", 'name': 'Cell-05 Voltage', 'icon': GAUGE, 'ent_cat': 'diagnostic'}}, # noqa: E501 - Register.BATT_CELL6_VOLT: {'name': ['batterie', 'Cell', 'Volt6'], 'level': logging.INFO, 'unit': 'V', 'ha': {'dev': 'batterie', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_cell6_', 'val_tpl': "{{ (value_json['Cell']['Volt6'] | float)}}", 'name': 'Cell-06 Voltage', 'icon': GAUGE, 'ent_cat': 'diagnostic'}}, # noqa: E501 - Register.BATT_CELL7_VOLT: {'name': ['batterie', 'Cell', 'Volt7'], 'level': logging.INFO, 'unit': 'V', 'ha': {'dev': 'batterie', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_cell7_', 'val_tpl': "{{ (value_json['Cell']['Volt7'] | float)}}", 'name': 'Cell-07 Voltage', 'icon': GAUGE, 'ent_cat': 'diagnostic'}}, # noqa: E501 - Register.BATT_CELL8_VOLT: {'name': ['batterie', 'Cell', 'Volt8'], 'level': logging.INFO, 'unit': 'V', 'ha': {'dev': 'batterie', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_cell8_', 'val_tpl': "{{ (value_json['Cell']['Volt8'] | float)}}", 'name': 'Cell-08 Voltage', 'icon': GAUGE, 'ent_cat': 'diagnostic'}}, # noqa: E501 - Register.BATT_CELL9_VOLT: {'name': ['batterie', 'Cell', 'Volt9'], 'level': logging.INFO, 'unit': 'V', 'ha': {'dev': 'batterie', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_cell9_', 'val_tpl': "{{ (value_json['Cell']['Volt9'] | float)}}", 'name': 'Cell-09 Voltage', 'icon': GAUGE, 'ent_cat': 'diagnostic'}}, # noqa: E501 - Register.BATT_CELL10_VOLT: {'name': ['batterie', 'Cell', 'Volt10'], 'level': logging.INFO, 'unit': 'V', 'ha': {'dev': 'batterie', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_cell10_', 'val_tpl': "{{ (value_json['Cell']['Volt10'] | float)}}", 'name': 'Cell-10 Voltage', 'icon': GAUGE, 'ent_cat': 'diagnostic'}}, # noqa: E501 - Register.BATT_CELL11_VOLT: {'name': ['batterie', 'Cell', 'Volt11'], 'level': logging.INFO, 'unit': 'V', 'ha': {'dev': 'batterie', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_cell11_', 'val_tpl': "{{ (value_json['Cell']['Volt11'] | float)}}", 'name': 'Cell-11 Voltage', 'icon': GAUGE, 'ent_cat': 'diagnostic'}}, # noqa: E501 - Register.BATT_CELL12_VOLT: {'name': ['batterie', 'Cell', 'Volt12'], 'level': logging.INFO, 'unit': 'V', 'ha': {'dev': 'batterie', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_cell12_', 'val_tpl': "{{ (value_json['Cell']['Volt12'] | float)}}", 'name': 'Cell-12 Voltage', 'icon': GAUGE, 'ent_cat': 'diagnostic'}}, # noqa: E501 - Register.BATT_CELL13_VOLT: {'name': ['batterie', 'Cell', 'Volt13'], 'level': logging.INFO, 'unit': 'V', 'ha': {'dev': 'batterie', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_cell13_', 'val_tpl': "{{ (value_json['Cell']['Volt13'] | float)}}", 'name': 'Cell-13 Voltage', 'icon': GAUGE, 'ent_cat': 'diagnostic'}}, # noqa: E501 - Register.BATT_CELL14_VOLT: {'name': ['batterie', 'Cell', 'Volt14'], 'level': logging.INFO, 'unit': 'V', 'ha': {'dev': 'batterie', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_cell14_', 'val_tpl': "{{ (value_json['Cell']['Volt14'] | float)}}", 'name': 'Cell-14 Voltage', 'icon': GAUGE, 'ent_cat': 'diagnostic'}}, # noqa: E501 - Register.BATT_CELL15_VOLT: {'name': ['batterie', 'Cell', 'Volt15'], 'level': logging.INFO, 'unit': 'V', 'ha': {'dev': 'batterie', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_cell15_', 'val_tpl': "{{ (value_json['Cell']['Volt15'] | float)}}", 'name': 'Cell-15 Voltage', 'icon': GAUGE, 'ent_cat': 'diagnostic'}}, # noqa: E501 - Register.BATT_CELL16_VOLT: {'name': ['batterie', 'Cell', 'Volt16'], 'level': logging.INFO, 'unit': 'V', 'ha': {'dev': 'batterie', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_cell16_', 'val_tpl': "{{ (value_json['Cell']['Volt16'] | float)}}", 'name': 'Cell-16 Voltage', 'icon': GAUGE, 'ent_cat': 'diagnostic'}}, # noqa: E501 - Register.BATT_TEMP_1: {'name': ['batterie', 'Temp_1'], 'level': logging.INFO, 'unit': '°C', 'ha': {'dev': 'batterie', 'dev_cla': 'temperature', 'stat_cla': 'measurement', 'id': 'temp_1_', 'fmt': FMT_INT, 'name': 'Batterie Temp-1', 'ent_cat': 'diagnostic'}}, # noqa: E501 - Register.BATT_TEMP_2: {'name': ['batterie', 'Temp_2'], 'level': logging.INFO, 'unit': '°C', 'ha': {'dev': 'batterie', 'dev_cla': 'temperature', 'stat_cla': 'measurement', 'id': 'temp_2_', 'fmt': FMT_INT, 'name': 'Batterie Temp-2', 'ent_cat': 'diagnostic'}}, # noqa: E501 - Register.BATT_TEMP_3: {'name': ['batterie', 'Temp_3'], 'level': logging.INFO, 'unit': '°C', 'ha': {'dev': 'batterie', 'dev_cla': 'temperature', 'stat_cla': 'measurement', 'id': 'temp_3_', 'fmt': FMT_INT, 'name': 'Batterie Temp-3', 'ent_cat': 'diagnostic'}}, # noqa: E501 + Register.BATT_PV_PWR: {'name': ['batterie', 'PV_Power'], 'level': logging.INFO, 'unit': 'W', 'ha': {'dev': 'batterie', 'dev_cla': 'power', 'stat_cla': 'measurement', 'id': 'pv_power_', 'fmt': FMT_INT, 'name': 'PV Power'}}, # noqa: E501 + Register.BATT_OUT_STATUS: {'name': ['batterie', 'out', 'Out_Status'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'batterie', 'comp': 'sensor', 'dev_cla': None, 'stat_cla': None, 'id': 'out_status_', 'name': 'Output Status', 'val_tpl': __out_status_type_val_tpl, 'icon': POWER}}, # noqa: E501 Register.BATT_OUT_VOLT: {'name': ['batterie', 'out', 'Voltage'], 'level': logging.INFO, 'unit': 'V', 'ha': {'dev': 'batterie', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'out_volt_', 'val_tpl': "{{ (value_json['out']['Voltage'] | float)}}", 'name': 'Output Voltage', 'icon': GAUGE, 'ent_cat': 'diagnostic'}}, # noqa: E501 Register.BATT_OUT_CUR: {'name': ['batterie', 'out', 'Current'], 'level': logging.INFO, 'unit': 'A', 'ha': {'dev': 'batterie', 'dev_cla': 'current', 'stat_cla': 'measurement', 'id': 'out_cur_', 'val_tpl': "{{ (value_json['out']['Current'] | float)}}", 'name': 'Output Current', 'icon': GAUGE, 'ent_cat': 'diagnostic'}}, # noqa: E501 - Register.BATT_OUT_STATUS: {'name': ['batterie', 'out', 'Out_Status'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'batterie', 'comp': 'sensor', 'dev_cla': None, 'stat_cla': None, 'id': 'out_status_', 'name': 'Output Status', 'val_tpl': __out_status_type_val_tpl, 'icon': POWER}}, # noqa: E501 - Register.BATT_TEMP_4: {'name': ['batterie', 'Controller_Temp'], 'level': logging.INFO, 'unit': '°C', 'ha': {'dev': 'batterie', 'dev_cla': 'temperature', 'stat_cla': 'measurement', 'id': 'temp_4_', 'fmt': FMT_INT, 'name': 'Ctrl Temperature'}}, # 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 - Register.BATT_PV_PWR: {'name': ['batterie', 'PV_Power'], 'level': logging.INFO, 'unit': 'W', 'ha': {'dev': 'batterie', 'dev_cla': 'power', 'stat_cla': 'measurement', 'id': 'pv_power_', 'fmt': FMT_INT, 'name': 'PV Power'}}, # noqa: E501 - Register.BATT_PWR: {'name': ['batterie', 'Power'], 'level': logging.INFO, 'unit': 'W', 'ha': {'dev': 'batterie', 'dev_cla': 'power', 'stat_cla': 'measurement', 'id': 'power_', 'fmt': FMT_INT, 'name': 'Batterie Power'}}, # noqa: E501 - Register.BATT_OUT_PWR: {'name': ['batterie', 'out', 'Power'], 'level': logging.INFO, 'unit': 'W', 'ha': {'dev': 'batterie', 'dev_cla': 'power', 'stat_cla': 'measurement', 'id': 'out_power_', 'val_tpl': "{{ (value_json['out']['Power'] | int)}}", 'name': 'Output Power'}}, # noqa: E501 + Register.BATT_OUT_PWR: {'name': ['batterie', 'out', 'Power'], 'level': logging.INFO, 'unit': 'W', 'ha': {'dev': 'batterie', 'dev_cla': 'power', 'stat_cla': 'measurement', 'id': 'out_power_', 'val_tpl': "{{ (value_json['out']['Power'] | int)}}", 'name': 'Supply Power'}}, # noqa: E501 + Register.BATT_PWR_SUPL_STATE: {'name': ['batterie', 'out', 'Suppl_State'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'batterie', 'comp': 'sensor', 'dev_cla': None, 'stat_cla': None, 'id': 'status_supply_', 'name': 'Supply State', 'val_tpl': __supply_status_type_val_tpl, 'icon': POWER}}, # noqa: E501 + + # Batterie DC-1000: Cell Package + Register.BATT_CELL1_VOLT: {'name': ['batterie', 'cell', 'Volt1'], 'level': logging.INFO, 'unit': 'V', 'ha': {'dev': 'batterie', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_cell1_', 'val_tpl': "{{ (value_json['cell']['Volt1'] | float)}}", 'name': 'Cell-01 Voltage', 'icon': GAUGE, 'ent_cat': 'diagnostic'}}, # noqa: E501 + Register.BATT_CELL3_VOLT: {'name': ['batterie', 'cell', 'Volt3'], 'level': logging.INFO, 'unit': 'V', 'ha': {'dev': 'batterie', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_cell3_', 'val_tpl': "{{ (value_json['cell']['Volt2'] | float)}}", 'name': 'Cell-03 Voltage', 'icon': GAUGE, 'ent_cat': 'diagnostic'}}, # noqa: E501 + Register.BATT_CELL4_VOLT: {'name': ['batterie', 'cell', 'Volt4'], 'level': logging.INFO, 'unit': 'V', 'ha': {'dev': 'batterie', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_cell4_', 'val_tpl': "{{ (value_json['cell']['Volt3'] | float)}}", 'name': 'Cell-04 Voltage', 'icon': GAUGE, 'ent_cat': 'diagnostic'}}, # noqa: E501 + Register.BATT_CELL2_VOLT: {'name': ['batterie', 'cell', 'Volt2'], 'level': logging.INFO, 'unit': 'V', 'ha': {'dev': 'batterie', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_cell2_', 'val_tpl': "{{ (value_json['cell']['Volt4'] | float)}}", 'name': 'Cell-02 Voltage', 'icon': GAUGE, 'ent_cat': 'diagnostic'}}, # noqa: E501 + Register.BATT_CELL5_VOLT: {'name': ['batterie', 'cell', 'Volt5'], 'level': logging.INFO, 'unit': 'V', 'ha': {'dev': 'batterie', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_cell5_', 'val_tpl': "{{ (value_json['cell']['Volt5'] | float)}}", 'name': 'Cell-05 Voltage', 'icon': GAUGE, 'ent_cat': 'diagnostic'}}, # noqa: E501 + Register.BATT_CELL6_VOLT: {'name': ['batterie', 'cell', 'Volt6'], 'level': logging.INFO, 'unit': 'V', 'ha': {'dev': 'batterie', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_cell6_', 'val_tpl': "{{ (value_json['cell']['Volt6'] | float)}}", 'name': 'Cell-06 Voltage', 'icon': GAUGE, 'ent_cat': 'diagnostic'}}, # noqa: E501 + Register.BATT_CELL7_VOLT: {'name': ['batterie', 'cell', 'Volt7'], 'level': logging.INFO, 'unit': 'V', 'ha': {'dev': 'batterie', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_cell7_', 'val_tpl': "{{ (value_json['cell']['Volt7'] | float)}}", 'name': 'Cell-07 Voltage', 'icon': GAUGE, 'ent_cat': 'diagnostic'}}, # noqa: E501 + Register.BATT_CELL8_VOLT: {'name': ['batterie', 'cell', 'Volt8'], 'level': logging.INFO, 'unit': 'V', 'ha': {'dev': 'batterie', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_cell8_', 'val_tpl': "{{ (value_json['cell']['Volt8'] | float)}}", 'name': 'Cell-08 Voltage', 'icon': GAUGE, 'ent_cat': 'diagnostic'}}, # noqa: E501 + Register.BATT_CELL9_VOLT: {'name': ['batterie', 'cell', 'Volt9'], 'level': logging.INFO, 'unit': 'V', 'ha': {'dev': 'batterie', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_cell9_', 'val_tpl': "{{ (value_json['cell']['Volt9'] | float)}}", 'name': 'Cell-09 Voltage', 'icon': GAUGE, 'ent_cat': 'diagnostic'}}, # noqa: E501 + Register.BATT_CELL10_VOLT: {'name': ['batterie', 'cell', 'Volt10'], 'level': logging.INFO, 'unit': 'V', 'ha': {'dev': 'batterie', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_cell10_', 'val_tpl': "{{ (value_json['cell']['Volt10'] | float)}}", 'name': 'Cell-10 Voltage', 'icon': GAUGE, 'ent_cat': 'diagnostic'}}, # noqa: E501 + Register.BATT_CELL11_VOLT: {'name': ['batterie', 'cell', 'Volt11'], 'level': logging.INFO, 'unit': 'V', 'ha': {'dev': 'batterie', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_cell11_', 'val_tpl': "{{ (value_json['cell']['Volt11'] | float)}}", 'name': 'Cell-11 Voltage', 'icon': GAUGE, 'ent_cat': 'diagnostic'}}, # noqa: E501 + Register.BATT_CELL12_VOLT: {'name': ['batterie', 'cell', 'Volt12'], 'level': logging.INFO, 'unit': 'V', 'ha': {'dev': 'batterie', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_cell12_', 'val_tpl': "{{ (value_json['cell']['Volt12'] | float)}}", 'name': 'Cell-12 Voltage', 'icon': GAUGE, 'ent_cat': 'diagnostic'}}, # noqa: E501 + Register.BATT_CELL13_VOLT: {'name': ['batterie', 'cell', 'Volt13'], 'level': logging.INFO, 'unit': 'V', 'ha': {'dev': 'batterie', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_cell13_', 'val_tpl': "{{ (value_json['cell']['Volt13'] | float)}}", 'name': 'Cell-13 Voltage', 'icon': GAUGE, 'ent_cat': 'diagnostic'}}, # noqa: E501 + Register.BATT_CELL14_VOLT: {'name': ['batterie', 'cell', 'Volt14'], 'level': logging.INFO, 'unit': 'V', 'ha': {'dev': 'batterie', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_cell14_', 'val_tpl': "{{ (value_json['cell']['Volt14'] | float)}}", 'name': 'Cell-14 Voltage', 'icon': GAUGE, 'ent_cat': 'diagnostic'}}, # noqa: E501 + Register.BATT_CELL15_VOLT: {'name': ['batterie', 'cell', 'Volt15'], 'level': logging.INFO, 'unit': 'V', 'ha': {'dev': 'batterie', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_cell15_', 'val_tpl': "{{ (value_json['cell']['Volt15'] | float)}}", 'name': 'Cell-15 Voltage', 'icon': GAUGE, 'ent_cat': 'diagnostic'}}, # noqa: E501 + Register.BATT_CELL16_VOLT: {'name': ['batterie', 'cell', 'Volt16'], 'level': logging.INFO, 'unit': 'V', 'ha': {'dev': 'batterie', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_cell16_', 'val_tpl': "{{ (value_json['cell']['Volt16'] | float)}}", 'name': 'Cell-16 Voltage', 'icon': GAUGE, 'ent_cat': 'diagnostic'}}, # noqa: E501 + + # Batterie DC-1000: Batterie Pack + Register.BATT_VOLT: {'name': ['batterie', 'batt', 'Voltage'], 'level': logging.INFO, 'unit': 'V', 'ha': {'dev': 'batterie', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_bat_', 'val_tpl': "{{ (value_json['batt']['Voltage'] | float)}}", 'name': 'Batterie Voltage', 'icon': GAUGE, 'ent_cat': 'diagnostic'}}, # noqa: E501 + Register.BATT_CUR: {'name': ['batterie', 'batt', 'Current'], 'level': logging.INFO, 'unit': 'A', 'ha': {'dev': 'batterie', 'dev_cla': 'current', 'stat_cla': 'measurement', 'id': 'cur_bat_', 'val_tpl': "{{ (value_json['batt']['Current'] | float)}}", 'name': 'Batterie Current', 'icon': GAUGE, 'ent_cat': 'diagnostic'}}, # noqa: E501 + Register.BATT_PWR: {'name': ['batterie', 'batt', 'Power'], 'level': logging.INFO, 'unit': 'W', 'ha': {'dev': 'batterie', 'dev_cla': 'power', 'stat_cla': 'measurement', 'id': 'power_', 'val_tpl': "{{ (value_json['batt']['Power'] | int)}}", 'name': 'Batterie Power'}}, # noqa: E501 + Register.BATT_SOC: {'name': ['batterie', 'batt', 'SOC'], 'level': logging.INFO, 'unit': '%', 'ha': {'dev': 'batterie', 'dev_cla': None, 'stat_cla': 'measurement', 'id': 'soc_', 'val_tpl': "{{ (value_json['batt']['SOC'] | float)}}", 'name': 'State of Charge (SOC)', 'icon': 'mdi:battery-90'}}, # noqa: E501 + Register.BATT_TOTAL_CHARG: {'name': ['batterie', 'batt', 'Total_Charging'], 'level': logging.INFO, 'unit': 'kWh', 'ha': {'dev': 'batterie', 'dev_cla': 'energy', 'stat_cla': 'total', 'id': 'total_charg_', 'val_tpl': "{{ (value_json['batt']['Total_Charging'] | float)}}", 'name': TOTAL_CHARG, 'icon': 'mdi:battery-charging', 'must_incr': True}}, # noqa: E501 + Register.BATT_STATUS: {'name': ['batterie', 'batt', 'Batt_State'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'batterie', 'comp': 'sensor', 'dev_cla': None, 'stat_cla': None, 'id': 'status_batt_', 'name': 'Batterie State', 'val_tpl': __batt_status_type_val_tpl, 'icon': POWER}}, # noqa: E501 + + Register.BATT_TEMP_1: {'name': ['batterie', 'cell', 'Temp_1'], 'level': logging.INFO, 'unit': '°C', 'ha': {'dev': 'batterie', 'dev_cla': 'temperature', 'stat_cla': 'measurement', 'id': 'temp_1_', 'val_tpl': "{{ (value_json['cell']['Temp_1'] | int)}}", 'name': 'Cell Temp-1', 'ent_cat': 'diagnostic'}}, # noqa: E501 + Register.BATT_TEMP_2: {'name': ['batterie', 'cell', 'Temp_2'], 'level': logging.INFO, 'unit': '°C', 'ha': {'dev': 'batterie', 'dev_cla': 'temperature', 'stat_cla': 'measurement', 'id': 'temp_2_', 'val_tpl': "{{ (value_json['cell']['Temp_2'] | int)}}", 'name': 'Cell Temp-2', 'ent_cat': 'diagnostic'}}, # noqa: E501 + Register.BATT_TEMP_3: {'name': ['batterie', 'cell', 'Temp_3'], 'level': logging.INFO, 'unit': '°C', 'ha': {'dev': 'batterie', 'dev_cla': 'temperature', 'stat_cla': 'measurement', 'id': 'temp_3_', 'val_tpl': "{{ (value_json['cell']['Temp_3'] | int)}}", 'name': 'Cell Temp-3', 'ent_cat': 'diagnostic'}}, # noqa: E501 + Register.BATT_TEMP_4: {'name': ['batterie', 'Controller_Temp'], 'level': logging.INFO, 'unit': '°C', 'ha': {'dev': 'batterie', 'dev_cla': 'temperature', 'stat_cla': 'measurement', 'id': 'temp_4_', 'fmt': FMT_INT, 'name': 'Temperature'}}, # noqa: E501 + Register.BATT_ALARM: {'name': ['batterie', 'Batterie_Alarm'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'batterie', 'comp': 'sensor', 'dev_cla': None, 'stat_cla': None, 'id': 'batt_alarm_', 'name': 'Batterie Alarm', 'val_tpl': __batt_alarm_val_tpl, 'icon': ALARM_LIGHT}}, # noqa: E501 + Register.BATT_HW_VERS: {'name': ['batterie', 'Hardware_Version'], 'level': logging.INFO, 'unit': ''}, # noqa: E501 + Register.BATT_SW_VERS: {'name': ['batterie', 'Software_Version'], 'level': logging.INFO, 'unit': ''}, # noqa: E501 Register.TEST_VAL_0: {'name': ['input', 'Val_0'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'inverter', 'dev_cla': 'power', 'stat_cla': 'measurement', 'id': 'val_0_', 'fmt': FMT_INT, 'ent_cat': 'diagnostic'}}, # noqa: E501 Register.TEST_VAL_1: {'name': ['input', 'Val_1'], 'level': logging.INFO, 'unit': '', 'ha': {'dev': 'inverter', 'dev_cla': 'power', 'stat_cla': 'measurement', 'id': 'val_1_', 'fmt': FMT_INT, 'ent_cat': 'diagnostic'}}, # noqa: E501 diff --git a/app/src/inverter_base.py b/app/src/inverter_base.py index a4036c9..18c8902 100644 --- a/app/src/inverter_base.py +++ b/app/src/inverter_base.py @@ -41,7 +41,7 @@ class InverterBase(InverterIfc, Proxy): self.remote) self.local = StreamPtr( - prot_class(self.addr, ifc, True, client_mode), ifc + prot_class(self, self.addr, ifc, True, client_mode), ifc ) def __enter__(self): @@ -122,11 +122,11 @@ class InverterBase(InverterIfc, Proxy): self.remote.ifc = ifc if hasattr(stream, 'id_str'): self.remote.stream = self.prot_class( - addr, ifc, server_side=False, + self, addr, ifc, server_side=False, client_mode=False, id_str=stream.id_str) else: self.remote.stream = self.prot_class( - addr, ifc, server_side=False, + self, addr, ifc, server_side=False, client_mode=False) logging.info(f'[{self.remote.stream.node_id}:' diff --git a/app/src/modbus.py b/app/src/modbus.py index c63b545..23ae92d 100644 --- a/app/src/modbus.py +++ b/app/src/modbus.py @@ -42,39 +42,38 @@ class Modbus(): 0x0009: {'reg': Register.BATT_PV1_CUR, 'fmt': '!H', 'ratio': 0.01}, # noqa: E501, PV1 current 0x000a: {'reg': Register.BATT_PV2_VOLT, 'fmt': '!H', 'ratio': 0.01}, # noqa: E501, PV2 voltage 0x000b: {'reg': Register.BATT_PV2_CUR, 'fmt': '!H', 'ratio': 0.01}, # noqa: E501, PV2 current - 0x000c: {'reg': Register.BATT_38, 'fmt': '!h'}, # noqa: E501 - 0x000d: {'reg': Register.BATT_TOTAL_GEN, 'fmt': '!h', 'ratio': 0.01}, # noqa: E501 - 0x000e: {'reg': Register.BATT_STATUS_1, 'fmt': '!h'}, # noqa: E501 - 0x000f: {'reg': Register.BATT_STATUS_2, 'fmt': '!h'}, # noqa: E501 + 0x000c: {'reg': Register.BATT_TOTAL_CHARG, 'fmt': '!L', 'ratio': 0.01}, # noqa: E501 + 0x000e: {'reg': Register.BATT_PV1_STATUS, 'fmt': '!H'}, # noqa: E501 + 0x000f: {'reg': Register.BATT_PV2_STATUS, 'fmt': '!H'}, # noqa: E501 0x0010: {'reg': Register.BATT_VOLT, 'fmt': '!h', 'ratio': 0.01}, # noqa: E501 0x0011: {'reg': Register.BATT_CUR, 'fmt': '!h', 'ratio': 0.01}, # noqa: E501 0x0012: {'reg': Register.BATT_SOC, 'fmt': '!H', 'ratio': 0.01}, # noqa: E501, state of charge (SOC) in percent - 0x0013: {'reg': Register.BATT_CELL1_VOLT, 'fmt': '!h', 'ratio': 0.001}, # noqa: E501 - 0x0014: {'reg': Register.BATT_CELL2_VOLT, 'fmt': '!h', 'ratio': 0.001}, # noqa: E501 - 0x0015: {'reg': Register.BATT_CELL3_VOLT, 'fmt': '!h', 'ratio': 0.001}, # noqa: E501 - 0x0016: {'reg': Register.BATT_CELL4_VOLT, 'fmt': '!h', 'ratio': 0.001}, # noqa: E501 - 0x0017: {'reg': Register.BATT_CELL5_VOLT, 'fmt': '!h', 'ratio': 0.001}, # noqa: E501 - 0x0018: {'reg': Register.BATT_CELL6_VOLT, 'fmt': '!h', 'ratio': 0.001}, # noqa: E501 - 0x0019: {'reg': Register.BATT_CELL7_VOLT, 'fmt': '!h', 'ratio': 0.001}, # noqa: E501 - 0x001a: {'reg': Register.BATT_CELL8_VOLT, 'fmt': '!h', 'ratio': 0.001}, # noqa: E501 - 0x001b: {'reg': Register.BATT_CELL9_VOLT, 'fmt': '!h', 'ratio': 0.001}, # noqa: E501 - 0x001c: {'reg': Register.BATT_CELL10_VOLT, 'fmt': '!h', 'ratio': 0.001}, # noqa: E501 - 0x001d: {'reg': Register.BATT_CELL11_VOLT, 'fmt': '!h', 'ratio': 0.001}, # noqa: E501 - 0x001e: {'reg': Register.BATT_CELL12_VOLT, 'fmt': '!h', 'ratio': 0.001}, # noqa: E501 - 0x001f: {'reg': Register.BATT_CELL13_VOLT, 'fmt': '!h', 'ratio': 0.001}, # noqa: E501 - 0x0020: {'reg': Register.BATT_CELL14_VOLT, 'fmt': '!h', 'ratio': 0.001}, # noqa: E501 - 0x0021: {'reg': Register.BATT_CELL15_VOLT, 'fmt': '!h', 'ratio': 0.001}, # noqa: E501 - 0x0022: {'reg': Register.BATT_CELL16_VOLT, 'fmt': '!h', 'ratio': 0.001}, # noqa: E501 + 0x0013: {'reg': Register.BATT_CELL1_VOLT, 'fmt': '!H', 'ratio': 0.001}, # noqa: E501 + 0x0014: {'reg': Register.BATT_CELL2_VOLT, 'fmt': '!H', 'ratio': 0.001}, # noqa: E501 + 0x0015: {'reg': Register.BATT_CELL3_VOLT, 'fmt': '!H', 'ratio': 0.001}, # noqa: E501 + 0x0016: {'reg': Register.BATT_CELL4_VOLT, 'fmt': '!H', 'ratio': 0.001}, # noqa: E501 + 0x0017: {'reg': Register.BATT_CELL5_VOLT, 'fmt': '!H', 'ratio': 0.001}, # noqa: E501 + 0x0018: {'reg': Register.BATT_CELL6_VOLT, 'fmt': '!H', 'ratio': 0.001}, # noqa: E501 + 0x0019: {'reg': Register.BATT_CELL7_VOLT, 'fmt': '!H', 'ratio': 0.001}, # noqa: E501 + 0x001a: {'reg': Register.BATT_CELL8_VOLT, 'fmt': '!H', 'ratio': 0.001}, # noqa: E501 + 0x001b: {'reg': Register.BATT_CELL9_VOLT, 'fmt': '!H', 'ratio': 0.001}, # noqa: E501 + 0x001c: {'reg': Register.BATT_CELL10_VOLT, 'fmt': '!H', 'ratio': 0.001}, # noqa: E501 + 0x001d: {'reg': Register.BATT_CELL11_VOLT, 'fmt': '!H', 'ratio': 0.001}, # noqa: E501 + 0x001e: {'reg': Register.BATT_CELL12_VOLT, 'fmt': '!H', 'ratio': 0.001}, # noqa: E501 + 0x001f: {'reg': Register.BATT_CELL13_VOLT, 'fmt': '!H', 'ratio': 0.001}, # noqa: E501 + 0x0020: {'reg': Register.BATT_CELL14_VOLT, 'fmt': '!H', 'ratio': 0.001}, # noqa: E501 + 0x0021: {'reg': Register.BATT_CELL15_VOLT, 'fmt': '!H', 'ratio': 0.001}, # noqa: E501 + 0x0022: {'reg': Register.BATT_CELL16_VOLT, 'fmt': '!H', 'ratio': 0.001}, # noqa: E501 0x0023: {'reg': Register.BATT_TEMP_1, 'fmt': '!h'}, # noqa: E501 0x0024: {'reg': Register.BATT_TEMP_2, 'fmt': '!h'}, # noqa: E501 0x0025: {'reg': Register.BATT_TEMP_3, 'fmt': '!h'}, # noqa: E501 - 0x0026: {'reg': Register.BATT_OUT_VOLT, 'fmt': '!h', 'ratio': 0.01}, # noqa: E501 - 0x0027: {'reg': Register.BATT_OUT_CUR, 'fmt': '!h', 'ratio': 0.01}, # noqa: E501 - 0x0028: {'reg': Register.BATT_OUT_STATUS, 'fmt': '!h'}, # noqa: E501 + 0x0026: {'reg': Register.BATT_OUT_VOLT, 'fmt': '!H', 'ratio': 0.01}, # noqa: E501 + 0x0027: {'reg': Register.BATT_OUT_CUR, 'fmt': '!H', 'ratio': 0.01}, # noqa: E501 + 0x0028: {'reg': Register.BATT_OUT_STATUS, 'fmt': '!H'}, # noqa: E501 0x0029: {'reg': Register.BATT_TEMP_4, 'fmt': '!h'}, # noqa: E501 - 0x002a: {'reg': Register.BATT_74, 'fmt': '!h'}, # noqa: E501 - 0x002b: {'reg': Register.BATT_76, 'fmt': '!h'}, # noqa: E501 - 0x002c: {'reg': Register.BATT_78, 'fmt': '!h'}, # noqa: E501 + 0x002a: {'reg': Register.BATT_ALARM, 'fmt': '!h'}, # noqa: E501 + 0x002b: {'reg': Register.BATT_HW_VERS, 'fmt': '!h'}, # noqa: E501 + 0x002c: {'reg': Register.BATT_SW_VERS, 'fmt': '!h'}, # noqa: E501 0x2000: {'reg': Register.BOOT_STATUS, 'fmt': '!H'}, # noqa: E501 0x2001: {'reg': Register.DSP_STATUS, 'fmt': '!H'}, # noqa: E501 diff --git a/app/tests/test_infos_g3.py b/app/tests/test_infos_g3.py index d0425e8..f786cc9 100644 --- a/app/tests/test_infos_g3.py +++ b/app/tests/test_infos_g3.py @@ -221,7 +221,7 @@ def inv_data_seq2(): # Data indication from the controller return msg @pytest.fixture -def inv_data_seq3(): # Inverter indication from MS-2000 +def inv_data_seq3(): # Inverter indication from MS-3000 msg = b'\x00\x00\x01\x2c\x00\x00\x00\x64\x53\x00\x00' # | ..^.....,...dS.. msg += b'\x00\x00\x00\xc8\x53\x44\x00\x00\x00\x01\x2c\x53\x00\x00\x00\x00' # | ....SD....,S.... diff --git a/app/tests/test_infos_g3p.py b/app/tests/test_infos_g3p.py index 87271d8..0596044 100644 --- a/app/tests/test_infos_g3p.py +++ b/app/tests/test_infos_g3p.py @@ -82,13 +82,25 @@ def batterie_data(): # 0x4210 ftype: 0x01 msg += b'\x00\x00\x00\x0f\x00\x00\x02\x05\x02\x01' return msg +@pytest.fixture +def batterie_data1(): # 0x4210 ftype: 0x01 + msg = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\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\x70\x0d\x2c\x00\x00\x00\x00\x08\x20\x00\x00\x00\x00' + msg += b'\x01\x00\x00\x00\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\x0c\x0e\x01\x00' + msg += b'\x00\x00\x00\x0f\x00\x00\x02\x05\x02\x01' + return msg + @pytest.fixture def batterie_data2(): # 0x4210 ftype: 0x01 msg = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\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\x70\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'\x14\x0e\x02\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' return msg @@ -155,15 +167,32 @@ def test_parse_4210_3026(batterie_data: bytes): assert json.dumps(i.db) == json.dumps({ "controller": {"Sensor_List": "3026", "Power_On_Time": 4684}, "inverter": {"Serial_Number": "4101240701490314"}, - "batterie": {"pv1": {"Voltage": 33.86, "Current": 1.12}, - "pv2": {"Voltage": 33.72, "Current": 0.0}, - "Reg_38": 0, "Total_Generation": 20.8, "Status_1": 0, "Status_2": 0, - "Voltage": 51.34, "Current": -0.02, "SOC": 10.0, - "Cell": {"Volt1": 3.21, "Volt2": 3.21, "Volt3": 3.21, "Volt4": 3.21, "Volt5": 3.21, "Volt6": 3.21, "Volt7": 3.21, "Volt8": 3.21, "Volt9": 3.21, "Volt10": 3.21, "Volt11": 3.21, "Volt12": 3.21, "Volt13": 3.21, "Volt14": 3.21, "Volt15": 3.21, "Volt16": 3.21}, - "Temp_1": 15, "Temp_2": 15, "Temp_3": 15, - "out": {"Voltage": 0.14, "Current": 0.0, "Out_Status": 0, "Power": 0.0}, - "Controller_Temp": 15, "Reg_74": 0, "Reg_76": 517, "Reg_78": 513, - "PV_Power": 37.9232, "Power": -1.0268000000000002}, + "batterie": {"pv1": {"Voltage": 33.86, "Current": 1.12, "MPPT-Status": 0}, + "pv2": {"Voltage": 33.72, "Current": 0.0, "MPPT-Status": 0}, + "batt": {"Total_Charging": 20.8, "Voltage": 51.34, "Current": -0.02, "SOC": 10.0, "Power": -1.0268000000000002, 'Batt_State': 0}, + "cell": {"Volt1": 3.21, "Volt2": 3.21, "Volt3": 3.21, "Volt4": 3.21, "Volt5": 3.21, "Volt6": 3.21, "Volt7": 3.21, "Volt8": 3.21, "Volt9": 3.21, "Volt10": 3.21, "Volt11": 3.21, "Volt12": 3.21, "Volt13": 3.21, "Volt14": 3.21, "Volt15": 3.21, "Volt16": 3.21, "Temp_1": 15, "Temp_2": 15, "Temp_3": 15}, + "out": {"Voltage": 0.14, "Current": 0.0, "Out_Status": 0, "Power": 0.0, "Suppl_State": 0}, + "Controller_Temp": 15, "Batterie_Alarm": 0, "Hardware_Version": 517, "Software_Version": 513, + "PV_Power": 37.9232}, + }) + +def test_parse_4210_3026_prod(batterie_data1: bytes): + i = InfosG3P(client_mode=False) + i.db.clear() + + for key, update in i.parse (batterie_data1, 0x42, 1, 0x3026): + pass # side effect is calling generator i.parse() + + assert json.dumps(i.db) == json.dumps({ + "controller": {"Sensor_List": "3026", "Power_On_Time": 4684}, + "inverter": {"Serial_Number": "4101240701490314"}, + "batterie": {"pv1": {"Voltage": 33.86, "Current": 1.12, "MPPT-Status": 0}, + "pv2": {"Voltage": 33.72, "Current": 0.0, "MPPT-Status": 0}, + "batt": {"Total_Charging": 20.8, "Voltage": 2.56, "Current": 0.0, "SOC": 10.0, "Power": 0.0, 'Batt_State': 1}, + "cell": {"Volt1": 3.21, "Volt2": 3.21, "Volt3": 3.21, "Volt4": 3.21, "Volt5": 3.21, "Volt6": 3.21, "Volt7": 3.21, "Volt8": 3.21, "Volt9": 3.21, "Volt10": 3.21, "Volt11": 3.21, "Volt12": 3.21, "Volt13": 3.21, "Volt14": 3.21, "Volt15": 3.21, "Volt16": 3.21, "Temp_1": 15, "Temp_2": 15, "Temp_3": 15}, + "out": {"Voltage": 30.86, "Current": 2.56, "Out_Status": 0, "Power": 79.0016, "Suppl_State": 1}, + "Controller_Temp": 15, "Batterie_Alarm": 0, "Hardware_Version": 517, "Software_Version": 513, + "PV_Power": 37.9232}, }) def test_parse_4210_3026_incomplete(batterie_data2: bytes): @@ -176,16 +205,14 @@ def test_parse_4210_3026_incomplete(batterie_data2: bytes): assert json.dumps(i.db) == json.dumps({ "controller": {"Sensor_List": "3026", "Power_On_Time": 4684}, "inverter": {"Serial_Number": "4101240701490314"}, - "batterie": {"pv1": {"Voltage": 33.86, "Current": 1.12}, - "pv2": {"Voltage": 33.72, "Current": 0.0}, - "Reg_38": 0, "Total_Generation": 20.8, "Status_1": 0, "Status_2": 0, - "Voltage": 51.34, "Current": -0.02, "SOC": 10.0, - "Cell": {"Volt1": 3.21, "Volt2": 3.21, "Volt3": 3.21, "Volt4": 3.21, "Volt5": 3.21, "Volt6": 3.21, "Volt7": 3.21, "Volt8": 3.21, "Volt9": 3.21, "Volt10": 3.21, "Volt11": 3.21, "Volt12": 3.21, "Volt13": 3.21, "Volt14": 3.21, "Volt15": 3.21, "Volt16": 3.21}, - "Temp_1": 15, "Temp_2": 15, "Temp_3": 15, - "out": {"Voltage": 0.14, "Current": None, "Out_Status": None, "Power": None}, - "Controller_Temp": None, "Reg_74": None, "Reg_76": None, "Reg_78": None, - "PV_Power": 37.9232, "Power": -1.0268000000000002}, - }) + "batterie": {"pv1": {"Voltage": 33.86, "Current": 1.12, "MPPT-Status": 0}, + "pv2": {"Voltage": 33.72, "Current": 0.0, "MPPT-Status": 0}, + "batt": {"Total_Charging": 20.8, "Voltage": 51.34, "Current": 7.66, "SOC": 10.0, "Power": 393.2644, 'Batt_State': 2}, + "cell": {"Volt1": 3.21, "Volt2": 3.21, "Volt3": 3.21, "Volt4": 3.21, "Volt5": 3.21, "Volt6": 3.21, "Volt7": 3.21, "Volt8": 3.21, "Volt9": 3.21, "Volt10": 3.21, "Volt11": 3.21, "Volt12": 3.21, "Volt13": 3.21, "Volt14": 3.21, "Volt15": 3.21, "Volt16": 3.21, "Temp_1": 15, "Temp_2": 15, "Temp_3": 15}, + "out": {"Voltage": 0.14, "Current": None, "Out_Status": None, "Power": None, "Suppl_State": None}, + "Controller_Temp": None, "Batterie_Alarm": None, "Hardware_Version": None, "Software_Version": None, + "PV_Power": 37.9232}, + }) def test_build_4210(inverter_data: bytes): i = InfosG3P(client_mode=False) @@ -359,7 +386,7 @@ def test_build_ha_conf5(): if id == 'out_power_123': assert comp == 'sensor' - assert d_json == json.dumps({"name": "Output Power", "stat_t": "tsun/garagendach/batterie", "dev_cla": "power", "stat_cla": "measurement", "uniq_id": "out_power_123", "val_tpl": "{{ (value_json['out']['Power'] | int)}}", "unit_of_meas": "W", "dev": {"name": "Batterie", "sa": "Batterie", "via_device": "controller_123", "mdl": "TSOL-MSxx00", "mf": "TSUN", "ids": ["batterie_123"]}, "o": {"name": "proxy", "sw": "unknown"}}) + assert d_json == json.dumps({"name": "Supply Power", "stat_t": "tsun/garagendach/batterie", "dev_cla": "power", "stat_cla": "measurement", "uniq_id": "out_power_123", "val_tpl": "{{ (value_json['out']['Power'] | int)}}", "unit_of_meas": "W", "dev": {"name": "Batterie", "sa": "Batterie", "via_device": "controller_123", "mdl": "TSOL-MSxx00", "mf": "TSUN", "ids": ["batterie_123"]}, "o": {"name": "proxy", "sw": "unknown"}}) tests +=1 elif id == 'daily_gen_123': assert False diff --git a/app/tests/test_inverter_g3.py b/app/tests/test_inverter_g3.py index e591d04..626ba7d 100644 --- a/app/tests/test_inverter_g3.py +++ b/app/tests/test_inverter_g3.py @@ -155,6 +155,7 @@ async def test_remote_except(config_conn, patch_open_connection): await asyncio.sleep(0) assert inverter.remote.stream==None del inverter + test = MockType.RD_TEST_0_BYTES cnt = 0 for inv in InverterBase: diff --git a/app/tests/test_inverter_g3p.py b/app/tests/test_inverter_g3p.py index 5b19121..f16a2d8 100644 --- a/app/tests/test_inverter_g3p.py +++ b/app/tests/test_inverter_g3p.py @@ -133,6 +133,9 @@ async def test_remote_except(config_conn, patch_open_connection): await asyncio.sleep(0) assert inverter.remote.stream==None + test = MockType.RD_TEST_0_BYTES + + @pytest.mark.asyncio async def test_mqtt_publish(config_conn, patch_open_connection): _ = config_conn diff --git a/app/tests/test_modbus_tcp.py b/app/tests/test_modbus_tcp.py index 1c69b60..dcb7e3e 100644 --- a/app/tests/test_modbus_tcp.py +++ b/app/tests/test_modbus_tcp.py @@ -190,7 +190,8 @@ def patch_mqtt_except(): yield conn @pytest.mark.asyncio -async def test_modbus_conn(patch_open): +async def test_modbus_conn(config_conn, patch_open): + _ = config_conn _ = patch_open assert Infos.stat['proxy']['Inverter_Cnt'] == 0 @@ -210,6 +211,7 @@ async def test_modbus_conn(patch_open): @pytest.mark.asyncio async def test_modbus_no_cnf(): + _ = config_conn assert Infos.stat['proxy']['Inverter_Cnt'] == 0 loop = asyncio.get_event_loop() ModbusTcp(loop) diff --git a/app/tests/test_mqtt.py b/app/tests/test_mqtt.py index 85f0ab2..421fadb 100644 --- a/app/tests/test_mqtt.py +++ b/app/tests/test_mqtt.py @@ -47,7 +47,7 @@ def config_no_conn(test_port): @pytest.fixture def spy_at_cmd(): - conn = SolarmanV5(('test.local', 1234), server_side=True, client_mode= False, ifc=AsyncIfcImpl()) + conn = SolarmanV5(None, ('test.local', 1234), server_side=True, client_mode= False, ifc=AsyncIfcImpl()) conn.node_id = 'inv_2/' with patch.object(conn, 'send_at_cmd', wraps=conn.send_at_cmd) as wrapped_conn: yield wrapped_conn @@ -55,7 +55,7 @@ def spy_at_cmd(): @pytest.fixture def spy_modbus_cmd(): - conn = SolarmanV5(('test.local', 1234), server_side=True, client_mode= False, ifc=AsyncIfcImpl()) + conn = SolarmanV5(None, ('test.local', 1234), server_side=True, client_mode= False, ifc=AsyncIfcImpl()) conn.node_id = 'inv_1/' with patch.object(conn, 'send_modbus_cmd', wraps=conn.send_modbus_cmd) as wrapped_conn: yield wrapped_conn @@ -63,7 +63,7 @@ def spy_modbus_cmd(): @pytest.fixture def spy_modbus_cmd_client(): - conn = SolarmanV5(('test.local', 1234), server_side=False, client_mode= False, ifc=AsyncIfcImpl()) + conn = SolarmanV5(None, ('test.local', 1234), server_side=False, client_mode= False, ifc=AsyncIfcImpl()) conn.node_id = 'inv_1/' with patch.object(conn, 'send_modbus_cmd', wraps=conn.send_modbus_cmd) as wrapped_conn: yield wrapped_conn diff --git a/app/tests/test_solarman.py b/app/tests/test_solarman.py index e9a28b1..0c3c86c 100644 --- a/app/tests/test_solarman.py +++ b/app/tests/test_solarman.py @@ -4,7 +4,9 @@ import time import asyncio import logging import random +from asyncio import StreamReader, StreamWriter from math import isclose + from async_stream import AsyncIfcImpl, StreamPtr from gen3plus.solarman_v5 import SolarmanV5, SolarmanBase from cnf.config import Config @@ -12,6 +14,11 @@ from infos import Infos, Register from modbus import Modbus from messages import State, Message from proxy import Proxy +from test_inverter_g3p import FakeReader, FakeWriter, patch_open_connection +from inverter_base import InverterBase +from test_modbus_tcp import test_port, test_hostname + + pytest_plugins = ('pytest_asyncio',) @@ -43,10 +50,14 @@ class FakeIfc(AsyncIfcImpl): async def create_remote(self): await asyncio.sleep(0) +class FakeInverter(): + def __init__(self): + self.forward_at_cmd_resp = False + class MemoryStream(SolarmanV5): - def __init__(self, msg, chunks = (0,), server_side: bool = True): + def __init__(self, msg, chunks = (0,), server_side: bool = True, inverter=FakeInverter()): _ifc = FakeIfc() - super().__init__(('test.local', 1234), _ifc, server_side, client_mode=False) + super().__init__(inverter, ('test.local', 1234), _ifc, server_side, client_mode=False) if server_side: self.mb.timeout = 0.4 # overwrite for faster testing self.mb_first_timeout = 0.5 @@ -816,7 +827,7 @@ def config_tsun_allow_all(): @pytest.fixture def config_no_tsun_inv1(): - Config.act_config = {'solarman':{'enabled': False},'inverters':{'Y170000000000001':{'monitor_sn': 2070233889, 'node_id':'inv1', 'modbus_polling': True, 'suggested_area':'roof', 'sensor_list': 688}}} + 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(): @@ -828,21 +839,21 @@ def config_tsun_inv1(): 'proxy_node_id': 'test_1', 'proxy_unique_id': '' }, - 'solarman':{'enabled': True},'inverters':{'Y170000000000001':{'monitor_sn': 2070233889, 'node_id':'inv1', 'modbus_polling': True, 'suggested_area':'roof', 'sensor_list': 0}}} + 'solarman':{'enabled': True, 'host': 'test_cloud.local', 'port': 1234},'inverters':{'Y170000000000001':{'monitor_sn': 2070233889, 'node_id':'inv1/', 'modbus_polling': True, 'suggested_area':'roof', 'sensor_list': 0}}} Proxy.class_init() Proxy.mqtt = Mqtt() @pytest.fixture def config_tsun_scan(): - Config.act_config = {'solarman':{'enabled': True},'inverters':{'Y170000000000001':{'monitor_sn': 2070233889, 'node_id':'inv1', 'modbus_polling': True, 'modbus_scanning': {'start': 0xffc0, 'step': 0x40, 'bytes':20}, 'suggested_area':'roof', 'sensor_list': 0}}} + Config.act_config = {'solarman':{'enabled': True},'inverters':{'Y170000000000001':{'monitor_sn': 2070233889, 'node_id':'inv1/', 'modbus_polling': True, 'modbus_scanning': {'start': 0xffc0, 'step': 0x40, 'bytes':20}, 'suggested_area':'roof', 'sensor_list': 0}}} @pytest.fixture def config_tsun_scan_dcu(): - Config.act_config = {'solarman':{'enabled': True},'inverters':{'4100000000000001':{'monitor_sn': 2070233888, 'node_id':'inv1', 'modbus_polling': True, 'modbus_scanning': {'start': 0x0000, 'step': 0x100, 'bytes':0x2d}, 'client_mode': {'host': '192.168.1.1.'}, 'suggested_area':'roof', 'sensor_list': 0}}} + Config.act_config = {'solarman':{'enabled': True},'inverters':{'4100000000000001':{'monitor_sn': 2070233888, 'node_id':'inv1/', 'modbus_polling': True, 'modbus_scanning': {'start': 0x0000, 'step': 0x100, 'bytes':0x2d}, 'client_mode': {'host': '192.168.1.1.'}, '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}}} + 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): Config.act_config = {'solarman':{'enabled': True}} @@ -1432,9 +1443,9 @@ def test_build_logger_modell(config_tsun_allow_all, device_ind_msg): def test_msg_iterator(): Message._registry.clear() - m1 = SolarmanV5(('test1.local', 1234), ifc=AsyncIfcImpl(), server_side=True, client_mode=False) - m2 = SolarmanV5(('test2.local', 1234), ifc=AsyncIfcImpl(), server_side=True, client_mode=False) - m3 = SolarmanV5(('test3.local', 1234), ifc=AsyncIfcImpl(), server_side=True, client_mode=False) + m1 = SolarmanV5(None, ('test1.local', 1234), ifc=AsyncIfcImpl(), server_side=True, client_mode=False) + m2 = SolarmanV5(None, ('test2.local', 1234), ifc=AsyncIfcImpl(), server_side=True, client_mode=False) + m3 = SolarmanV5(None, ('test3.local', 1234), ifc=AsyncIfcImpl(), server_side=True, client_mode=False) m3.close() del m3 test1 = 0 @@ -1452,7 +1463,7 @@ def test_msg_iterator(): assert test2 == 1 def test_proxy_counter(): - m = SolarmanV5(('test.local', 1234), ifc=AsyncIfcImpl(), server_side=True, client_mode=False) + m = SolarmanV5(None, ('test.local', 1234), ifc=AsyncIfcImpl(), server_side=True, client_mode=False) assert m.new_data == {} m.db.stat['proxy']['Unknown_Msg'] = 0 Infos.new_stat_data['proxy'] = False @@ -1559,7 +1570,7 @@ async def test_at_cmd(config_tsun_allow_all, device_ind_msg, device_rsp_msg, inv assert m.ifc.fwd_fifo.get()==b'' assert m.sent_pdu == b'' assert str(m.seq) == '03:04' - assert m.forward_at_cmd_resp == False + assert m.inverter.forward_at_cmd_resp == False assert Proxy.mqtt.key == '' assert Proxy.mqtt.data == "" m.close() @@ -1594,12 +1605,12 @@ async def test_at_cmd_blocked(config_tsun_allow_all, device_ind_msg, device_rsp_ assert m.ifc.tx_fifo.get()==b'' assert m.ifc.fwd_fifo.get()==b'' assert str(m.seq) == '02:02' - assert m.forward_at_cmd_resp == False + assert m.inverter.forward_at_cmd_resp == False assert Proxy.mqtt.key == 'tsun/at_resp' assert Proxy.mqtt.data == "'AT+WEBU' is forbidden" m.close() -def test_at_cmd_ind(config_tsun_inv1, at_command_ind_msg): +def test_at_cmd_ind(config_tsun_inv1, at_command_ind_msg, at_command_rsp_msg): _ = config_tsun_inv1 m = MemoryStream(at_command_ind_msg, (0,), False) m.db.stat['proxy']['Unknown_Ctrl'] = 0 @@ -1621,6 +1632,17 @@ def test_at_cmd_ind(config_tsun_inv1, at_command_ind_msg): assert m.db.stat['proxy']['AT_Command'] == 1 assert m.db.stat['proxy']['AT_Command_Blocked'] == 0 assert m.db.stat['proxy']['Modbus_Command'] == 0 + + m.append_msg(at_command_rsp_msg) + m.read() # read at resp + assert m.control == 0x1510 + assert str(m.seq) == '03:03' + assert m.ifc.rx_get()==b'' + assert m.ifc.tx_fifo.get()==b'' + assert m.ifc.fwd_fifo.get()==at_command_rsp_msg + assert Proxy.mqtt.key == '' + assert Proxy.mqtt.data == "" + m.close() def test_at_cmd_ind_block(config_tsun_inv1, at_command_ind_msg_block): @@ -1630,6 +1652,7 @@ def test_at_cmd_ind_block(config_tsun_inv1, at_command_ind_msg_block): m.db.stat['proxy']['AT_Command'] = 0 m.db.stat['proxy']['AT_Command_Blocked'] = 0 m.db.stat['proxy']['Modbus_Command'] = 0 + m.inverter.forward_at_cmd_resp = False m.read() # read complete msg, and dispatch msg assert not m.header_valid # must be invalid, since msg was handled and buffer flushed assert m.msg_count == 1 @@ -1645,6 +1668,9 @@ def test_at_cmd_ind_block(config_tsun_inv1, at_command_ind_msg_block): assert m.db.stat['proxy']['AT_Command'] == 0 assert m.db.stat['proxy']['AT_Command_Blocked'] == 1 assert m.db.stat['proxy']['Modbus_Command'] == 0 + assert m.inverter.forward_at_cmd_resp == False + assert Proxy.mqtt.key == '' + assert Proxy.mqtt.data == "" m.close() def test_msg_at_command_rsp1(config_tsun_inv1, at_command_rsp_msg): @@ -1652,7 +1678,7 @@ def test_msg_at_command_rsp1(config_tsun_inv1, at_command_rsp_msg): m = MemoryStream(at_command_rsp_msg) m.db.stat['proxy']['Unknown_Ctrl'] = 0 m.db.stat['proxy']['Modbus_Command'] = 0 - m.forward_at_cmd_resp = True + m.inverter.forward_at_cmd_resp = True m.read() # read complete msg, and dispatch msg assert not m.header_valid # must be invalid, since msg was handled and buffer flushed assert m.msg_count == 1 @@ -1671,7 +1697,7 @@ def test_msg_at_command_rsp2(config_tsun_inv1, at_command_rsp_msg): m = MemoryStream(at_command_rsp_msg) m.db.stat['proxy']['Unknown_Ctrl'] = 0 m.db.stat['proxy']['Modbus_Command'] = 0 - m.forward_at_cmd_resp = False + m.inverter.forward_at_cmd_resp = False m.read() # read complete msg, and dispatch msg assert not m.header_valid # must be invalid, since msg was handled and buffer flushed assert m.msg_count == 1 @@ -1683,6 +1709,8 @@ def test_msg_at_command_rsp2(config_tsun_inv1, at_command_rsp_msg): assert m.ifc.tx_fifo.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 assert m.db.stat['proxy']['Modbus_Command'] == 0 + assert Proxy.mqtt.key == 'tsun/inv1/at_resp' + assert Proxy.mqtt.data == "+ok" m.close() def test_msg_at_command_rsp3(config_tsun_inv1, at_command_interim_rsp_msg): @@ -1692,7 +1720,7 @@ def test_msg_at_command_rsp3(config_tsun_inv1, at_command_interim_rsp_msg): m.db.stat['proxy']['Modbus_Command'] = 0 m.db.stat['proxy']['Invalid_Msg_Format'] = 0 m.db.stat['proxy']['Unknown_Msg'] = 0 - m.forward_at_cmd_resp = True + m.inverter.forward_at_cmd_resp = True m.read() # read complete msg, and dispatch msg assert not m.header_valid # must be invalid, since msg was handled and buffer flushed assert m.msg_count == 1 @@ -1706,6 +1734,8 @@ def test_msg_at_command_rsp3(config_tsun_inv1, at_command_interim_rsp_msg): assert m.db.stat['proxy']['Modbus_Command'] == 0 assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 assert m.db.stat['proxy']['Unknown_Msg'] == 0 + assert Proxy.mqtt.key == '' + assert Proxy.mqtt.data == "" m.close() def test_msg_modbus_req(config_tsun_inv1, msg_modbus_cmd, msg_modbus_cmd_fwd): @@ -2218,4 +2248,100 @@ def test_timestamp(): m = MemoryStream(b'') ts = m._timestamp() ts_emu = m._emu_timestamp() - assert ts == ts_emu + 24*60*60 \ No newline at end of file + assert ts == ts_emu + 24*60*60 + +class MemoryStream2(MemoryStream): + def __init__(self, inverter, addr, ifc, + server_side: bool, client_mode: bool): + super().__init__(b'', inverter=inverter) + + +class InverterTest(InverterBase): + def __init__(self, reader: StreamReader, writer: StreamWriter, + client_mode: bool = False): + remote_prot = None + super().__init__(reader, writer, 'solarman', + MemoryStream2, client_mode, remote_prot) + + def forward(self, src, dst) -> None: + """forward handler transmits data over the remote connection""" + # dst.ifc.update_header_cb(src.fwd_fifo.peek()) + + dst.ifc.tx_add(src.ifc.fwd_fifo.get()) + + +@pytest.mark.asyncio +async def test_proxy_at_cmd(config_tsun_inv1, patch_open_connection, at_command_ind_msg, at_command_rsp_msg): + _ = config_tsun_inv1 + _ = patch_open_connection + assert asyncio.get_running_loop() + + with InverterTest(FakeReader(), FakeWriter(), client_mode=False) as inverter: + await inverter.create_remote() + await asyncio.sleep(0) + r = inverter.remote.stream + l = inverter.local.stream + + l.db.stat['proxy']['AT_Command'] = 0 + l.db.stat['proxy']['Unknown_Ctrl'] = 0 + l.db.stat['proxy']['AT_Command_Blocked'] = 0 + l.db.stat['proxy']['Modbus_Command'] = 0 + inverter.forward_at_cmd_resp = False + r.append_msg(at_command_ind_msg) + r.read() # read complete msg, and dispatch msg + assert inverter.forward_at_cmd_resp + inverter.forward(r,l) + + assert l.ifc.tx_fifo.get()==at_command_ind_msg + + assert l.db.stat['proxy']['Invalid_Msg_Format'] == 0 + assert l.db.stat['proxy']['AT_Command'] == 1 + assert l.db.stat['proxy']['AT_Command_Blocked'] == 0 + assert l.db.stat['proxy']['Modbus_Command'] == 0 + + l.append_msg(at_command_rsp_msg) + l.read() # read at resp + assert l.ifc.fwd_fifo.peek()==at_command_rsp_msg + inverter.forward(l,r) + assert r.ifc.tx_fifo.get()==at_command_rsp_msg + + assert Proxy.mqtt.key == '' + assert Proxy.mqtt.data == "" + +@pytest.mark.asyncio +async def test_proxy_at_blocked(config_tsun_inv1, patch_open_connection, at_command_ind_msg_block, at_command_rsp_msg): + _ = config_tsun_inv1 + _ = patch_open_connection + assert asyncio.get_running_loop() + + with InverterTest(FakeReader(), FakeWriter(), client_mode=False) as inverter: + await inverter.create_remote() + await asyncio.sleep(0) + r = inverter.remote.stream + l = inverter.local.stream + + l.db.stat['proxy']['AT_Command'] = 0 + l.db.stat['proxy']['Unknown_Ctrl'] = 0 + l.db.stat['proxy']['AT_Command_Blocked'] = 0 + l.db.stat['proxy']['Modbus_Command'] = 0 + inverter.forward_at_cmd_resp = False + r.append_msg(at_command_ind_msg_block) + r.read() # read complete msg, and dispatch msg + assert not inverter.forward_at_cmd_resp + inverter.forward(r,l) + + assert l.ifc.tx_fifo.get()==b'' + + assert l.db.stat['proxy']['Invalid_Msg_Format'] == 0 + assert l.db.stat['proxy']['AT_Command'] == 0 + assert l.db.stat['proxy']['AT_Command_Blocked'] == 1 + assert l.db.stat['proxy']['Modbus_Command'] == 0 + + l.append_msg(at_command_rsp_msg) + l.read() # read at resp + assert l.ifc.fwd_fifo.peek()==b'' + inverter.forward(l,r) + assert r.ifc.tx_fifo.get()==b'' + + assert Proxy.mqtt.key == 'tsun/inv1/at_resp' + assert Proxy.mqtt.data == "+ok" diff --git a/app/tests/test_solarman_emu.py b/app/tests/test_solarman_emu.py index 41e0e48..a62fbdc 100644 --- a/app/tests/test_solarman_emu.py +++ b/app/tests/test_solarman_emu.py @@ -6,7 +6,7 @@ from gen3plus.solarman_v5 import SolarmanV5, SolarmanBase from gen3plus.solarman_emu import SolarmanEmu from infos import Infos, Register -from test_solarman import FakeIfc, MemoryStream, get_sn_int, get_sn, correct_checksum, config_tsun_inv1, msg_modbus_rsp +from test_solarman import FakeIfc, FakeInverter, MemoryStream, get_sn_int, get_sn, correct_checksum, config_tsun_inv1, msg_modbus_rsp from test_infos_g3p import str_test_ip, bytes_test_ip timestamp = 0x3224c8bc @@ -19,10 +19,10 @@ class InvStream(MemoryStream): return timestamp class CldStream(SolarmanEmu): - def __init__(self, inv: InvStream): + def __init__(self, inv: InvStream, inverter=FakeInverter()): _ifc = FakeIfc() _ifc.remote.stream = inv - super().__init__(('test.local', 1234), _ifc, server_side=False, client_mode=False) + super().__init__(inverter, ('test.local', 1234), _ifc, server_side=False, client_mode=False) self.__msg = b'' self.__msg_len = 0 self.__offs = 0 diff --git a/app/tests/test_talent.py b/app/tests/test_talent.py index 2e7a4f0..225c38e 100644 --- a/app/tests/test_talent.py +++ b/app/tests/test_talent.py @@ -25,7 +25,7 @@ class FakeIfc(AsyncIfcImpl): class MemoryStream(Talent): def __init__(self, msg, chunks = (0,), server_side: bool = True): self.ifc = FakeIfc() - super().__init__(('test.local', 1234), self.ifc, server_side) + super().__init__(None, ('test.local', 1234), self.ifc, server_side) if server_side: self.mb.timeout = 0.4 # overwrite for faster testing self.mb_first_timeout = 0.5 @@ -2026,9 +2026,9 @@ def test_ctrl_byte(): def test_msg_iterator(): - m1 = Talent(('test1.local', 1234), ifc=AsyncIfcImpl(), server_side=True) - m2 = Talent(('test2.local', 1234), ifc=AsyncIfcImpl(), server_side=True) - m3 = Talent(('test3.local', 1234), ifc=AsyncIfcImpl(), server_side=True) + m1 = Talent(None, ('test1.local', 1234), ifc=AsyncIfcImpl(), server_side=True) + m2 = Talent(None, ('test2.local', 1234), ifc=AsyncIfcImpl(), server_side=True) + m3 = Talent(None, ('test3.local', 1234), ifc=AsyncIfcImpl(), server_side=True) m3.close() del m3 test1 = 0 diff --git a/ha_addons/.gitignore b/ha_addons/.gitignore index 8a014ce..1e60ea6 100644 --- a/ha_addons/.gitignore +++ b/ha_addons/.gitignore @@ -1,3 +1,4 @@ .data.json config.yaml -apparmor.txt \ No newline at end of file +apparmor.txt +README.md \ No newline at end of file diff --git a/ha_addons/Makefile b/ha_addons/Makefile index 15b8794..ab162c4 100644 --- a/ha_addons/Makefile +++ b/ha_addons/Makefile @@ -47,7 +47,16 @@ dev debug: local_add_on export IMAGE=$(PRIVAT_CONTAINER_REGISTRY)$(IMAGE) && \ docker buildx bake -f docker-bake.hcl $@ -rc rel: local_add_on +rc: local_add_on + @[ "${RC}" ] || ( echo ">> RC is not set"; exit 1 ) + @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)-$@$(RC) && \ + export IMAGE=$(PUBLIC_CONTAINER_REGISTRY)$(IMAGE) && \ + docker buildx bake -f docker-bake.hcl $@ + +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)" @@ -66,7 +75,7 @@ clean: # 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 $(ADDON_PATH)/apparmor.txt +local_add_on: rootfs $(ADDON_PATH)/config.yaml $(ADDON_PATH)/apparmor.txt $(ADDON_PATH)/README.md # collect source files SRC_FILES := $(wildcard $(SRC_PROXY)/*.py)\ @@ -103,6 +112,9 @@ $(ADDON_PATH)/%.yaml: $(TEMPL)/%.jinja $(TEMPL)/.data.json $(ADDON_PATH)/%.txt: $(TEMPL)/%.jinja $(TEMPL)/.data.json $(JINJA) --strict --format=json $^ -o $@ +$(ADDON_PATH)/%.md: $(TEMPL)/%.jinja $(TEMPL)/.data.json + $(JINJA) --strict --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 @@ -119,6 +131,7 @@ repro_files = DOCS.md icon.png logo.png translations/de.yaml translations/en.yam repro_root = CHANGELOG.md LICENSE.md repro_templates = config.yaml repro_apparmor = apparmor.txt +repro_readme = README.md repro_subdirs = translations rootfs repro_vers = debug dev rc rel @@ -126,29 +139,34 @@ repro_all_files := $(foreach dir,$(repro_vers), $(foreach file,$(repro_files),$( repro_root_files := $(foreach dir,$(repro_vers), $(foreach file,$(repro_root),$(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_apparmor := $(foreach dir,$(repro_vers), $(foreach file,$(repro_apparmor),$(INST_BASE)/ha_addon_$(dir)/$(file))) +repro_all_readme := $(foreach dir,$(repro_vers), $(foreach file,$(repro_readme),$(INST_BASE)/ha_addon_$(dir)/$(file))) repro_all_subdirs := $(foreach dir,$(repro_vers), $(foreach file,$(repro_subdirs),$(INST_BASE)/ha_addon_$(dir)/$(file))) debug: $(foreach file,$(repro_subdirs),$(INST_BASE)/ha_addon_debug/$(file)) \ $(foreach file,$(repro_templates),$(INST_BASE)/ha_addon_debug/$(file)) \ $(foreach file,$(repro_apparmor),$(INST_BASE)/ha_addon_debug/$(file)) \ + $(foreach file,$(repro_readme),$(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_apparmor),$(INST_BASE)/ha_addon_dev/$(file)) \ + $(foreach file,$(repro_readme),$(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_apparmor),$(INST_BASE)/ha_addon_rc/$(file)) \ + $(foreach file,$(repro_readme),$(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_apparmor),$(INST_BASE)/ha_addon_rel/$(file)) \ + $(foreach file,$(repro_readme),$(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)) @@ -161,6 +179,9 @@ $(repro_all_templates) : $(INST_BASE)/ha_addon_%/config.yaml: $(TEMPL)/config.ji $(repro_all_apparmor) : $(INST_BASE)/ha_addon_%/apparmor.txt: $(TEMPL)/apparmor.jinja $(TEMPL)/%_data.json $(JINJA) --strict $< $(filter %.json,$^) -o $@ +$(repro_all_readme) : $(INST_BASE)/ha_addon_%/README.md: $(TEMPL)/README.jinja $(TEMPL)/%_data.json + $(JINJA) --strict $< $(filter %.json,$^) -o $@ + $(filter $(INST_BASE)/ha_addon_debug/%,$(repro_root_files)) : $(INST_BASE)/ha_addon_debug/% : ../% cp $< $@ $(filter $(INST_BASE)/ha_addon_dev/%,$(repro_root_files)) : $(INST_BASE)/ha_addon_dev/% : ../% diff --git a/ha_addons/ha_addon/Dockerfile b/ha_addons/ha_addon/Dockerfile index 9d3ecdb..1d9df4d 100755 --- a/ha_addons/ha_addon/Dockerfile +++ b/ha_addons/ha_addon/Dockerfile @@ -13,7 +13,7 @@ # 1 Build Base Image # ###################### -ARG BUILD_FROM="ghcr.io/hassio-addons/base:17.2.3" +ARG BUILD_FROM="ghcr.io/hassio-addons/base:17.2.4" # hadolint ignore=DL3006 FROM $BUILD_FROM AS base diff --git a/ha_addons/templates/README.jinja b/ha_addons/templates/README.jinja new file mode 100644 index 0000000..421ddad --- /dev/null +++ b/ha_addons/templates/README.jinja @@ -0,0 +1,21 @@ +# Home Assistant Add-on: {{name}} + +{{readme_descr}} + +## Features + +- Supports TSUN GEN3 PLUS inverters: TSOL-MS2000, MS1800 and MS1600 +- Supports TSUN GEN3 PLUS batteries: TSOL-DC1000 (from version 0.13) +- Supports TSUN GEN3 inverters: TSOL-MS3000, MS800, MS700, MS600, MS400, MS350 and MS300 +- `Home-Assistant` auto-discovery support +- `MODBUS` support via MQTT topics +- `AT-Command` support via MQTT topics (GEN3PLUS only) +- Faster DataUp interval sends measurement data to the MQTT broker every minute +- Self-sufficient island operation without internet +- Security-Features: + - control access via `AT-commands` + +## About + +This Add-on and the TSUN Proxy is not related to the company TSUN. It is a private initiative that aims to connect TSUN inverters and storage systems with an MQTT broker. There is no support and no warranty from TSUN. +{{readme_links}} \ No newline at end of file diff --git a/ha_addons/templates/debug_data.json b/ha_addons/templates/debug_data.json index fcfdfe7..47fa2d2 100644 --- a/ha_addons/templates/debug_data.json +++ b/ha_addons/templates/debug_data.json @@ -5,5 +5,7 @@ "image": "docker.io/sallius/tsun-gen3-addon", "slug": "tsun-proxy-debug", "advanced": true, - "stage": "experimental" + "stage": "experimental", + "readme_descr": "This is a bleeding-edge version of the `TSUN Proxy` Add-On with debuging enabled by default.\n\nThe versions may be based on different feature branches and therefore the range of functions may change.\n\nIt is intended to be used to simulate special situations/problems and should only be used in consultation with the maintainer.\n\nFor production please use the stable version `TSUN Proxy`. If you are interested in a bleeding edge version, we offer the `TSUN Proxy (dev)` version.", + "readme_links": "" } \ No newline at end of file diff --git a/ha_addons/templates/dev_data.json b/ha_addons/templates/dev_data.json index a7fbb82..68e933c 100644 --- a/ha_addons/templates/dev_data.json +++ b/ha_addons/templates/dev_data.json @@ -5,5 +5,7 @@ "image": "docker.io/sallius/tsun-gen3-addon", "slug": "tsun-proxy-dev", "advanced": false, - "stage": "experimental" + "stage": "experimental", + "readme_descr": "This is a bleeding-edge version of the `TSUN Proxy` Add-On.\n\nThe versions may be based on different feature branches and therefore the range of functions may change.\n\nIt is intended for testing new functions or testing new devices that are to be supported with the next release.\nFor production, please use the stable version 'TSUN Proxy'.", + "readme_links": "" } \ No newline at end of file diff --git a/ha_addons/templates/rc_data.json b/ha_addons/templates/rc_data.json index 6f21cc4..05d33ce 100644 --- a/ha_addons/templates/rc_data.json +++ b/ha_addons/templates/rc_data.json @@ -6,5 +6,8 @@ "image": "ghcr.io/s-allius/tsun-gen3-addon", "slug": "tsun-proxy-rc", "advanced": true, - "stage": "experimental" + "stage": "experimental", + "readme_descr": "This is a release candidate of the `TSUN Proxy` Add-On.\n\nIt is intended for testing the next release.\nFor production, please use the stable version 'TSUN Proxy'.", + "readme_links": "" + } \ No newline at end of file diff --git a/ha_addons/templates/rel_data.json b/ha_addons/templates/rel_data.json index d6046f5..edf673c 100644 --- a/ha_addons/templates/rel_data.json +++ b/ha_addons/templates/rel_data.json @@ -5,5 +5,7 @@ "image": "ghcr.io/s-allius/tsun-gen3-addon", "slug": "tsun-proxy", "advanced": false, - "stage": "stable" + "stage": "stable", + "readme_dsecr": "Integrates TSUN inverters (e.g. TSOL MS800, MS2000, MS3000) and batteries (TSOL DC1000) into Home Assistant.\n\nIt is based on the [TSUN Proxy][tsunproxy] and enables a reliable connection between TSUN devices and an MQTT broker.\n\nWith the Add-on, you can easily retrieve real-time values such as power, current and daily energy and integrate the inverter into Home Assistant.\nThis works even without an internet connection.\n\nThe optional connection to the TSUN Cloud can be disabled!", + "readme_links": "\n[tsunproxy]: https://github.com/s-allius/tsun-gen3-proxy\n" } \ No newline at end of file diff --git a/system_tests/test_tcp_socket_v2.py b/system_tests/test_tcp_socket_v2.py index 94816fd..faa0e63 100644 --- a/system_tests/test_tcp_socket_v2.py +++ b/system_tests/test_tcp_socket_v2.py @@ -5,13 +5,14 @@ from dotenv import load_dotenv load_dotenv() -SOLARMAN_SNR = os.getenv('SOLARMAN_SNR', '00000080') +SOLARMAN_INV_SNR = os.getenv('SOLARMAN_INV_SNR', '00000080') +SOLARMAN_DCU_SNR = os.getenv('SOLARMAN_DCU_SNR', '00000080') def get_sn() -> bytes: - return bytes.fromhex(SOLARMAN_SNR) + return bytes.fromhex(SOLARMAN_INV_SNR) def get_dcu_sn() -> bytes: - return b'\x20\x43\x65\x7b' + return bytes.fromhex(SOLARMAN_DCU_SNR) def get_dcu_no() -> bytes: return b'4100000000000001' @@ -27,7 +28,7 @@ def correct_checksum(buf): return checksum.to_bytes(length=1) @pytest.fixture -def MsgContactInfo(): # Contact Info message +def msg_contact_info(): # Contact Info message msg = b'\xa5\xd4\x00\x10\x41\x00\x01' +get_sn() +b'\x02\xba\xd2\x00\x00' msg += b'\x19\x00\x00\x00\x00\x00\x00\x00\x05\x3c\x78\x01\x64\x01\x4c\x53' msg += b'\x57\x35\x42\x4c\x45\x5f\x31\x37\x5f\x30\x32\x42\x30\x5f\x31\x2e' @@ -46,13 +47,13 @@ def MsgContactInfo(): # Contact Info message return msg @pytest.fixture -def MsgContactResp(): # Contact Response message +def msg_contact_resp(): # Contact Response message msg = b'\xa5\x0a\x00\x10\x11\x01\x01' +get_sn() +b'\x02\x01\x6a\xfd\x8f' msg += b'\x65\x3c\x00\x00\x00\x75\x15' return msg @pytest.fixture -def MsgDataInd(): +def msg_data_ind(): msg = b'\xa5\x99\x01\x10\x42\x59\x84' +get_sn() +b'\x01\xb0\x02\x2c\x87' msg += b'\x22\x32\xb7\x29\x00\x00\xd6\xcf\xe1\x33\x01\x00\x0c\x05\x00\x00' msg += b'\x59\x31\x37\x45\x37\x41\x30\x46\x30\x31\x30\x42\x30\x31\x33\x45' @@ -86,14 +87,14 @@ def MsgDataInd(): return msg @pytest.fixture -def MsgDataResp(): # Contact Response message +def msg_data_rsp(): # Contact Response message msg = b'\xa5\x0a\x00\x10\x12\x80\x84' +get_sn() +b'\x01\x01\xd1\x96\x04' msg += b'\x66\x3c\x00\x00\x00\xed\x15' return msg @pytest.fixture -def MsgInvalidInfo(): # Contact Info message wrong start byte +def msg_invalid_info(): # Contact Info message wrong start byte msg = b'\x47\xd4\x00\x10\x41\x00\x01' +get_sn() +b'\x02\xba\xd2\x00\x00' msg += b'\x19\x00\x00\x00\x00\x00\x00\x00\x05\x3c\x78\x01\x64\x01\x4c\x53' msg += b'\x57\x35\x42\x4c\x45\x5f\x31\x37\x5f\x30\x32\x42\x30\x5f\x31\x2e' @@ -152,9 +153,9 @@ def dcu_data_ind_msg(): # 0x4210 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\x0a\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'\x14\x0e\x05\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'\x0c\x89\x0c\x89\x0c\x88\x00\x0f\x00\x0f\x00\x0f\x00\x0e\x02\x00' msg += b'\x00\x00\x00\x0f\x00\x00\x02\x05\x02\x01' msg += correct_checksum(msg) msg += b'\x15' @@ -169,7 +170,7 @@ def dcu_data_rsp_msg(): # 0x1210 return msg @pytest.fixture(scope="session") -def ClientConnection(): +def client_connection(): host = 'logger.talent-monitoring.com' port = 10000 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: @@ -178,15 +179,15 @@ def ClientConnection(): yield s s.close() -def checkResponse(data, Msg): +def check_response(data, msg): check = bytearray(data) - check[5]= Msg[5] # ignore seq - check[13:18]= Msg[13:18] # ignore timestamp + first byte of repeat time - check[21]= Msg[21] # ignore crc - assert check == Msg + check[5]= msg[5] # ignore seq + check[13:18]= msg[13:18] # ignore timestamp + first byte of repeat time + check[21]= msg[21] # ignore crc + assert check == msg -def tempClientConnection(): +def tempclient_connection(): host = 'logger.talent-monitoring.com' port = 10000 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: @@ -198,53 +199,53 @@ def tempClientConnection(): def test_open_close(): try: - for _ in tempClientConnection(): - pass # test generator tempClientConnection() - except: + for _ in tempclient_connection(): + pass # test generator tempclient_connection() + except TimeoutError: assert False -def test_conn_msg(ClientConnection,MsgContactInfo, MsgContactResp): - s = ClientConnection +def test_conn_msg(client_connection,msg_contact_info, msg_contact_resp): + s = client_connection try: - s.sendall(MsgContactInfo) - # time.sleep(2.5) + s.sendall(msg_contact_info) + time.sleep(2.5) data = s.recv(1024) except TimeoutError: pass # time.sleep(2.5) - checkResponse(data, MsgContactResp) + check_response(data, msg_contact_resp) -def test_data_ind(ClientConnection,MsgDataInd, MsgDataResp): - s = ClientConnection +def test_data_ind(client_connection,msg_data_ind, msg_data_rsp): + s = client_connection try: - s.sendall(MsgDataInd) + s.sendall(msg_data_ind) # time.sleep(2.5) data = s.recv(1024) except TimeoutError: pass # time.sleep(2.5) - checkResponse(data, MsgDataResp) + check_response(data, msg_data_rsp) -def test_inavlid_msg(ClientConnection,MsgInvalidInfo,MsgContactInfo, MsgContactResp): - s = ClientConnection +def test_inavlid_msg(client_connection,msg_invalid_info,msg_contact_info, msg_contact_resp): + s = client_connection try: - s.sendall(MsgInvalidInfo) + s.sendall(msg_invalid_info) # time.sleep(2.5) data = s.recv(1024) except TimeoutError: pass # time.sleep(2.5) try: - s.sendall(MsgContactInfo) + s.sendall(msg_contact_info) # time.sleep(2.5) data = s.recv(1024) except TimeoutError: pass # time.sleep(2.5) - checkResponse(data, MsgContactResp) + check_response(data, msg_contact_resp) -def test_dcu_dev(ClientConnection,dcu_dev_ind_msg, dcu_dev_rsp_msg): - s = ClientConnection +def test_dcu_dev(client_connection,dcu_dev_ind_msg, dcu_dev_rsp_msg): + s = client_connection try: s.sendall(dcu_dev_ind_msg) # time.sleep(2.5) @@ -252,10 +253,10 @@ def test_dcu_dev(ClientConnection,dcu_dev_ind_msg, dcu_dev_rsp_msg): except TimeoutError: pass # time.sleep(2.5) - checkResponse(data, dcu_dev_rsp_msg) + check_response(data, dcu_dev_rsp_msg) -def test_dcu_ind(ClientConnection,dcu_data_ind_msg, dcu_data_rsp_msg): - s = ClientConnection +def test_dcu_ind(client_connection,dcu_data_ind_msg, dcu_data_rsp_msg): + s = client_connection try: s.sendall(dcu_data_ind_msg) # time.sleep(2.5) @@ -263,4 +264,4 @@ def test_dcu_ind(ClientConnection,dcu_data_ind_msg, dcu_data_rsp_msg): except TimeoutError: pass # time.sleep(2.5) - checkResponse(data, dcu_data_rsp_msg) + check_response(data, dcu_data_rsp_msg)