From 23c404942bea9e5bd48479d586bc3da8119514f7 Mon Sep 17 00:00:00 2001 From: Johan Meijer <16508298+johanmeijer@users.noreply.github.com> Date: Mon, 6 Feb 2023 16:33:09 +0100 Subject: [PATCH 01/27] Rebased Issue-359 on top of Beta 2.8 #1 --- README.md | 7 ------- 1 file changed, 7 deletions(-) diff --git a/README.md b/README.md index 6de3df4..f17e556 100644 --- a/README.md +++ b/README.md @@ -40,13 +40,6 @@ The purpose of Grott is to read, parse and forward the *raw metrics as they are * Added option to add inverter serial to MQTT topic (thanks to @ebosveld) - Add mqttinverterintopic = True to MQTT section of grott.ini or use qmqttinverterintopic = "True" environmental (e.g. docker). -### planned in Version 2.7.x (not commited yet) -* Auto detect for SPF, SPH, TL3 inverters -* Improved / configurable PVOutput support -* MQTT Retain message support -* Enhanced record layout for SPH -* tbd - ### Two modes of metric data retrieval Grott can intercept the inverter metrics in two distinct modes: * Proxy mode (man in the middle): The Growatt ShineWifi or ShineLAN box can be easily configured to use Grott as an alternative server to the default server.growatt.com. Grott then acts as a relay to the Growatt servers. Grott reads the transmitted data, and then forwards the data to server.grott.com. From a3a95806bbdb951e3104a1a458b5b53d47d81510 Mon Sep 17 00:00:00 2001 From: Johan Meijer <16508298+johanmeijer@users.noreply.github.com> Date: Mon, 6 Feb 2023 16:35:00 +0100 Subject: [PATCH 02/27] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index f17e556..0e96dc3 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,8 @@ The purpose of Grott is to read, parse and forward the *raw metrics as they are * Added option to add inverter serial to MQTT topic (thanks to @ebosveld) - Add mqttinverterintopic = True to MQTT section of grott.ini or use qmqttinverterintopic = "True" environmental (e.g. docker). +### For all changes see: https://github.com/johanmeijer/grott/blob/Master-(2.7.8)/Version_history.txt + ### Two modes of metric data retrieval Grott can intercept the inverter metrics in two distinct modes: * Proxy mode (man in the middle): The Growatt ShineWifi or ShineLAN box can be easily configured to use Grott as an alternative server to the default server.growatt.com. Grott then acts as a relay to the Growatt servers. Grott reads the transmitted data, and then forwards the data to server.grott.com. From 2388ffff749a839152e14d69150b74bdb1c69124 Mon Sep 17 00:00:00 2001 From: Peter Havekes Date: Mon, 6 Feb 2023 16:13:23 +0100 Subject: [PATCH 03/27] Update grott.ini Remove duplicate line in example grott.ini, fix comment --- examples/grott.ini | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/examples/grott.ini b/examples/grott.ini index 632dcb7..d5e4ae0 100644 --- a/examples/grott.ini +++ b/examples/grott.ini @@ -19,9 +19,8 @@ #port = 5279 # To blocks commands from outside (to channge inverter and shine devices settings) specify blockcmd = True, -# specify noipf = True if you still want be able to dest ip addres from growatt server -# Specify noipf = True if you still want be able to dest ip addres from growatt server (advice only to use -# this for a short time) +# Specify noipf = True if you still want be able to set the destination ip addres from growatt server (advice +# only to use this for a short time) #blockcmd = True #noipf = True @@ -106,4 +105,4 @@ #extension = True #extname = grottext -#extvar = {"var1": "var1_content", "var2": "var2_content"} \ No newline at end of file +#extvar = {"var1": "var1_content", "var2": "var2_content"} From 19cf647884196b8f5cc958b106c8dafff68b8c5a Mon Sep 17 00:00:00 2001 From: egguy Date: Wed, 15 Feb 2023 00:31:42 +1100 Subject: [PATCH 04/27] Corrected a bug in the generation of configuration. Thx @Maes152 for reporting. - Added unit tests --- examples/Home Assistent/grott_ha.py | 54 ++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/examples/Home Assistent/grott_ha.py b/examples/Home Assistent/grott_ha.py index 2e75bd9..674622f 100644 --- a/examples/Home Assistent/grott_ha.py +++ b/examples/Home Assistent/grott_ha.py @@ -8,13 +8,16 @@ from grottconf import Conf -__version__ = "0.0.7-rc2" +__version__ = "0.0.7-rc3" """A pluging for grott This plugin allow to have autodiscovery of the device in HA Should be able to support multiples inverters +Version 0.0.7 + - Corrected a bug when creating the configuration + Config: - ha_mqtt_host (required): The host of the MQTT broker user by HA (often the IP of HA) - ha_mqtt_port (required): The port (the default is oftent 1883) @@ -522,14 +525,13 @@ def make_payload(conf: Conf, device: str, name: str, key: str, unit: str = None) layout = conf.recorddict[conf.layout] if "value_template" not in payload and key in layout: # From grottdata:207, default type is num, also process numx - if layout[key].get("type", "num") in ("num", "numx") and layout[key].get( - "divide", "1" - ): + if layout[key].get("type", "num") in ("num", "numx"): + divider = layout[key].get("divide", "1") payload[ "value_template" ] = "{{{{ value_json.{key} | float / {divide} }}}}".format( key=key, - divide=layout[key].get("divide"), + divide=divider, ) if "value_template" not in payload: @@ -690,3 +692,45 @@ def grottext(conf: Conf, data: str, jsonmsg: str): # Reset connection state in case of problem return 2 return 0 + + +def test_generate_payload(): + "Test that an auto generated payload for MQTT configuration" + + class TestConf(): + recorddict = { + "test": { + "pvpowerout": {"value" :122, "length" : 4, "type" : "num", "divide" : 10} + } + } + layout = "test" + + payload = make_payload(TestConf(), "NCO7410", "pvpowerout", "pvpowerout") + print(payload) + # The default divider for pvpowerout is 10 + assert payload["value_template"] == "{{ value_json.pvpowerout | float / 10 }}" + assert payload["name"] == "NCO7410 PV Output (Actual)" + assert payload["unique_id"] == "grott_NCO7410_pvpowerout" + assert payload["state_class"] == "measurement" + assert payload["device_class"] == "power" + assert payload["unit_of_measurement"] == "W" + +def test_generate_payload_without_divider(): + "Test that an auto generated payload for MQTT configuration" + class TestConf(): + recorddict = { + "test": { + "pvpowerout": {"value" :122, "length" : 4, "type" : "num", } + } + } + layout = "test" + + payload = make_payload(TestConf(), "NCO7410", "pvpowerout", "pvpowerout") + print(payload) + # The default divider for pvpowerout is 10 + assert payload["value_template"] == "{{ value_json.pvpowerout | float / 1 }}" + assert payload["name"] == "NCO7410 PV Output (Actual)" + assert payload["unique_id"] == "grott_NCO7410_pvpowerout" + assert payload["state_class"] == "measurement" + assert payload["device_class"] == "power" + assert payload["unit_of_measurement"] == "W" \ No newline at end of file From 2a4aff878cea8c8cb0295a1e6a1f40eacf76544c Mon Sep 17 00:00:00 2001 From: egguy Date: Wed, 15 Feb 2023 10:35:11 +1100 Subject: [PATCH 05/27] More debug message + QoS 1 --- examples/Home Assistent/grott_ha.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/examples/Home Assistent/grott_ha.py b/examples/Home Assistent/grott_ha.py index 674622f..439aaa3 100644 --- a/examples/Home Assistent/grott_ha.py +++ b/examples/Home Assistent/grott_ha.py @@ -17,6 +17,7 @@ Version 0.0.7 - Corrected a bug when creating the configuration + - Add QoS 1 to reduce the possibility of lost message. Config: - ha_mqtt_host (required): The host of the MQTT broker user by HA (often the IP of HA) @@ -625,7 +626,7 @@ def grottext(conf: Conf, data: str, jsonmsg: str): conf, "layout", None ): configs_payloads = [] - print(f"\tGrott HA {__version__} - creating {device_serial} config in HA") + print(f"\tGrott HA {__version__} - creating {device_serial} config in HA, {len(values.keys())} to push") for key in values.keys(): # Generate a configuration payload payload = make_payload(conf, device_serial, key, key) @@ -644,6 +645,7 @@ def grottext(conf: Conf, data: str, jsonmsg: str): "topic": topic, "payload": json.dumps(payload), "retain": True, + "qos": 1, } ) except Exception as e: @@ -667,6 +669,7 @@ def grottext(conf: Conf, data: str, jsonmsg: str): "topic": topic, "payload": json.dumps(payload), "retain": True, + "qos": 1, } ) except Exception as e: @@ -674,7 +677,9 @@ def grottext(conf: Conf, data: str, jsonmsg: str): f"\t - [grott HA] {__version__} Exception while creating new sensor last push: {e}" ) return 4 + print(f"\tPushing {len(configs_payloads)} configurations payload to HA") publish_multiple(conf, configs_payloads) + print(f"\tConfigurations pushed") # Now it's configured, no need to come back MqttStateHandler.set_configured(device_serial) @@ -682,7 +687,7 @@ def grottext(conf: Conf, data: str, jsonmsg: str): print(f"\t[Grott HA] {__version__} Can't configure device: {device_serial}") return 7 - # Push the vales to the topics + # Push the values to the topics try: publish_single( conf, state_topic.format(device=device_serial), json.dumps(values) From 48dde6a7c51aa1b1615b9b53ca193c4068f06c20 Mon Sep 17 00:00:00 2001 From: egguy Date: Wed, 15 Feb 2023 10:46:56 +1100 Subject: [PATCH 06/27] Bump to RC4 --- examples/Home Assistent/grott_ha.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/Home Assistent/grott_ha.py b/examples/Home Assistent/grott_ha.py index 439aaa3..199e933 100644 --- a/examples/Home Assistent/grott_ha.py +++ b/examples/Home Assistent/grott_ha.py @@ -8,7 +8,7 @@ from grottconf import Conf -__version__ = "0.0.7-rc3" +__version__ = "0.0.7-rc4" """A pluging for grott This plugin allow to have autodiscovery of the device in HA From 7a460c6f901018df66489f220c5556ab1f959208 Mon Sep 17 00:00:00 2001 From: egguy Date: Wed, 15 Feb 2023 22:46:43 +1100 Subject: [PATCH 07/27] Updated unit of time for the totalwork time measure --- examples/Home Assistent/grott_ha.py | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/examples/Home Assistent/grott_ha.py b/examples/Home Assistent/grott_ha.py index 199e933..0719dfd 100644 --- a/examples/Home Assistent/grott_ha.py +++ b/examples/Home Assistent/grott_ha.py @@ -8,7 +8,7 @@ from grottconf import Conf -__version__ = "0.0.7-rc4" +__version__ = "0.0.7-rc5" """A pluging for grott This plugin allow to have autodiscovery of the device in HA @@ -18,6 +18,7 @@ Version 0.0.7 - Corrected a bug when creating the configuration - Add QoS 1 to reduce the possibility of lost message. + - Updated Total work time unit. Config: - ha_mqtt_host (required): The host of the MQTT broker user by HA (often the IP of HA) @@ -245,7 +246,7 @@ "totworktime": { "name": "Working time", "device_class": "duration", - "unit_of_measurement": "hours", + "unit_of_measurement": "h", "value_template": "{{ value_json.totworktime| float / 7200 | round(2) }}", }, "pvtemperature": { @@ -626,7 +627,9 @@ def grottext(conf: Conf, data: str, jsonmsg: str): conf, "layout", None ): configs_payloads = [] - print(f"\tGrott HA {__version__} - creating {device_serial} config in HA, {len(values.keys())} to push") + print( + f"\tGrott HA {__version__} - creating {device_serial} config in HA, {len(values.keys())} to push" + ) for key in values.keys(): # Generate a configuration payload payload = make_payload(conf, device_serial, key, key) @@ -702,10 +705,10 @@ def grottext(conf: Conf, data: str, jsonmsg: str): def test_generate_payload(): "Test that an auto generated payload for MQTT configuration" - class TestConf(): + class TestConf: recorddict = { "test": { - "pvpowerout": {"value" :122, "length" : 4, "type" : "num", "divide" : 10} + "pvpowerout": {"value": 122, "length": 4, "type": "num", "divide": 10} } } layout = "test" @@ -719,13 +722,19 @@ class TestConf(): assert payload["state_class"] == "measurement" assert payload["device_class"] == "power" assert payload["unit_of_measurement"] == "W" - + + def test_generate_payload_without_divider(): "Test that an auto generated payload for MQTT configuration" - class TestConf(): + + class TestConf: recorddict = { "test": { - "pvpowerout": {"value" :122, "length" : 4, "type" : "num", } + "pvpowerout": { + "value": 122, + "length": 4, + "type": "num", + } } } layout = "test" @@ -738,4 +747,4 @@ class TestConf(): assert payload["unique_id"] == "grott_NCO7410_pvpowerout" assert payload["state_class"] == "measurement" assert payload["device_class"] == "power" - assert payload["unit_of_measurement"] == "W" \ No newline at end of file + assert payload["unit_of_measurement"] == "W" From 291674e3b66dbf7768d612a6ceb8c64128c66b30 Mon Sep 17 00:00:00 2001 From: egguy Date: Sun, 19 Feb 2023 23:48:07 +1100 Subject: [PATCH 08/27] Add retain flag to MQTT --- examples/Home Assistent/grott_ha.py | 33 +++++++++++++++++++---------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/examples/Home Assistent/grott_ha.py b/examples/Home Assistent/grott_ha.py index 0719dfd..29c4b3c 100644 --- a/examples/Home Assistent/grott_ha.py +++ b/examples/Home Assistent/grott_ha.py @@ -25,6 +25,7 @@ - ha_mqtt_port (required): The port (the default is oftent 1883) - ha_mqtt_user (optional): The user use to connect to the broker (you can use your user) - ha_mqtt_password (optional): The password to connect to the mqtt broket (you can use your password) + - ha_mqtt_retain (optional): Set the retain flag for the data message (default: False) Return codes: - 0: Everything is OK @@ -496,6 +497,12 @@ }, } +MQTT_HOST_CONF_KEY = "ha_mqtt_host" +MQTT_PORT_CONF_KEY = "ha_mqtt_port" +MQTT_USERNAME_CONF_KEY = "ha_mqtt_user" +MQTT_PASSWORD_CONF_KEY = "ha_mqtt_password" +MQTT_RETAIN_CONF_KEY = "ha_mqtt_retain" + def make_payload(conf: Conf, device: str, name: str, key: str, unit: str = None): # Default configuration payload @@ -557,29 +564,29 @@ def set_configured(cls, serial: str): def process_conf(conf: Conf): required_params = [ - "ha_mqtt_host", - "ha_mqtt_port", + MQTT_HOST_CONF_KEY, + MQTT_PORT_CONF_KEY, ] if not all([param in conf.extvar for param in required_params]): print("Missing configuration for ha_mqtt") raise AttributeError - if "ha_mqtt_user" in conf.extvar: + if MQTT_USERNAME_CONF_KEY in conf.extvar: auth = { - "username": conf.extvar["ha_mqtt_user"], - "password": conf.extvar["ha_mqtt_password"], + "username": conf.extvar[MQTT_USERNAME_CONF_KEY], + "password": conf.extvar[MQTT_PASSWORD_CONF_KEY], } else: auth = None # Need to convert the port if passed as a string - port = conf.extvar["ha_mqtt_port"] + port = conf.extvar[MQTT_PORT_CONF_KEY] if isinstance(port, str): port = int(port) return { "client_id": MqttStateHandler.client_name, "auth": auth, - "hostname": conf.extvar["ha_mqtt_host"], + "hostname": conf.extvar[MQTT_HOST_CONF_KEY], "port": port, } @@ -598,8 +605,8 @@ def grottext(conf: Conf, data: str, jsonmsg: str): """Allow to push to HA MQTT bus, with auto discovery""" required_params = [ - "ha_mqtt_host", - "ha_mqtt_port", + MQTT_HOST_CONF_KEY, + MQTT_PORT_CONF_KEY, ] if not all([param in conf.extvar for param in required_params]): print("Missing configuration for ha_mqtt") @@ -690,10 +697,14 @@ def grottext(conf: Conf, data: str, jsonmsg: str): print(f"\t[Grott HA] {__version__} Can't configure device: {device_serial}") return 7 - # Push the values to the topics + # Push the values to the topic + retain = conf.extvar.get(MQTT_RETAIN_CONF_KEY, False) try: publish_single( - conf, state_topic.format(device=device_serial), json.dumps(values) + conf, + state_topic.format(device=device_serial), + json.dumps(values), + retain=retain, ) except Exception as e: print("[HA ext] - Exception while publishing - {}".format(e)) From a0b0c64e74f63d4c28492ca23ba41fc4adadf039 Mon Sep 17 00:00:00 2001 From: egguy Date: Mon, 20 Feb 2023 00:47:42 +1100 Subject: [PATCH 09/27] Add RC6 tag --- examples/Home Assistent/grott_ha.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/Home Assistent/grott_ha.py b/examples/Home Assistent/grott_ha.py index 29c4b3c..389a1f8 100644 --- a/examples/Home Assistent/grott_ha.py +++ b/examples/Home Assistent/grott_ha.py @@ -8,7 +8,7 @@ from grottconf import Conf -__version__ = "0.0.7-rc5" +__version__ = "0.0.7-rc6" """A pluging for grott This plugin allow to have autodiscovery of the device in HA @@ -19,6 +19,7 @@ - Corrected a bug when creating the configuration - Add QoS 1 to reduce the possibility of lost message. - Updated Total work time unit. + - Add support for setting the retain flag Config: - ha_mqtt_host (required): The host of the MQTT broker user by HA (often the IP of HA) From af9b7376665027f18124a16d7a80235dc1eefcb1 Mon Sep 17 00:00:00 2001 From: catch Date: Wed, 8 Feb 2023 23:07:50 +0000 Subject: [PATCH 10/27] Split sensors.yaml into mqtt.yaml and template.yaml to match new HA configuration. --- ...growatt_eng.yaml => mqtt_growatt_eng.yaml} | 81 +++++-------------- .../Home Assistent/template_growatt_eng.yaml | 22 +++++ 2 files changed, 40 insertions(+), 63 deletions(-) rename examples/Home Assistent/{sensors_growatt_eng.yaml => mqtt_growatt_eng.yaml} (73%) create mode 100644 examples/Home Assistent/template_growatt_eng.yaml diff --git a/examples/Home Assistent/sensors_growatt_eng.yaml b/examples/Home Assistent/mqtt_growatt_eng.yaml similarity index 73% rename from examples/Home Assistent/sensors_growatt_eng.yaml rename to examples/Home Assistent/mqtt_growatt_eng.yaml index 5a3fa85..d9fe3be 100644 --- a/examples/Home Assistent/sensors_growatt_eng.yaml +++ b/examples/Home Assistent/mqtt_growatt_eng.yaml @@ -3,42 +3,13 @@ # This file exposes all sensors from Grott to HA, including dummy sensors for the type of the inverter and the type and serial number of the datalogger (Be aware, the dummy # sensors have to be set manually) -- platform: template - sensors: - growatt_inverter: - unique_id: growatt_invertertype - friendly_name: Growatt - Type - # Please set the type of your inverter - value_template: "MIN 4200TL-XE" - icon_template: mdi:select-inverse - -- platform: template - sensors: - growatt_datalogger_type: - unique_id: growatt_datloggertype - friendly_name: Growatt - Datalogger type - # Please set the type of your datalogger - value_template: "ShineLink X" - icon_template: mdi:select-inverse - -- platform: template - sensors: - growatt_datalogger_serial: - unique_id: growatt_datlogger_serial - friendly_name: Growatt - Datalogger serienr - # Please set the serial number of your datalogger - value_template: " XXX1X23456" - icon_template: mdi:select-inverse - -- platform: mqtt +- name: Growatt - Serial number state_topic: energy/growatt value_template: "{{ value_json['device'] }}" unique_id: growatt_serial - name: Growatt - Serial number icon: mdi:select-inverse -- platform: mqtt - state_topic: energy/growatt +- state_topic: energy/growatt # If you like to have the date in another format, please change "timestamp_custom('%d-%m-%Y')" # For more information: https://docs.python.org/3/library/time.html#time.strftime value_template: "{{ as_timestamp(strptime(value_json['time'], '%Y-%m-%dT%H:%M:%S')) | timestamp_custom('%d-%m-%Y') }}" @@ -46,8 +17,7 @@ name: Growatt - Date icon: mdi:calendar -- platform: mqtt - state_topic: energy/growatt +- state_topic: energy/growatt # If you like to have the date in another format, please change "timestamp_custom('%H:%M:%S')" # For more information: https://docs.python.org/3/library/time.html#time.strftime value_template: "{{ as_timestamp(strptime(value_json['time'], '%Y-%m-%dT%H:%M:%S')) | timestamp_custom('%H:%M:%S') }}" @@ -55,8 +25,7 @@ name: Growatt - Time icon: mdi:clock-digital -- platform: mqtt - state_topic: energy/growatt +- state_topic: energy/growatt value_template: > {% if (value_json['values']['pvstatus'] | int == 0) %} Waiting @@ -71,88 +40,77 @@ name: Growatt - State icon: mdi:power-settings -- platform: mqtt - state_topic: energy/growatt +- state_topic: energy/growatt value_template: "{{ value_json['values']['pv1watt'] | float / 10000 }}" unique_id: growatt_string1_watt device_class: power unit_of_measurement: "kW" name: Growatt - String 1 (kiloWatt) -- platform: mqtt - state_topic: energy/growatt +- state_topic: energy/growatt value_template: "{{ value_json['values']['pv1voltage'] | float / 10 }}" unique_id: growatt_string1_voltage device_class: voltage unit_of_measurement: "V" name: Growatt - String 1 (Voltage) -- platform: mqtt - state_topic: energy/growatt +- state_topic: energy/growatt value_template: "{{ value_json['values']['pv1current'] | float / 10 }}" unique_id: growatt_string1_current device_class: current unit_of_measurement: "A" name: Growatt - String 1 (Current) -- platform: mqtt - state_topic: energy/growatt +- state_topic: energy/growatt value_template: "{{ value_json['values']['pv2watt'] | float / 10000 }}" unique_id: growatt_string2_watt device_class: power unit_of_measurement: "kW" name: Growatt - String 2 (kiloWatt) -- platform: mqtt - state_topic: energy/growatt +- state_topic: energy/growatt value_template: "{{ value_json['values']['pv2voltage'] | float / 10 }}" unique_id: growatt_string2_voltage device_class: voltage unit_of_measurement: "V" name: Growatt - String 2 (Voltage) -- platform: mqtt - state_topic: energy/growatt +- state_topic: energy/growatt value_template: "{{ value_json['values']['pv2current'] | float / 10 }}" unique_id: growatt_string2_current device_class: current unit_of_measurement: "A" name: Growatt - String 2 (Current) -- platform: mqtt - state_topic: energy/growatt +- state_topic: energy/growatt value_template: "{{ value_json['values']['pvpowerin'] | float / 10000 }}" unique_id: growatt_actual_input_power device_class: power unit_of_measurement: "kW" name: Growatt - Input kiloWatt (Actual) -- platform: mqtt - state_topic: energy/growatt +- state_topic: energy/growatt value_template: "{{ value_json['values']['pvpowerout'] | float / 10000 }}" unique_id: growatt_actual_output_power device_class: power unit_of_measurement: "kW" name: Growatt - Output kiloWatt (Actual) -- platform: mqtt - state_topic: energy/growatt +- state_topic: energy/growatt value_template: "{{ value_json['values']['pvfrequentie'] | float / 100 }}" unique_id: growatt_grid_frequency unit_of_measurement: "Hz" name: Growatt - Grid frequency icon: mdi:waveform -- platform: mqtt - state_topic: energy/growatt +- state_topic: energy/growatt value_template: "{{ value_json['values']['pvgridvoltage'] | float / 10 }}" unique_id: growatt_phase_voltage device_class: voltage unit_of_measurement: "V" name: Growatt - Phase voltage -- platform: mqtt - state_topic: energy/growatt +- state_topic: energy/growatt value_template: "{{ value_json['values']['pvenergytoday'] | float / 10 }}" unique_id: growatt_generated_energy_today device_class: energy @@ -160,8 +118,7 @@ name: Growatt - Generated energy (Today) icon: mdi:solar-power -- platform: mqtt - state_topic: energy/growatt +- state_topic: energy/growatt value_template: "{{ value_json['values']['pvenergytotal'] | float / 10 }}" unique_id: growatt_generated_energy_total device_class: energy @@ -170,8 +127,7 @@ name: Growatt - Generated energy (Total) icon: mdi:solar-power -- platform: mqtt - state_topic: energy/growatt +- state_topic: energy/growatt value_template: "{{ value_json['values']['pvtemperature'] | float / 10 }}" unique_id: growatt_inverer_temperature device_class: temperature @@ -180,8 +136,7 @@ # The entity below is not available in all inverters. -- platform: mqtt - state_topic: energy/growatt +- state_topic: energy/growatt value_template: "{{ value_json['values']['pvipmtemperature'] | float / 10 }}" unique_id: growatt_ipm_temperature device_class: temperature diff --git a/examples/Home Assistent/template_growatt_eng.yaml b/examples/Home Assistent/template_growatt_eng.yaml new file mode 100644 index 0000000..9258ad9 --- /dev/null +++ b/examples/Home Assistent/template_growatt_eng.yaml @@ -0,0 +1,22 @@ +# Grott - Home Assistant Growatt sensors +# +# This file exposes all sensors from Grott to HA, including dummy sensors for the type of the inverter and the type and serial number of the datalogger (Be aware, the dummy +# sensors have to be set manually) + +- name: Growatt - Type + unique_id: growatt_invertertype + # Please set the type of your inverter + state: "SPH6000" + icon: mdi:select-inverse + +- name: Growatt - Datalogger type + unique_id: growatt_datloggertype + # Please set the type of your datalogger + state: "ShineWifi S" + icon: mdi:select-inverse + +- name: Growatt - Datalogger serial + unique_id: growatt_datlogger_serial + # Please set the serial number of your datalogger + state: "ABCDEFG" + icon: mdi:select-inverse From f04d78fcd2c3f6ccd894f25d1aab2a10d584f0c4 Mon Sep 17 00:00:00 2001 From: Ken Date: Mon, 3 Jul 2023 11:13:58 +0000 Subject: [PATCH 11/27] - added flags discussed in issue #359 - added unit tests --- examples/grott.ini | 10 +- grottconf.py | 19 ++- tests/test_conf.py | 18 +++ tests/test_procdata.py | 116 ++++++++++++++++++ .../testdata/grott_postprocess_test_false.ini | 5 + .../grott_postprocess_test_mqtt_false.ini | 13 ++ .../grott_postprocess_test_mqtt_true.ini | 13 ++ .../testdata/grott_postprocess_test_true.ini | 5 + 8 files changed, 193 insertions(+), 6 deletions(-) create mode 100644 tests/test_conf.py create mode 100644 tests/test_procdata.py create mode 100644 tests/testdata/grott_postprocess_test_false.ini create mode 100644 tests/testdata/grott_postprocess_test_mqtt_false.ini create mode 100644 tests/testdata/grott_postprocess_test_mqtt_true.ini create mode 100644 tests/testdata/grott_postprocess_test_true.ini diff --git a/examples/grott.ini b/examples/grott.ini index d5e4ae0..3209b2a 100644 --- a/examples/grott.ini +++ b/examples/grott.ini @@ -66,6 +66,10 @@ #user = grott #password = growatt2020 +# Specify postprocessdata = True if you want to postprocess the MQTT data (default = False) +# This will apply the dividers etc. as specified in the inverter configuration file (see examples/Record Layout and Conf.recorddict in grottconf.py) +#postprocessdata = False + [PVOutput] # PVOutput parameters definitions @@ -98,7 +102,11 @@ #password = growatt2020 #token = "influx_token" #org = "grottorg" -#bucket = "grottdb" +#bucket = "grottdb" + +# Specify postprocessdata = True if you want to postprocess the Influxdb data (default = False) +# This will apply the dividers etc. as specified in the inverter configuration file (see examples/Record Layout and Conf.recorddict in grottconf.py) +#postprocessdata = False [extension] # grott extension parameters definitions diff --git a/grottconf.py b/grottconf.py index ade116f..b7a8eac 100644 --- a/grottconf.py +++ b/grottconf.py @@ -10,7 +10,7 @@ class Conf : - def __init__(self, vrm): + def __init__(self, vrm, cmdargs=None): self.verrel = vrm #Set default variables @@ -51,6 +51,7 @@ def __init__(self, vrm): self.mqttuser = "grott" self.mqttpsw = "growatt2020" self.mqttretain = False + self.mqttpostprocess = False #Post process mqtt message (applying dividers etc.) #pvoutput default self.pvoutput = False @@ -76,6 +77,7 @@ def __init__(self, vrm): self.iftoken = "influx_token" self.iforg = "grottorg" self.ifbucket = "grottdb" + self.ifpostprocess = False #Post process influxdb message (applying dividers etc.) #extension self.extension = False @@ -88,7 +90,7 @@ def __init__(self, vrm): #Set parm's #prio: 1.Command line parms, 2.env. variables, 3.config file 4.program default #process command settings that set processing values (verbose, trace, output, config, nomqtt) - self.parserinit() + self.parserinit(cmdargs) #Process config file self.procconf() @@ -216,6 +218,7 @@ def print(self): print("\tgrottip \t",self.grottip) print("\tgrottport \t",self.grottport) #print("\tSN \t",self.SN) + print("_MQTT:") print("\tnomqtt \t",self.nomqtt) print("\tmqttip: \t",self.mqttip) @@ -228,6 +231,8 @@ def print(self): print("\tmqtttauth: \t",self.mqttauth) print("\tmqttuser: \t",self.mqttuser) print("\tmqttpsw: \t","**secret**") #scramble output if tested! + print("\tmqttpostprocess: \t",self.mqttpostprocess) + #print("\tmqttpsw: \t",self.mqttpsw) #scramble output if tested! print("_Growatt server:") print("\tgrowattip: \t",self.growattip) @@ -244,6 +249,7 @@ def print(self): else: print("\tpvsystemid: \t",self.pvsystemid) print("\tpvinvertid: \t",self.pvinverterid) + print("_Influxdb:") print("\tinflux: \t",self.influx) print("\tinflux2: \t",self.influx2) @@ -256,7 +262,8 @@ def print(self): print("\torganization: \t",self.iforg ) print("\tbucket: \t",self.ifbucket) print("\ttoken: \t","**secret**") - #print("\ttoken: \t",self.iftoken) + #print("\ttoken: \t",self.iftoken) + print("\tifpostprocess: \t",self.ifpostprocess) print("_Extension:") print("\textension: \t",self.extension) @@ -266,7 +273,7 @@ def print(self): print() - def parserinit(self): + def parserinit(self, cmdargs): #Process commandline parameters init (read args, process c,v,o settings) parser = argparse.ArgumentParser(prog='grott') parser.add_argument('-v','--verbose',help="set verbose",action='store_true') @@ -282,7 +289,7 @@ def parserinit(self): parser.add_argument('-n','--noipf',help="Allow IP change from growatt website",action='store_true') - args, unknown = parser.parse_known_args() + args, unknown = parser.parse_known_args(args=cmdargs) if (args.c != None) : self.cfgfile=args.c #if (args.o != None) : sys.stdout = open(args.o, 'wb',0) changed to support unbuffered output in windows !!! @@ -387,6 +394,7 @@ def procconf(self): if config.has_option("MQTT","auth"): self.mqttauth = config.getboolean("MQTT","auth") if config.has_option("MQTT","user"): self.mqttuser = config.get("MQTT","user") if config.has_option("MQTT","password"): self.mqttpsw = config.get("MQTT","password") + if config.has_option("MQTT","postprocessdata"): self.mqttpostprocess = config.getboolean("MQTT","postprocessdata") if config.has_option("PVOutput","pvoutput"): self.pvoutput = config.get("PVOutput","pvoutput") if config.has_option("PVOutput","pvtemp"): self.pvtemp = config.get("PVOutput","pvtemp") if config.has_option("PVOutput","pvdisv1"): self.pvdisv1 = config.get("PVOutput","pvdisv1") @@ -411,6 +419,7 @@ def procconf(self): if config.has_option("influx","org"): self.iforg = config.get("influx","org") if config.has_option("influx","bucket"): self.ifbucket = config.get("influx","bucket") if config.has_option("influx","token"): self.iftoken = config.get("influx","token") + if config.has_option("influx","postprocessdata"): self.ifpostprocess = config.getboolean("influx","postprocessdata") #extensionINFLUX if config.has_option("extension","extension"): self.extension = config.get("extension","extension") if config.has_option("extension","extname"): self.extname = config.get("extension","extname") diff --git a/tests/test_conf.py b/tests/test_conf.py new file mode 100644 index 0000000..0bfb39e --- /dev/null +++ b/tests/test_conf.py @@ -0,0 +1,18 @@ +import pytest +from grottconf import Conf + +class TestGrottConf: + + def test_default_postprocess_flags(self): + conf = Conf("2.7.8") + assert conf.mqttpostprocess == False + assert conf.ifpostprocess == False + + @pytest.mark.parametrize('cfg_file, expected', [ + ('tests/testdata/grott_postprocess_test_true.ini', True), + ('tests/testdata/grott_postprocess_test_false.ini', False)]) + def test_cfgfile_overidden_postprocess_flag(self, cfg_file, expected): + conf = Conf("2.7.8", cmdargs=['-c', cfg_file]) + assert conf.mqttpostprocess == expected + assert conf.ifpostprocess == expected + diff --git a/tests/test_procdata.py b/tests/test_procdata.py new file mode 100644 index 0000000..17d72cb --- /dev/null +++ b/tests/test_procdata.py @@ -0,0 +1,116 @@ +import pytest +from unittest.mock import patch +import json + +from grottconf import Conf +import grottdata + +class TestProcData: + + + # Record layout used : T06NNNNSPF + # + # Apr 10 16:36:39 europa grott[145191]: - Grott values retrieved: + # Apr 10 16:36:39 europa grott[145191]: - datalogserial : DDD0CAB191 + # Apr 10 16:36:39 europa grott[145191]: - pvserial : GYH2BLL080 + # Apr 10 16:36:39 europa grott[145191]: - pvstatus : 2 + # Apr 10 16:36:39 europa grott[145191]: - vpv1 : 0.0 + # Apr 10 16:36:39 europa grott[145191]: - vpv2 : 0.0 + # Apr 10 16:36:39 europa grott[145191]: - ppv1 : 0.0 + # Apr 10 16:36:39 europa grott[145191]: - ppv2 : 0.0 + # Apr 10 16:36:39 europa grott[145191]: - buck1curr : 0.0 + # Apr 10 16:36:39 europa grott[145191]: - buck2curr : 0.0 + # Apr 10 16:36:39 europa grott[145191]: - op_watt : 48.0 + # Apr 10 16:36:39 europa grott[145191]: - pvpowerout : 48.0 + # Apr 10 16:36:39 europa grott[145191]: - op_va : 32000.0 + # Apr 10 16:36:39 europa grott[145191]: - acchr_watt : 0.0 + # Apr 10 16:36:39 europa grott[145191]: - acchr_VA : 0.0 + # Apr 10 16:36:39 europa grott[145191]: - bat_Volt : 53.1 + # Apr 10 16:36:39 europa grott[145191]: - batterySoc : 100 + # Apr 10 16:36:39 europa grott[145191]: - bus_volt : 0.0 + # Apr 10 16:36:39 europa grott[145191]: - grid_volt : 0.0 + # Apr 10 16:36:39 europa grott[145191]: - line_freq : 0.0 + # Apr 10 16:36:39 europa grott[145191]: - outputvolt : 208.0 + # Apr 10 16:36:39 europa grott[145191]: - pvgridvoltage : 208.0 + # Apr 10 16:36:39 europa grott[145191]: - outputfreq : 50.0 + # Apr 10 16:36:39 europa grott[145191]: - invtemp : 25.6 + # Apr 10 16:36:39 europa grott[145191]: - dcdctemp : 25.9 + # Apr 10 16:36:39 europa grott[145191]: - loadpercent : 2.7 + # Apr 10 16:36:39 europa grott[145191]: - buck1_ntc : 0.0 + # Apr 10 16:36:39 europa grott[145191]: - buck2_ntc : 0.0 + # Apr 10 16:36:39 europa grott[145191]: - OP_Curr : 0.4 + # Apr 10 16:36:39 europa grott[145191]: - Inv_Curr : 0.4 + # Apr 10 16:36:39 europa grott[145191]: - AC_InWatt : 0.0 + # Apr 10 16:36:39 europa grott[145191]: - AC_InVA : 0.0 + # Apr 10 16:36:39 europa grott[145191]: - faultBit : 0 + # Apr 10 16:36:39 europa grott[145191]: - warningBit : 0 + # Apr 10 16:36:39 europa grott[145191]: - faultValue : 0 + # Apr 10 16:36:39 europa grott[145191]: - warningValue : 0 + # Apr 10 16:36:39 europa grott[145191]: - constantPowerOK : 0 + # Apr 10 16:36:39 europa grott[145191]: - epvtoday : 3.3 + # Apr 10 16:36:39 europa grott[145191]: - pvenergytoday : 3.3 + # Apr 10 16:36:39 europa grott[145191]: - epvtotal : 2434.5 + # Apr 10 16:36:39 europa grott[145191]: - eacCharToday : 0.0 + # Apr 10 16:36:39 europa grott[145191]: - eacCharTotal : 68.9 + # Apr 10 16:36:39 europa grott[145191]: - ebatDischarToday : 1.1 + # Apr 10 16:36:39 europa grott[145191]: - ebatDischarTotal : 1799.2 + # Apr 10 16:36:39 europa grott[145191]: - eacDischarToday : 0.0 + # Apr 10 16:36:39 europa grott[145191]: - eacDischarTotal : 0.0 + # Apr 10 16:36:39 europa grott[145191]: - ACCharCurr : 0.0 + # Apr 10 16:36:39 europa grott[145191]: - ACDischarWatt : 0.0 + # Apr 10 16:36:39 europa grott[145191]: - ACDischarVA : 0.0 + # Apr 10 16:36:39 europa grott[145191]: - BatDischarWatt : 42.0 + # Apr 10 16:36:39 europa grott[145191]: - BatDischarVA : 42.0 + # Apr 10 16:36:39 europa grott[145191]: - BatWatt : 59.0 + # + # Apr 10 16:36:39 europa grott[145191]: - MQTT jsonmsg: + # Apr 10 16:36:39 europa grott[145191]: {"device": "GYH2BLL080", "time": "2023-04-10T12:20:31", "buffered": "no", + # Apr 10 16:36:39 europa grott[145191]: "values": {"datalogserial": "DDD0CAB191", "pvserial": "GYH2BLL080", + # Apr 10 16:36:39 europa grott[145191]: "pvstatus": 2, "vpv1": 0, "vpv2": 0, "ppv1": 0, "ppv2": 0, "buck1curr": 0, + # Apr 10 16:36:39 europa grott[145191]: "buck2curr": 0, "op_watt": 480, "pvpowerout": 480, "op_va": 320000, + # Apr 10 16:36:39 europa grott[145191]: "acchr_watt": 0, "acchr_VA": 0, "bat_Volt": 5310, "batterySoc": 100, + # Apr 10 16:36:39 europa grott[145191]: "bus_volt": 0, "grid_volt": 0, "line_freq": 0, "outputvolt": 2080, + # Apr 10 16:36:39 europa grott[145191]: "pvgridvoltage": 2080, "outputfreq": 5000, "invtemp": 256, "dcdctemp": 259, + # Apr 10 16:36:39 europa grott[145191]: "loadpercent": 27, "buck1_ntc": 0, "buck2_ntc": 0, "OP_Curr": 4, "Inv_Curr": + # Apr 10 16:36:39 europa grott[145191]: 4, "AC_InWatt": 0, "AC_InVA": 0, "faultBit": 0, "warningBit": 0, + # Apr 10 16:36:39 europa grott[145191]: "faultValue": 0, "warningValue": 0, "constantPowerOK": 0, "epvtoday": 33, + # Apr 10 16:36:39 europa grott[145191]: "pvenergytoday": 33, "epvtotal": 24345, "eacCharToday": 0, "eacCharTotal": + # Apr 10 16:36:39 europa grott[145191]: 689, "ebatDischarToday": 11, "ebatDischarTotal": 17992, "eacDischarToday": + # Apr 10 16:36:39 europa grott[145191]: 0, "eacDischarTotal": 0, "ACCharCurr": 0, "ACDischarWatt": 0, "ACDischarVA": + # Apr 10 16:36:39 europa grott[145191]: 0, "BatDischarWatt": 420, "BatDischarVA": 420, "BatWatt": 590}} + + valid_data = b'\x00\xa1\x00\x06\x01\x5f\x01\x04\x03\x36\x2b\x47\x22\x35\x36\x76\x4b\x5e\x77'\ + b'\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72'\ + b'\x28\x2e\x29\x46\x36\x0b\x3e\x5f\x4f\x51\x74\x74\x47\x72\x6f\x77\x61\x74\x74'\ + b'\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x76\x70\x7e\x4b\x66\x70\x74\x61'\ + b'\x74\x74\x6b\x72\x6d\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f'\ + b'\x77\x61\x74\x74\x46\x92\x6f\x77\x65\x96\x74\x47\x72\x6f\x77\x61\x74\x74\x53'\ + b'\xcc\x6f\x13\x61\x74\x74\x47\x72\x6f\x7f\x41\x67\xfc\x47\x72\x6e\x77\x60\x77'\ + b'\x74\x5c\x66\xd1\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x43\x72\x6b\x77'\ + b'\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x3a\x51\x47\x5f'\ + b'\x6f\x2e\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x55\x47\x72\x30\x6e\x61\x74\x74'\ + b'\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x63\xc5\x74\x47\x72\x64\x77\x61'\ + b'\x32\x3c\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f'\ + b'\x77\x61\x74\x75\xe3\x72\x6f\x76\xc5\x74\x74\x45\x3c\x6f\x77\x61\x74\x74\x47'\ + b'\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x7c\x61\x74\x32\x0f\x72\x6f\x77\x3b\x74'\ + b'\xf2\xb8\x8f\x6f\x76\x9e\xbf\x74\x47\x72\x6e\x77\x61\x74\x74\x47\x72\x6f\x77'\ + b'\x61\x74\x2b\x5e\x72\x4e\x10\xba\x13\xaf\x20\xa9\x08\xac\x06\xaf\x74\x47\x72'\ + b'\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74'\ + b'\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61'\ + b'\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x4b\x2f' + + @pytest.mark.parametrize('cfg_file, expected', [ + ('tests/testdata/grott_postprocess_test_mqtt_true.ini', 59), + ('tests/testdata/grott_postprocess_test_mqtt_false.ini', 590)]) + @patch('grottdata.publish.single') + def test_procdata(self, mock_publish_single, cfg_file, expected): + conf = Conf("2.7.8", cmdargs=['-c', cfg_file]) + grottdata.procdata(conf,self.valid_data) + assert mock_publish_single.called == True + json_payload = json.loads(mock_publish_single.call_args.kwargs['payload']) + assert json_payload['device'] == 'GYH2BLL080' + assert json_payload['values']['BatWatt'] == expected + + + + diff --git a/tests/testdata/grott_postprocess_test_false.ini b/tests/testdata/grott_postprocess_test_false.ini new file mode 100644 index 0000000..1efd2a4 --- /dev/null +++ b/tests/testdata/grott_postprocess_test_false.ini @@ -0,0 +1,5 @@ +[MQTT] +postprocessdata = False + +[influx] +postprocessdata = False \ No newline at end of file diff --git a/tests/testdata/grott_postprocess_test_mqtt_false.ini b/tests/testdata/grott_postprocess_test_mqtt_false.ini new file mode 100644 index 0000000..e03b663 --- /dev/null +++ b/tests/testdata/grott_postprocess_test_mqtt_false.ini @@ -0,0 +1,13 @@ +[Generic] +verbose = True +invtype = spf + +[MQTT] +#postprocessdata = False + +[PVOutput] +pvoutput = False + +[extension] +extension = False + diff --git a/tests/testdata/grott_postprocess_test_mqtt_true.ini b/tests/testdata/grott_postprocess_test_mqtt_true.ini new file mode 100644 index 0000000..e3beac6 --- /dev/null +++ b/tests/testdata/grott_postprocess_test_mqtt_true.ini @@ -0,0 +1,13 @@ +[Generic] +verbose = True +invtype = spf + +[MQTT] +postprocessdata = True + +[PVOutput] +pvoutput = False + +[extension] +extension = False + diff --git a/tests/testdata/grott_postprocess_test_true.ini b/tests/testdata/grott_postprocess_test_true.ini new file mode 100644 index 0000000..d4b0621 --- /dev/null +++ b/tests/testdata/grott_postprocess_test_true.ini @@ -0,0 +1,5 @@ +[MQTT] +postprocessdata = True + +[influx] +postprocessdata = True \ No newline at end of file From 10da787db5ae4024ca13c1b2ded52ac63295952e Mon Sep 17 00:00:00 2001 From: Ken Date: Mon, 3 Jul 2023 13:43:26 +0000 Subject: [PATCH 12/27] - added flag management for mqtt and influxdb --- examples/grott.ini | 12 ++--- grottconf.py | 25 ++++++++-- grottdata.py | 18 +++----- tests/test_conf.py | 12 ++--- tests/test_procdata.py | 46 +++++++++++++++++-- .../conf_applydividers_test_false.ini | 5 ++ .../testdata/conf_applydividers_test_true.ini | 5 ++ ...rott_applydividers_test_influxdb_false.ini | 18 ++++++++ ...grott_applydividers_test_influxdb_true.ini | 18 ++++++++ ...> grott_applydividers_test_mqtt_false.ini} | 2 +- ...=> grott_applydividers_test_mqtt_true.ini} | 2 +- .../testdata/grott_postprocess_test_false.ini | 5 -- .../testdata/grott_postprocess_test_true.ini | 5 -- 13 files changed, 128 insertions(+), 45 deletions(-) create mode 100644 tests/testdata/conf_applydividers_test_false.ini create mode 100644 tests/testdata/conf_applydividers_test_true.ini create mode 100644 tests/testdata/grott_applydividers_test_influxdb_false.ini create mode 100644 tests/testdata/grott_applydividers_test_influxdb_true.ini rename tests/testdata/{grott_postprocess_test_mqtt_true.ini => grott_applydividers_test_mqtt_false.ini} (82%) rename tests/testdata/{grott_postprocess_test_mqtt_false.ini => grott_applydividers_test_mqtt_true.ini} (81%) delete mode 100644 tests/testdata/grott_postprocess_test_false.ini delete mode 100644 tests/testdata/grott_postprocess_test_true.ini diff --git a/examples/grott.ini b/examples/grott.ini index 3209b2a..b0c4bc4 100644 --- a/examples/grott.ini +++ b/examples/grott.ini @@ -66,9 +66,9 @@ #user = grott #password = growatt2020 -# Specify postprocessdata = True if you want to postprocess the MQTT data (default = False) -# This will apply the dividers etc. as specified in the inverter configuration file (see examples/Record Layout and Conf.recorddict in grottconf.py) -#postprocessdata = False +# Specify applydividers = True if you want to postprocess the MQTT data (default = False) +# This will apply the dividers as specified in the inverter configuration file (see examples/Record Layout and Conf.recorddict in grottconf.py) +#applydividers = False [PVOutput] # PVOutput parameters definitions @@ -104,9 +104,9 @@ #org = "grottorg" #bucket = "grottdb" -# Specify postprocessdata = True if you want to postprocess the Influxdb data (default = False) -# This will apply the dividers etc. as specified in the inverter configuration file (see examples/Record Layout and Conf.recorddict in grottconf.py) -#postprocessdata = False +# Specify applydividers = True if you want to postprocess the Influxdb data (default = False) +# This will apply the dividers as specified in the inverter configuration file (see examples/Record Layout and Conf.recorddict in grottconf.py) +#applydividers = False [extension] # grott extension parameters definitions diff --git a/grottconf.py b/grottconf.py index b7a8eac..b77614a 100644 --- a/grottconf.py +++ b/grottconf.py @@ -7,6 +7,7 @@ import ipaddress from os import walk from grottdata import format_multi_line, str2bool +from typing import Optional class Conf : @@ -51,7 +52,7 @@ def __init__(self, vrm, cmdargs=None): self.mqttuser = "grott" self.mqttpsw = "growatt2020" self.mqttretain = False - self.mqttpostprocess = False #Post process mqtt message (applying dividers etc.) + self.mqttapplydividers = False # Apply dividers defined in the Record Layouts before sending data out to MQTT #pvoutput default self.pvoutput = False @@ -77,7 +78,7 @@ def __init__(self, vrm, cmdargs=None): self.iftoken = "influx_token" self.iforg = "grottorg" self.ifbucket = "grottdb" - self.ifpostprocess = False #Post process influxdb message (applying dividers etc.) + self.ifapplydividers = False # Apply dividers defined in the Record Layouts before sending data out to InfluxDB #extension self.extension = False @@ -394,7 +395,7 @@ def procconf(self): if config.has_option("MQTT","auth"): self.mqttauth = config.getboolean("MQTT","auth") if config.has_option("MQTT","user"): self.mqttuser = config.get("MQTT","user") if config.has_option("MQTT","password"): self.mqttpsw = config.get("MQTT","password") - if config.has_option("MQTT","postprocessdata"): self.mqttpostprocess = config.getboolean("MQTT","postprocessdata") + if config.has_option("MQTT","applydividers"): self.mqttapplydividers = config.getboolean("MQTT","applydividers") if config.has_option("PVOutput","pvoutput"): self.pvoutput = config.get("PVOutput","pvoutput") if config.has_option("PVOutput","pvtemp"): self.pvtemp = config.get("PVOutput","pvtemp") if config.has_option("PVOutput","pvdisv1"): self.pvdisv1 = config.get("PVOutput","pvdisv1") @@ -419,7 +420,7 @@ def procconf(self): if config.has_option("influx","org"): self.iforg = config.get("influx","org") if config.has_option("influx","bucket"): self.ifbucket = config.get("influx","bucket") if config.has_option("influx","token"): self.iftoken = config.get("influx","token") - if config.has_option("influx","postprocessdata"): self.ifpostprocess = config.getboolean("influx","postprocessdata") + if config.has_option("influx","applydividers"): self.ifapplydividers = config.getboolean("influx","applydividers") #extensionINFLUX if config.has_option("extension","extension"): self.extension = config.get("extension","extension") if config.has_option("extension","extname"): self.extname = config.get("extension","extname") @@ -1609,3 +1610,19 @@ def set_reclayouts(self): if self.verbose : print(key, " : ") if self.verbose : print(self.recorddict[key]) + # Get the key divider, if applicable + def get_recdivider(self, key) -> Optional[int]: + + # No divider if the key doesn't exist + if not key in self.recorddict[self.layout]: + return None + + # No divider if the key is not numeric + if not ('type' in self.recorddict[self.layout][key] and self.recorddict[self.layout][key]['type'] in ['num', 'numx']): + return None + + # No divider if there is no divide key + if not 'divide' in self.recorddict[self.layout][key]: + return None + + return self.recorddict[self.layout][key]['divide'] \ No newline at end of file diff --git a/grottdata.py b/grottdata.py index 0ea1872..7e55f1a 100644 --- a/grottdata.py +++ b/grottdata.py @@ -452,16 +452,9 @@ def procdata(conf,data): "values" : {} } - for key in definedkey : - - #if key != "pvserial" : - #if conf.recorddict[layout][key]["type"] == "num" : - # only add int values to the json object - #print(definedkey[key]) - #print(type(definedkey[key])) - #if type(definedkey[key]) == type(1) : - # jsonobj["values"][key] = definedkey[key] - jsonobj["values"][key] = definedkey[key] + for key in definedkey : + divider = conf.get_recdivider(key) + jsonobj["values"][key] = definedkey[key]/divider if conf.mqttapplydividers and divider is not None else definedkey[key] jsonmsg = json.dumps(jsonobj) @@ -635,8 +628,9 @@ def procdata(conf,data): } for key in definedkey : - if key != "date" : - ifobj["fields"][key] = definedkey[key] + if key != "date" : + divider = conf.get_recdivider(key) + ifobj["fields"][key] = definedkey[key]/divider if conf.ifapplydividers and divider is not None else definedkey[key] #Create list for influx ifjson = [ifobj] diff --git a/tests/test_conf.py b/tests/test_conf.py index 0bfb39e..d5cc85c 100644 --- a/tests/test_conf.py +++ b/tests/test_conf.py @@ -5,14 +5,14 @@ class TestGrottConf: def test_default_postprocess_flags(self): conf = Conf("2.7.8") - assert conf.mqttpostprocess == False - assert conf.ifpostprocess == False + assert conf.mqttapplydividers == False + assert conf.ifapplydividers == False @pytest.mark.parametrize('cfg_file, expected', [ - ('tests/testdata/grott_postprocess_test_true.ini', True), - ('tests/testdata/grott_postprocess_test_false.ini', False)]) + ('tests/testdata/conf_applydividers_test_true.ini', True), + ('tests/testdata/conf_applydividers_test_false.ini', False)]) def test_cfgfile_overidden_postprocess_flag(self, cfg_file, expected): conf = Conf("2.7.8", cmdargs=['-c', cfg_file]) - assert conf.mqttpostprocess == expected - assert conf.ifpostprocess == expected + assert conf.mqttapplydividers == expected + assert conf.ifapplydividers == expected diff --git a/tests/test_procdata.py b/tests/test_procdata.py index 17d72cb..0a375ef 100644 --- a/tests/test_procdata.py +++ b/tests/test_procdata.py @@ -1,5 +1,5 @@ import pytest -from unittest.mock import patch +from unittest.mock import patch, Mock import json from grottconf import Conf @@ -78,6 +78,22 @@ class TestProcData: # Apr 10 16:36:39 europa grott[145191]: 689, "ebatDischarToday": 11, "ebatDischarTotal": 17992, "eacDischarToday": # Apr 10 16:36:39 europa grott[145191]: 0, "eacDischarTotal": 0, "ACCharCurr": 0, "ACDischarWatt": 0, "ACDischarVA": # Apr 10 16:36:39 europa grott[145191]: 0, "BatDischarWatt": 420, "BatDischarVA": 420, "BatWatt": 590}} + # + # Apr 10 16:36:39 europa grott[145191]: - Grott influxdb jsonmsg: + # Apr 10 16:36:39 europa grott[145191]: [{'measurement': 'GYH2BLL080', 'time': '2023-04-10T12:20:31', 'fields': + # Apr 10 16:36:39 europa grott[145191]: {'datalogserial': 'DDD0CAB191', 'pvserial': 'GYH2BLL080', 'pvstatus': 2, + # Apr 10 16:36:39 europa grott[145191]: 'vpv1': 0, 'vpv2': 0, 'ppv1': 0, 'ppv2': 0, 'buck1curr': 0, 'buck2curr': 0, + # Apr 10 16:36:39 europa grott[145191]: 'op_watt': 480, 'pvpowerout': 480, 'op_va': 320000, 'acchr_watt': 0, + # Apr 10 16:36:39 europa grott[145191]: 'acchr_VA': 0, 'bat_Volt': 5310, 'batterySoc': 100, 'bus_volt': 0, + # Apr 10 16:36:39 europa grott[145191]: 'grid_volt': 0, 'line_freq': 0, 'outputvolt': 2080, 'pvgridvoltage': 2080, + # Apr 10 16:36:39 europa grott[145191]: 'outputfreq': 5000, 'invtemp': 256, 'dcdctemp': 259, 'loadpercent': 27, + # Apr 10 16:36:39 europa grott[145191]: 'buck1_ntc': 0, 'buck2_ntc': 0, 'OP_Curr': 4, 'Inv_Curr': 4, 'AC_InWatt': 0, + # Apr 10 16:36:39 europa grott[145191]: 'AC_InVA': 0, 'faultBit': 0, 'warningBit': 0, 'faultValue': 0, + # Apr 10 16:36:39 europa grott[145191]: 'warningValue': 0, 'constantPowerOK': 0, 'epvtoday': 33, 'pvenergytoday': + # Apr 10 16:36:39 europa grott[145191]: 33, 'epvtotal': 24345, 'eacCharToday': 0, 'eacCharTotal': 689, + # Apr 10 16:36:39 europa grott[145191]: 'ebatDischarToday': 11, 'ebatDischarTotal': 17992, 'eacDischarToday': 0, + # Apr 10 16:36:39 europa grott[145191]: 'eacDischarTotal': 0, 'ACCharCurr': 0, 'ACDischarWatt': 0, 'ACDischarVA': 0, + # Apr 10 16:36:39 europa grott[145191]: 'BatDischarWatt': 420, 'BatDischarVA': 420, 'BatWatt': 590}}] valid_data = b'\x00\xa1\x00\x06\x01\x5f\x01\x04\x03\x36\x2b\x47\x22\x35\x36\x76\x4b\x5e\x77'\ b'\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72'\ @@ -100,17 +116,37 @@ class TestProcData: b'\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x4b\x2f' @pytest.mark.parametrize('cfg_file, expected', [ - ('tests/testdata/grott_postprocess_test_mqtt_true.ini', 59), - ('tests/testdata/grott_postprocess_test_mqtt_false.ini', 590)]) + ('tests/testdata/grott_applydividers_test_mqtt_true.ini', 59), + ('tests/testdata/grott_applydividers_test_mqtt_false.ini', 590)]) @patch('grottdata.publish.single') - def test_procdata(self, mock_publish_single, cfg_file, expected): + def test_mqtt_applydividers(self, mock_publish_single, cfg_file, expected): conf = Conf("2.7.8", cmdargs=['-c', cfg_file]) grottdata.procdata(conf,self.valid_data) assert mock_publish_single.called == True json_payload = json.loads(mock_publish_single.call_args.kwargs['payload']) assert json_payload['device'] == 'GYH2BLL080' + assert json_payload['values']['pvserial'] == 'GYH2BLL080' assert json_payload['values']['BatWatt'] == expected - + @pytest.mark.parametrize('cfg_file, expected', [ + ('tests/testdata/grott_applydividers_test_influxdb_false.ini', 590), + ('tests/testdata/grott_applydividers_test_influxdb_true.ini', 59)]) + def test_influxdb_applydividers(self, cfg_file, expected): + conf = Conf("2.7.8", cmdargs=['-c', cfg_file]) + + # we need to set influx and influx2 to True, otherwise the ifwrite_api.write() call will not be made + # but we have to set it after conf is initialized, to skip the actual influxdb connection opening + conf.influx = True + conf.influx2 = True + + ifwrite_mock = Mock() + conf.ifwrite_api = ifwrite_mock + + grottdata.procdata(conf,self.valid_data) + assert ifwrite_mock.write.called == True + payload = ifwrite_mock.write.call_args.args[2] + assert payload[0]['measurement'] == 'GYH2BLL080' + assert payload[0]['fields']['pvserial'] == 'GYH2BLL080' + assert payload[0]['fields']['BatWatt'] == expected diff --git a/tests/testdata/conf_applydividers_test_false.ini b/tests/testdata/conf_applydividers_test_false.ini new file mode 100644 index 0000000..f69c008 --- /dev/null +++ b/tests/testdata/conf_applydividers_test_false.ini @@ -0,0 +1,5 @@ +[MQTT] +applydividers = False + +[influx] +applydividers = False \ No newline at end of file diff --git a/tests/testdata/conf_applydividers_test_true.ini b/tests/testdata/conf_applydividers_test_true.ini new file mode 100644 index 0000000..24c786a --- /dev/null +++ b/tests/testdata/conf_applydividers_test_true.ini @@ -0,0 +1,5 @@ +[MQTT] +applydividers = True + +[influx] +applydividers = True \ No newline at end of file diff --git a/tests/testdata/grott_applydividers_test_influxdb_false.ini b/tests/testdata/grott_applydividers_test_influxdb_false.ini new file mode 100644 index 0000000..744184a --- /dev/null +++ b/tests/testdata/grott_applydividers_test_influxdb_false.ini @@ -0,0 +1,18 @@ +[Generic] +verbose = True +invtype = spf + +[MQTT] +nomqtt = True + +[influx] +influx = False +influx2 = False +applydividers = False + +[PVOutput] +pvoutput = False + +[extension] +extension = False + diff --git a/tests/testdata/grott_applydividers_test_influxdb_true.ini b/tests/testdata/grott_applydividers_test_influxdb_true.ini new file mode 100644 index 0000000..7f2c887 --- /dev/null +++ b/tests/testdata/grott_applydividers_test_influxdb_true.ini @@ -0,0 +1,18 @@ +[Generic] +verbose = True +invtype = spf + +[MQTT] +nomqtt = True + +[influx] +influx = False +influx2 = False +applydividers = True + +[PVOutput] +pvoutput = False + +[extension] +extension = False + diff --git a/tests/testdata/grott_postprocess_test_mqtt_true.ini b/tests/testdata/grott_applydividers_test_mqtt_false.ini similarity index 82% rename from tests/testdata/grott_postprocess_test_mqtt_true.ini rename to tests/testdata/grott_applydividers_test_mqtt_false.ini index e3beac6..b822fd9 100644 --- a/tests/testdata/grott_postprocess_test_mqtt_true.ini +++ b/tests/testdata/grott_applydividers_test_mqtt_false.ini @@ -3,7 +3,7 @@ verbose = True invtype = spf [MQTT] -postprocessdata = True +#applydividers = False [PVOutput] pvoutput = False diff --git a/tests/testdata/grott_postprocess_test_mqtt_false.ini b/tests/testdata/grott_applydividers_test_mqtt_true.ini similarity index 81% rename from tests/testdata/grott_postprocess_test_mqtt_false.ini rename to tests/testdata/grott_applydividers_test_mqtt_true.ini index e03b663..07002ad 100644 --- a/tests/testdata/grott_postprocess_test_mqtt_false.ini +++ b/tests/testdata/grott_applydividers_test_mqtt_true.ini @@ -3,7 +3,7 @@ verbose = True invtype = spf [MQTT] -#postprocessdata = False +applydividers = True [PVOutput] pvoutput = False diff --git a/tests/testdata/grott_postprocess_test_false.ini b/tests/testdata/grott_postprocess_test_false.ini deleted file mode 100644 index 1efd2a4..0000000 --- a/tests/testdata/grott_postprocess_test_false.ini +++ /dev/null @@ -1,5 +0,0 @@ -[MQTT] -postprocessdata = False - -[influx] -postprocessdata = False \ No newline at end of file diff --git a/tests/testdata/grott_postprocess_test_true.ini b/tests/testdata/grott_postprocess_test_true.ini deleted file mode 100644 index d4b0621..0000000 --- a/tests/testdata/grott_postprocess_test_true.ini +++ /dev/null @@ -1,5 +0,0 @@ -[MQTT] -postprocessdata = True - -[influx] -postprocessdata = True \ No newline at end of file From 6c0ae482bc10479be10365824834d1774d0b9613 Mon Sep 17 00:00:00 2001 From: Ken Date: Mon, 3 Jul 2023 13:52:44 +0000 Subject: [PATCH 13/27] - minor fix to conf. printing --- grottconf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/grottconf.py b/grottconf.py index b77614a..e882324 100644 --- a/grottconf.py +++ b/grottconf.py @@ -232,7 +232,7 @@ def print(self): print("\tmqtttauth: \t",self.mqttauth) print("\tmqttuser: \t",self.mqttuser) print("\tmqttpsw: \t","**secret**") #scramble output if tested! - print("\tmqttpostprocess: \t",self.mqttpostprocess) + print("\mqttapplydividers: \t",self.mqttapplydividers) #print("\tmqttpsw: \t",self.mqttpsw) #scramble output if tested! print("_Growatt server:") @@ -264,7 +264,7 @@ def print(self): print("\tbucket: \t",self.ifbucket) print("\ttoken: \t","**secret**") #print("\ttoken: \t",self.iftoken) - print("\tifpostprocess: \t",self.ifpostprocess) + print("\ifapplydividers: \t",self.ifapplydividers) print("_Extension:") print("\textension: \t",self.extension) From 4f861ed90a8278c39502700711847020ab47810d Mon Sep 17 00:00:00 2001 From: Ken Date: Fri, 7 Jul 2023 17:01:18 +0000 Subject: [PATCH 14/27] - Added messages consistency check - Fixed some UT --- grottdata.py | 27 ++++- tests/test_conf.py | 2 +- tests/test_procdata.py | 207 +++++++++++++++++++++++++++++++++- tests/testdata/conf_empty.ini | 5 + 4 files changed, 237 insertions(+), 4 deletions(-) create mode 100644 tests/testdata/conf_empty.ini diff --git a/grottdata.py b/grottdata.py index 7e55f1a..787b0f0 100644 --- a/grottdata.py +++ b/grottdata.py @@ -79,6 +79,23 @@ def str2bool(defstr): return(defret) else : return() +def is_message_valid(definedkey:dict) -> bool: + # TODO : this test is a too arbitrary. We just assume that no Growatt inverter can handle more than 12000W of input solar power. + if 'ppv1' in definedkey: + if definedkey['ppv1'] > 12000: + return False + + if 'ppv2' in definedkey: + if definedkey['ppv2'] > 12000: + return False + + # TODO : this test is a too arbitrary. We just assume that no Growatt inverter can produce more than 200kWh of energy in a single day. + if 'epvtoday' in definedkey: + if definedkey['epvtoday'] > 200: + return False + + return True + def procdata(conf,data): if conf.verbose: print("\t - " + "Growatt original Data:") @@ -377,7 +394,13 @@ def procdata(conf,data): if conf.trace: print("\t - "+ 'Growatt unprocessed Data:') print(format_multi_line("\t\t - ", result_string)) - + + # check if data is valid + # sometime my SPF6000T DVM MPV sends inconsistent data, which must be discared (see unit tests for examples of corrupted data) + if not is_message_valid(definedkey): + if conf.verbose: print("\t - " + 'Growatt data not consistent, processing stopped') + return + if dataprocessed: # only sendout data to MQTT if it is processed. @@ -583,7 +606,7 @@ def procdata(conf,data): # influxDB processing if conf.influx: - if conf.verbose : print("\t - " + "Grott InfluxDB publihing started") + if conf.verbose : print("\t - " + "Grott InfluxDB publishing started") try: import pytz except: diff --git a/tests/test_conf.py b/tests/test_conf.py index d5cc85c..fa46160 100644 --- a/tests/test_conf.py +++ b/tests/test_conf.py @@ -4,7 +4,7 @@ class TestGrottConf: def test_default_postprocess_flags(self): - conf = Conf("2.7.8") + conf = Conf("2.7.8", cmdargs=['-c', 'tests/testdata/conf_empty.ini']) assert conf.mqttapplydividers == False assert conf.ifapplydividers == False diff --git a/tests/test_procdata.py b/tests/test_procdata.py index 0a375ef..002d685 100644 --- a/tests/test_procdata.py +++ b/tests/test_procdata.py @@ -115,6 +115,199 @@ class TestProcData: b'\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61'\ b'\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x4b\x2f' + # ------------ This data has absurd ppv2 value and should not be processed + # Jul 07 02:57:43 europa grott[1222736]: - Grott automatic protocol detection + # Jul 07 02:57:43 europa grott[1222736]: - Grott data record length 359 + # Jul 07 02:57:43 europa grott[1222736]: - layout : T060104SPF + # Jul 07 02:57:43 europa grott[1222736]: - no matching record layout found, try generic + # Jul 07 02:57:43 europa grott[1222736]: - Record layout used : T06NNNNSPF + # Jul 07 02:57:43 europa grott[1222736]: - Growatt data decrypted V2 + # Jul 07 02:57:43 europa grott[1222736]: - Grott Growatt data decrypted + # Jul 07 02:57:43 europa grott[1222736]: - Growatt plain data: + # Jul 07 02:57:43 europa grott[1222736]: 00a90006015f01044444443043414231393100000000000000000000000000000000000000004 + # Jul 07 02:57:43 europa grott[1222736]: 7594832424c4c303830000000000000000000000000000000000000000017070617073a030000 + # Jul 07 02:57:43 europa grott[1222736]: 002c00000000000000000000ea600000ea6000000000000000000000000000000000000000000 + # Jul 07 02:57:43 europa grott[1222736]: 00000000000000000000000000000000000000000000000000000000000000000010000000c08 + # Jul 07 02:57:43 europa grott[1222736]: 8c088c000000000000000000000000002d00590000000000000000002d0000741c00000000000 + # Jul 07 02:57:43 europa grott[1222736]: 0000000000000000002b10000000a000054370000000000000000000000000000000000000000 + # Jul 07 02:57:43 europa grott[1222736]: 0eb000000eb000000f460000000000000000000000000000000a000054370000005a008600000 + # Jul 07 02:57:43 europa grott[1222736]: 001fe7d0000000300000000000000010000741c002df7dbf7dbf7dbf7dbf7db00000000000000 + # Jul 07 02:57:43 europa grott[1222736]: 00000000000000000000000000000000000000000000000000000000000000000000000000000 + # Jul 07 02:57:43 europa grott[1222736]: 000000000000000000000e802 + # Jul 07 02:57:43 europa grott[1222736]: - Growatt new layout processing + # Jul 07 02:57:43 europa grott[1222736]: - decrypt : True + # Jul 07 02:57:43 europa grott[1222736]: - offset : 6 + # Jul 07 02:57:43 europa grott[1222736]: - record layout : T06NNNNSPF + # Jul 07 02:57:43 europa grott[1222736]: - Grott server date/time used + # Jul 07 02:57:43 europa grott[1222736]: - Grott values retrieved: + # Jul 07 02:57:43 europa grott[1222736]: - datalogserial : DDD0CAB191 + # Jul 07 02:57:43 europa grott[1222736]: - pvserial : GYH2BLL080 + # Jul 07 02:57:43 europa grott[1222736]: - pvstatus : 0 + # Jul 07 02:57:43 europa grott[1222736]: - vpv1 : 0.0 + # Jul 07 02:57:43 europa grott[1222736]: - vpv2 : 0.0 + # Jul 07 02:57:43 europa grott[1222736]: - ppv1 : 0.0 + # Jul 07 02:57:43 europa grott[1222736]: - ppv2 : 393216000.0 + # Jul 07 02:57:43 europa grott[1222736]: - buck1curr : 6000.0 + # Jul 07 02:57:43 europa grott[1222736]: - buck2curr : 0.0 + # Jul 07 02:57:43 europa grott[1222736]: - op_watt : 0.0 + # Jul 07 02:57:43 europa grott[1222736]: - pvpowerout : 0.0 + # Jul 07 02:57:43 europa grott[1222736]: - op_va : 0.0 + # Jul 07 02:57:43 europa grott[1222736]: - acchr_watt : 0.0 + # Jul 07 02:57:43 europa grott[1222736]: - acchr_VA : 0.0 + # Jul 07 02:57:43 europa grott[1222736]: - bat_Volt : 0.0 + # Jul 07 02:57:43 europa grott[1222736]: - batterySoc : 0 + # Jul 07 02:57:43 europa grott[1222736]: - bus_volt : 0.0 + # Jul 07 02:57:43 europa grott[1222736]: - grid_volt : 0.0 + # Jul 07 02:57:43 europa grott[1222736]: - line_freq : 0.0 + # Jul 07 02:57:43 europa grott[1222736]: - outputvolt : 0.0 + # Jul 07 02:57:43 europa grott[1222736]: - pvgridvoltage : 0.0 + # Jul 07 02:57:43 europa grott[1222736]: - outputfreq : 0.0 + # Jul 07 02:57:43 europa grott[1222736]: - invtemp : 0.0 + # Jul 07 02:57:43 europa grott[1222736]: - dcdctemp : 0.0 + # Jul 07 02:57:43 europa grott[1222736]: - loadpercent : 0.0 + # Jul 07 02:57:43 europa grott[1222736]: - buck1_ntc : 0.0 + # Jul 07 02:57:43 europa grott[1222736]: - buck2_ntc : 0.0 + # Jul 07 02:57:43 europa grott[1222736]: - OP_Curr : 0.1 + # Jul 07 02:57:43 europa grott[1222736]: - Inv_Curr : 0.0 + # Jul 07 02:57:43 europa grott[1222736]: - AC_InWatt : 78862.0 + # Jul 07 02:57:43 europa grott[1222736]: - AC_InVA : 14339276.8 + # Jul 07 02:57:43 europa grott[1222736]: - faultBit : 0 + # Jul 07 02:57:43 europa grott[1222736]: - warningBit : 0 + # Jul 07 02:57:43 europa grott[1222736]: - faultValue : 0 + # Jul 07 02:57:43 europa grott[1222736]: - warningValue : 0 + # Jul 07 02:57:43 europa grott[1222736]: - constantPowerOK : 0 + # Jul 07 02:57:43 europa grott[1222736]: - epvtoday : 4.5 + # Jul 07 02:57:43 europa grott[1222736]: - pvenergytoday : 4.5 + # Jul 07 02:57:43 europa grott[1222736]: - epvtotal : 2972.4 + # Jul 07 02:57:43 europa grott[1222736]: - eacCharToday : 0.0 + # Jul 07 02:57:43 europa grott[1222736]: - eacCharTotal : 68.9 + # Jul 07 02:57:43 europa grott[1222736]: - ebatDischarToday : 1.0 + # Jul 07 02:57:43 europa grott[1222736]: - ebatDischarTotal : 2155.9 + # Jul 07 02:57:43 europa grott[1222736]: - eacDischarToday : 0.0 + # Jul 07 02:57:43 europa grott[1222736]: - eacDischarTotal : 0.0 + # Jul 07 02:57:43 europa grott[1222736]: - ACCharCurr : 0.0 + # Jul 07 02:57:43 europa grott[1222736]: - ACDischarWatt : 0.0 + # Jul 07 02:57:43 europa grott[1222736]: - ACDischarVA : 0.0 + # Jul 07 02:57:43 europa grott[1222736]: - BatDischarWatt : 376.0 + # Jul 07 02:57:43 europa grott[1222736]: - BatDischarVA : 376.0 + # Jul 07 02:57:43 europa grott[1222736]: - BatWatt : 391.0 + invalid_data_1 = b'\x00\xa9\x00\x06\x01\x5f\x01\x04\x03\x36\x2b\x47\x22\x35\x36\x76\x4b\x5e\x77'\ + b'\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72'\ + b'\x28\x2e\x29\x46\x36\x0b\x3e\x5f\x4f\x51\x74\x74\x47\x72\x6f\x77\x61\x74\x74'\ + b'\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x76\x73\x72\x50\x75\x55\x74\x61'\ + b'\x74\x74\x6b\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x8b\x14\x74\x47\x98\x0f'\ + b'\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47'\ + b'\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74'\ + b'\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x46\x72\x6f\x77'\ + b'\x6d\x7c\xf8\x4f\xfe\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x5f'\ + b'\x6f\x2e\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x59\x47\x72\x1b\x6b\x61\x74\x74'\ + b'\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x63\xc5\x74\x47\x72\x65\x77\x61'\ + b'\x20\x43\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f'\ + b'\x77\x61\x74\x7a\xf7\x72\x6f\x79\xd1\x74\x74\x48\x34\x6f\x77\x61\x74\x74\x47'\ + B'\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x7d\x61\x74\x20\x70\x72\x6f\x77\x3b\x74'\ + b'\xf2\x47\x72\x6f\x76\x9f\x09\x74\x47\x72\x6c\x77\x61\x74\x74\x47\x72\x6f\x76'\ + b'\x61\x74\x00\x5b\x72\x42\x80\xba\x83\xaf\xb0\xa9\x98\xac\x96\xaf\x74\x47\x72'\ + b'\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74'\ + b'\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61'\ + b'\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x9c\x45' + + + # ---------- this data has absurd epvtoday value and should not be processed + # Jul 06 22:40:59 europa grott[1222736]: - Grott automatic protocol detection + # Jul 06 22:40:59 europa grott[1222736]: - Grott data record length 359 + # Jul 06 22:40:59 europa grott[1222736]: - layout : T060104SPF + # Jul 06 22:40:59 europa grott[1222736]: - no matching record layout found, try generic + # Jul 06 22:40:59 europa grott[1222736]: - Record layout used : T06NNNNSPF + # Jul 06 22:40:59 europa grott[1222736]: - Growatt data decrypted V2 + # Jul 06 22:40:59 europa grott[1222736]: - Grott Growatt data decrypted + # Jul 06 22:40:59 europa grott[1222736]: - Growatt plain data: + # Jul 06 22:40:59 europa grott[1222736]: 00760006015f01044444443043414231393100000000000000000000000000000000000000004 + # Jul 06 22:40:59 europa grott[1222736]: 7594832424c4c303830000000000000000000000000000000000000000017070617073a030000 + # Jul 06 22:40:59 europa grott[1222736]: 002c00000000000000000000ea600000ea6000000000000000000000000000000000000000000 + # Jul 06 22:40:59 europa grott[1222736]: 00000000000000000000000000000000000000000000000000000000000000000010000000c07 + # Jul 06 22:40:59 europa grott[1222736]: 44074400000af000000af000660000002d0059000003160000053c000000000000000014fa006 + # Jul 06 22:40:59 europa grott[1222736]: 400000000000008201388000001610183001f14f0000000000000000000000004000400000000 + # Jul 06 22:40:59 europa grott[1222736]: 0000000000000000000000004e2500000000000000000006000073f500000000005a008600660 + # Jul 06 22:40:59 europa grott[1222736]: 00101e600000003000000000000022e000073f50006f7dbf7dbf7dbf7dbf7db00000000000000 + # Jul 06 22:40:59 europa grott[1222736]: 00000000000000000000000000000000000000000000000000000000000000000000000000000 + # Jul 06 22:40:59 europa grott[1222736]: 000000000000000000000792a + # Jul 06 22:40:59 europa grott[1222736]: - Growatt new layout processing + # Jul 06 22:40:59 europa grott[1222736]: - decrypt : True + # Jul 06 22:40:59 europa grott[1222736]: - offset : 6 + # Jul 06 22:40:59 europa grott[1222736]: - record layout : T06NNNNSPF + # Jul 06 22:40:59 europa grott[1222736]: - Grott server date/time used + # Jul 06 22:40:59 europa grott[1222736]: - Grott values retrieved: + # Jul 06 22:40:59 europa grott[1222736]: - datalogserial : DDD0CAB191 + # Jul 06 22:40:59 europa grott[1222736]: - pvserial : GYH2BLL080 + # Jul 06 22:40:59 europa grott[1222736]: - pvstatus : 0 + # Jul 06 22:40:59 europa grott[1222736]: - vpv1 : 0.0 + # Jul 06 22:40:59 europa grott[1222736]: - vpv2 : 0.0 + # Jul 06 22:40:59 europa grott[1222736]: - ppv1 : 0.0 + # Jul 06 22:40:59 europa grott[1222736]: - ppv2 : 393216000.0 + # Jul 06 22:40:59 europa grott[1222736]: - buck1curr : 6000.0 + # Jul 06 22:40:59 europa grott[1222736]: - buck2curr : 0.0 + # Jul 06 22:40:59 europa grott[1222736]: - op_watt : 0.0 + # Jul 06 22:40:59 europa grott[1222736]: - pvpowerout : 0.0 + # Jul 06 22:40:59 europa grott[1222736]: - op_va : 0.0 + # Jul 06 22:40:59 europa grott[1222736]: - acchr_watt : 0.0 + # Jul 06 22:40:59 europa grott[1222736]: - acchr_VA : 0.0 + # Jul 06 22:40:59 europa grott[1222736]: - bat_Volt : 0.0 + # Jul 06 22:40:59 europa grott[1222736]: - batterySoc : 0 + # Jul 06 22:40:59 europa grott[1222736]: - bus_volt : 0.0 + # Jul 06 22:40:59 europa grott[1222736]: - grid_volt : 0.0 + # Jul 06 22:40:59 europa grott[1222736]: - line_freq : 0.0 + # Jul 06 22:40:59 europa grott[1222736]: - outputvolt : 0.0 + # Jul 06 22:40:59 europa grott[1222736]: - pvgridvoltage : 0.0 + # Jul 06 22:40:59 europa grott[1222736]: - outputfreq : 0.0 + # Jul 06 22:40:59 europa grott[1222736]: - invtemp : 0.0 + # Jul 06 22:40:59 europa grott[1222736]: - dcdctemp : 0.0 + # Jul 06 22:40:59 europa grott[1222736]: - loadpercent : 0.0 + # Jul 06 22:40:59 europa grott[1222736]: - buck1_ntc : 0.0 + # Jul 06 22:40:59 europa grott[1222736]: - buck2_ntc : 0.0 + # Jul 06 22:40:59 europa grott[1222736]: - OP_Curr : 0.1 + # Jul 06 22:40:59 europa grott[1222736]: - Inv_Curr : 0.0 + # Jul 06 22:40:59 europa grott[1222736]: - AC_InWatt : 78829.2 + # Jul 06 22:40:59 europa grott[1222736]: - AC_InVA : 12189696.0 + # Jul 06 22:40:59 europa grott[1222736]: - faultBit : 2800 + # Jul 06 22:40:59 europa grott[1222736]: - warningBit : 0 + # Jul 06 22:40:59 europa grott[1222736]: - faultValue : 2800 + # Jul 06 22:40:59 europa grott[1222736]: - warningValue : 102 + # Jul 06 22:40:59 europa grott[1222736]: - constantPowerOK : 0 + # Jul 06 22:40:59 europa grott[1222736]: - epvtoday : 8781824.0 + # Jul 06 22:40:59 europa grott[1222736]: - pvenergytoday : 8781824.0 + # Jul 06 22:40:59 europa grott[1222736]: - epvtotal : 0.0 + # Jul 06 22:40:59 europa grott[1222736]: - eacCharToday : 0.0 + # Jul 06 22:40:59 europa grott[1222736]: - eacCharTotal : 13631988.0 + # Jul 06 22:40:59 europa grott[1222736]: - ebatDischarToday : 35.3 + # Jul 06 22:40:59 europa grott[1222736]: - ebatDischarTotal : 2536246.3 + # Jul 06 22:40:59 europa grott[1222736]: - eacDischarToday : 35127296.0 + # Jul 06 22:40:59 europa grott[1222736]: - eacDischarTotal : 0.0 + # Jul 06 22:40:59 europa grott[1222736]: - ACCharCurr : 0.0 + # Jul 06 22:40:59 europa grott[1222736]: - ACDischarWatt : 0.4 + # Jul 06 22:40:59 europa grott[1222736]: - ACDischarVA : 26214.4 + # Jul 06 22:40:59 europa grott[1222736]: - BatDischarWatt : 0.0 + # Jul 06 22:40:59 europa grott[1222736]: - BatDischarVA : 0.0 + # Jul 06 22:40:59 europa grott[1222736]: - BatWatt : 0.0 + invalid_data_2 = b'\x00\x77\x00\x06\x01\x5f\x01\x04\x03\x36\x2b\x47\x22\x35\x36\x76\x4b\x5e\x77'\ + b'\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72'\ + b'\x28\x2e\x29\x46\x36\x0b\x3e\x5f\x4f\x51\x74\x74\x47\x72\x6f\x77\x61\x74\x74'\ + b' \x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x76\x73\x72\x50\x75\x55\x74\x61'\ + b'\x74\x74\x6b\x72\x63\x70\x12\x73\x07\x47\x72\x66\x67\x61\x74\x7d\x57\x72\x39'\ + b'\x77\x61\x74\x74\x44\x52\x6f\x77\x64\x32\x74\x47\x72\x6f\x77\x61\x74\x74\x53'\ + b'\x88\x6f\x13\x61\x74\x74\x47\x72\x6f\x7f\x41\x67\xfc\x47\x72\x6e\x21\x60\xf9'\ + b'\x74\x58\x66\x9f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x43\x72\x6b\x77'\ + b'\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x3a\x51\x47\x5f'\ + b'\x6f\x2e\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x73\x47\x72\x1c\x81\x61\x74\x74'\ + b'\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x63\xc5\x74\x47\x72\x6a\x77\x61'\ + b'\x20\x46\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f'\ + b'\x77\x61\x74\x76\x97\x72\x6f\x75\xb1\x8b\x8b\xb6\xa0\x6f\x77\x61\x74\x74\x47'\ + b'\x72\x6f\x77\x22\x74\x74\x47\x72\x6f\x72\x61\x74\x20\x75\x72\x6f\x77\x3b\x74'\ + b'\xf2\x47\x26\x6f\x76\x60\xf0\x74\x47\x72\x6c\x77\x61\x74\x74\x47\x72\x6e\xbc'\ + b'\x61\x74\x07\xb1\x72\x68\x80\xba\x83\xaf\xb0\xa9\x98\xac\x96\xaf\x74\x47\x72'\ + b'\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74'\ + b'\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61'\ + b'\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x35\xd3' + @pytest.mark.parametrize('cfg_file, expected', [ ('tests/testdata/grott_applydividers_test_mqtt_true.ini', 59), ('tests/testdata/grott_applydividers_test_mqtt_false.ini', 590)]) @@ -149,4 +342,16 @@ def test_influxdb_applydividers(self, cfg_file, expected): assert payload[0]['measurement'] == 'GYH2BLL080' assert payload[0]['fields']['pvserial'] == 'GYH2BLL080' assert payload[0]['fields']['BatWatt'] == expected - + + + @patch('grottdata.publish.single') + def test_discard_invalid_data_1(self, mock_publish_single): + conf = Conf("2.7.8", cmdargs=['-c', 'tests/testdata/grott_applydividers_test_mqtt_true.ini']) + grottdata.procdata(conf,self.invalid_data_1) + assert mock_publish_single.called == False + + @patch('grottdata.publish.single') + def test_discard_invalid_data_2(self, mock_publish_single): + conf = Conf("2.7.8", cmdargs=['-c', 'tests/testdata/grott_applydividers_test_mqtt_true.ini']) + grottdata.procdata(conf,self.invalid_data_2) + assert mock_publish_single.called == False \ No newline at end of file diff --git a/tests/testdata/conf_empty.ini b/tests/testdata/conf_empty.ini new file mode 100644 index 0000000..f69c008 --- /dev/null +++ b/tests/testdata/conf_empty.ini @@ -0,0 +1,5 @@ +[MQTT] +applydividers = False + +[influx] +applydividers = False \ No newline at end of file From e0039a74635c87768361d5610af82248eca1e5ef Mon Sep 17 00:00:00 2001 From: Ken Date: Sat, 8 Jul 2023 13:48:25 +0000 Subject: [PATCH 15/27] - print out full message before discarding it, if required --- grottdata.py | 106 +++++++++++++++++++++++++-------------------------- 1 file changed, 52 insertions(+), 54 deletions(-) diff --git a/grottdata.py b/grottdata.py index 787b0f0..8348ef0 100644 --- a/grottdata.py +++ b/grottdata.py @@ -12,7 +12,7 @@ import textwrap from itertools import cycle # to support "cycling" the iterator import json, codecs -from typing import Dict +from typing import Dict, Optional # requests #import mqtt @@ -79,22 +79,22 @@ def str2bool(defstr): return(defret) else : return() -def is_message_valid(definedkey:dict) -> bool: +def is_message_valid(definedkey:dict) -> Optional[str]: # TODO : this test is a too arbitrary. We just assume that no Growatt inverter can handle more than 12000W of input solar power. if 'ppv1' in definedkey: if definedkey['ppv1'] > 12000: - return False + return 'ppv1' if 'ppv2' in definedkey: if definedkey['ppv2'] > 12000: - return False + return 'ppv2' # TODO : this test is a too arbitrary. We just assume that no Growatt inverter can produce more than 200kWh of energy in a single day. if 'epvtoday' in definedkey: if definedkey['epvtoday'] > 200: - return False + return 'epvtoday' - return True + return None def procdata(conf,data): if conf.verbose: @@ -395,60 +395,58 @@ def procdata(conf,data): print("\t - "+ 'Growatt unprocessed Data:') print(format_multi_line("\t\t - ", result_string)) + # print data to the standard output if verbose is set + if dataprocessed and conf.verbose: + if conf.compat : + #print in compatibility mode + definedkey["pvserial"] = codecs.decode(definedkey["pvserial"], "hex").decode('utf-8') + print("\t - " + "Grott values retrieved:") + print("\t\t - " + "pvserial: ", definedkey["pvserial"]) + print("\t\t - " + "pvstatus: ", definedkey["pvstatus"]) + print("\t\t - " + "pvpowerin: ", definedkey["pvpowerin"]/10) + print("\t\t - " + "pvpowerout: ", definedkey["pvpowerout"]/10) + print("\t\t - " + "pvenergytoday: ", definedkey["pvenergytoday"]/10) + print("\t\t - " + "pvenergytotal: ", definedkey["pvenergytotal"]/10) + print("\t\t - " + "pv1watt: ", definedkey["pv1watt"]/10) + print("\t\t - " + "pv2watt: ", definedkey["pv2watt"]/10) + print("\t\t - " + "pvfrequentie: ", definedkey["pvfrequentie"]/100) + print("\t\t - " + "pvgridvoltage: ", definedkey["pvgridvoltage"]/10) + print("\t\t - " + "pv1voltage: ", definedkey["pv1voltage"]/10) + print("\t\t - " + "pv1current: ", definedkey["pv1current"]/10) + print("\t\t - " + "pv2voltage: ", definedkey["pv2voltage"]/10) + print("\t\t - " + "pv2current: ", definedkey["pv2current"]/10) + print("\t\t - " + "pvtemperature: ", definedkey["pvtemperature"]/10) + print("\t\t - " + "pvipmtemperature: ", definedkey["pvipmtemperature"]/10) + else: + #dynamic print + print("\t - " + "Grott values retrieved:") + for key in definedkey : + # test if there is an divide factor is specifed + try: + #print(keyword) + keydivide = conf.recorddict[layout][key]["divide"] + #print(keydivide) + except: + #print("error") + keydivide = 1 + + if type(definedkey[key]) != type(str()) and keydivide != 1 : + printkey = "{:.1f}".format(definedkey[key]/keydivide) + else : + printkey = definedkey[key] + print("\t\t - ",key.ljust(20) + " : ",printkey) + # check if data is valid - # sometime my SPF6000T DVM MPV sends inconsistent data, which must be discared (see unit tests for examples of corrupted data) - if not is_message_valid(definedkey): - if conf.verbose: print("\t - " + 'Growatt data not consistent, processing stopped') + # @KevinEeckman : sometime my SPF6000T DVM MPV sends inconsistent data, which must be discared (see unit tests for examples of corrupted data) + invalid_key = is_message_valid(definedkey) + if invalid_key is not None: + if conf.verbose: print(f"\t - " + 'Growatt data not consistent ({invalid_key}), processing stopped') return if dataprocessed: # only sendout data to MQTT if it is processed. - - # Print values - if conf.verbose: - if conf.compat : - #print in compatibility mode - definedkey["pvserial"] = codecs.decode(definedkey["pvserial"], "hex").decode('utf-8') - print("\t - " + "Grott values retrieved:") - print("\t\t - " + "pvserial: ", definedkey["pvserial"]) - print("\t\t - " + "pvstatus: ", definedkey["pvstatus"]) - print("\t\t - " + "pvpowerin: ", definedkey["pvpowerin"]/10) - print("\t\t - " + "pvpowerout: ", definedkey["pvpowerout"]/10) - print("\t\t - " + "pvenergytoday: ", definedkey["pvenergytoday"]/10) - print("\t\t - " + "pvenergytotal: ", definedkey["pvenergytotal"]/10) - print("\t\t - " + "pv1watt: ", definedkey["pv1watt"]/10) - print("\t\t - " + "pv2watt: ", definedkey["pv2watt"]/10) - print("\t\t - " + "pvfrequentie: ", definedkey["pvfrequentie"]/100) - print("\t\t - " + "pvgridvoltage: ", definedkey["pvgridvoltage"]/10) - print("\t\t - " + "pv1voltage: ", definedkey["pv1voltage"]/10) - print("\t\t - " + "pv1current: ", definedkey["pv1current"]/10) - print("\t\t - " + "pv2voltage: ", definedkey["pv2voltage"]/10) - print("\t\t - " + "pv2current: ", definedkey["pv2current"]/10) - print("\t\t - " + "pvtemperature: ", definedkey["pvtemperature"]/10) - print("\t\t - " + "pvipmtemperature: ", definedkey["pvipmtemperature"]/10) - else: - #dynamic print - print("\t - " + "Grott values retrieved:") - for key in definedkey : - # test if there is an divide factor is specifed - try: - #print(keyword) - keydivide = conf.recorddict[layout][key]["divide"] - #print(keydivide) - except: - #print("error") - keydivide = 1 - - if type(definedkey[key]) != type(str()) and keydivide != 1 : - printkey = "{:.1f}".format(definedkey[key]/keydivide) - else : - printkey = definedkey[key] - print("\t\t - ",key.ljust(20) + " : ",printkey) + #create JSON message (first create obj dict and then convert to a JSON message) - #create JSON message (first create obj dict and then convert to a JSON message) - - - # filter invalid 0120 record (0 < voltage_l1 > 500 ) if header[14:16] == "20" : if (definedkey["voltage_l1"]/10 > 500) or (definedkey["voltage_l1"]/10 < 0) : From cf5ee9a30dd954b0176f7f5fc4daddeeb9be942f Mon Sep 17 00:00:00 2001 From: Johan Meijer <16508298+johanmeijer@users.noreply.github.com> Date: Fri, 17 May 2024 20:39:22 +0200 Subject: [PATCH 16/27] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 47bf31a..9758b00 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,7 @@ ## The Growatt Inverter Monitor [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/donate?business=RQFS46F9JTESQ&item_name=Grott+¤cy_code=EUR) -### !!!! This is Beta version (2.8.x) -### Be aware no changes (only hyper fixes) will be commited to 2.8.x anymore. A new version (for now 2.9.x) is being worked on. +### From 17-05-2024 this version (2.8.3 is the new master) Growatt inverters can send performance and status metrics (log data) to the Growatt company servers. The inverters rely on either a ShineWIFI module or a ShineLAN box to relay the data to Growatt. The metrics stored on the Growatt servers then can be viewed on the Growatt website or using the ShinePhone mobile app. @@ -14,6 +13,7 @@ The purpose of Grott is to read, parse and forward the *raw metrics as they are ### New in Version 2.8 * Added first SPA support (2.8.1) * Added first MIN support (2.8.2) +* For all changes see Version_history file (https://github.com/johanmeijer/grott/blob/2.8.3/Version_history.txt) ### New in Version 2.7 * Added first beta of **grottserver** to act as destination for inverter/datalogger data (remove need to cummunicate with internet). From 1146fb9142bd4de9336d8b21d72ffe325581312f Mon Sep 17 00:00:00 2001 From: Johan Meijer <16508298+johanmeijer@users.noreply.github.com> Date: Fri, 17 May 2024 21:01:40 +0200 Subject: [PATCH 17/27] Update README.md --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9758b00..4dfb5b1 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,17 @@ ## The Growatt Inverter Monitor [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/donate?business=RQFS46F9JTESQ&item_name=Grott+¤cy_code=EUR) -### From 17-05-2024 this version (2.8.3 is the new master) +### From 17-05-2024 this version (2.8.3) is the new master Growatt inverters can send performance and status metrics (log data) to the Growatt company servers. The inverters rely on either a ShineWIFI module or a ShineLAN box to relay the data to Growatt. The metrics stored on the Growatt servers then can be viewed on the Growatt website or using the ShinePhone mobile app. The purpose of Grott is to read, parse and forward the *raw metrics as they are sent* to Growatt servers. This means other applications can consume the raw Growatt metrics without relying on the Growatt API and servers and without delay. -### Please see: https://github.com/johanmeijer/grott/wiki/@Statement-of-use-and-limitations before using Grott. +Grottserver (under development) is emulating the Growatt server so you do not need a connection with the growatt servers anymore. Grottserver provides also an API interface to read and write Inverter and Datalogger registers. +For more information see: https://github.com/johanmeijer/grott/wiki/Grottserver. + +### Before using Grott please see: https://github.com/johanmeijer/grott/wiki/@Statement-of-use-and-limitations +### First time users please start with: https://github.com/johanmeijer/grott/wiki/@-First-time-installation ### New in Version 2.8 * Added first SPA support (2.8.1) From 352c74dff1f80e347b50467e4738dadad14e896e Mon Sep 17 00:00:00 2001 From: Johan Meijer <16508298+johanmeijer@users.noreply.github.com> Date: Tue, 18 Jun 2024 20:46:15 +0200 Subject: [PATCH 18/27] Add files via upload Copied modified extension from 2,.7.8 --- examples/Extensions/grottext.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/examples/Extensions/grottext.py b/examples/Extensions/grottext.py index 781f182..c7b5715 100644 --- a/examples/Extensions/grottext.py +++ b/examples/Extensions/grottext.py @@ -13,7 +13,7 @@ def grottext(conf,data,jsonmsg) : print("\t - " + "Grott extension module entered ") ### - ### uncomment this print statements if you want to see the information that is availble. + ### uncomment this print statements if you want to see the information that is available. ### #print(jsonmsg) @@ -21,8 +21,11 @@ def grottext(conf,data,jsonmsg) : #print(dir(conf)) #print(conf.extvar) - url = "http://" + conf.extvar["ip"] + ":" + str(conf.extvar["port"]) - + if "url" in conf.extvar: + url = conf.extvar["url"] + else: + url = f"http://{conf.extvar['ip']}:{conf.extvar['port']}" + try: r = requests.post(url, json = jsonmsg) @@ -33,5 +36,3 @@ def grottext(conf,data,jsonmsg) : #print(r.text) return resultcode - - \ No newline at end of file From 8880e203b4521a2b0a26f838c1e356eb64b93f59 Mon Sep 17 00:00:00 2001 From: Johan Meijer <16508298+johanmeijer@users.noreply.github.com> Date: Tue, 18 Jun 2024 21:24:49 +0200 Subject: [PATCH 19/27] Changed SPF layout add invFanSpeed to SPF layout --- Version_history.txt | 3 ++- grottconf.py | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Version_history.txt b/Version_history.txt index 12b5fcd..57d2871 100644 --- a/Version_history.txt +++ b/Version_history.txt @@ -253,4 +253,5 @@ Complement bugfix prepared by KBraham (issue / pull request #41) 2.8.3 - Removed space at the end of epvtotal and epv1today keywords in min layout (#481) - Changed bms_batterycurr in MIN layout to numx (issue #362) -- Add gpvuplimit environmental for limiting pvoutpy writes in docker container (#346), be ware need new docker image 2.8.3 (will be created in next gen) \ No newline at end of file +- Add gpvuplimit environmental for limiting pvoutpy writes in docker container (#346), be ware need new docker image 2.8.3 (will be created in next gen) +- add invFanSpeed to SPF layout \ No newline at end of file diff --git a/grottconf.py b/grottconf.py index 5587f11..52859b1 100644 --- a/grottconf.py +++ b/grottconf.py @@ -940,7 +940,8 @@ def set_reclayouts(self): "ACDischarVA" : {"value" :370, "length" : 4, "type" : "num", "divide" : 10}, "BatDischarWatt" : {"value" :378, "length" : 4, "type" : "num", "divide" : 10}, "BatDischarVA" : {"value" :386, "length" : 4, "type" : "num", "divide" : 10}, - "BatWatt" : {"value" :394, "length" : 4, "type" : "numx", "divide" : 10} + "BatWatt" : {"value" :394, "length" : 4, "type" : "numx", "divide" : 10}, + "invFanSpeed" : {"value" :414, "length" : 2, "type" : "num", "divide" : 1} } } self.recorddict8 = {"T06NNNNSPF": { @@ -998,7 +999,8 @@ def set_reclayouts(self): "ACDischarVA" : {"value" :450, "length" : 4, "type" : "num", "divide" : 10}, "BatDischarWatt" : {"value" :458, "length" : 4, "type" : "num", "divide" : 10}, "BatDischarVA" : {"value" :466, "length" : 4, "type" : "num", "divide" : 10}, - "BatWatt" : {"value" :474, "length" : 4, "type" : "numx", "divide" : 10} + "BatWatt" : {"value" :474, "length" : 4, "type" : "numx", "divide" : 10}, + "invFanSpeed" : {"value" :494, "length" : 2, "type" : "num", "divide" : 1} }} self.recorddict9 = {"T06NNNNXTL3": { From f52ece051b17efd4a1f57fa8a3e0d6cfeca5adf5 Mon Sep 17 00:00:00 2001 From: Johan Meijer <16508298+johanmeijer@users.noreply.github.com> Date: Tue, 18 Jun 2024 21:27:54 +0200 Subject: [PATCH 20/27] Changed SPF layout added invfanspeed to SPF layout --- Version_history.txt | 2 +- grottconf.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Version_history.txt b/Version_history.txt index 57d2871..ca482a8 100644 --- a/Version_history.txt +++ b/Version_history.txt @@ -254,4 +254,4 @@ Complement bugfix prepared by KBraham (issue / pull request #41) - Removed space at the end of epvtotal and epv1today keywords in min layout (#481) - Changed bms_batterycurr in MIN layout to numx (issue #362) - Add gpvuplimit environmental for limiting pvoutpy writes in docker container (#346), be ware need new docker image 2.8.3 (will be created in next gen) -- add invFanSpeed to SPF layout \ No newline at end of file +- add invfanspeed to SPF layout \ No newline at end of file diff --git a/grottconf.py b/grottconf.py index 52859b1..76917af 100644 --- a/grottconf.py +++ b/grottconf.py @@ -941,7 +941,7 @@ def set_reclayouts(self): "BatDischarWatt" : {"value" :378, "length" : 4, "type" : "num", "divide" : 10}, "BatDischarVA" : {"value" :386, "length" : 4, "type" : "num", "divide" : 10}, "BatWatt" : {"value" :394, "length" : 4, "type" : "numx", "divide" : 10}, - "invFanSpeed" : {"value" :414, "length" : 2, "type" : "num", "divide" : 1} + "invfanspeed" : {"value" :414, "length" : 2, "type" : "num", "divide" : 1} } } self.recorddict8 = {"T06NNNNSPF": { @@ -1000,7 +1000,7 @@ def set_reclayouts(self): "BatDischarWatt" : {"value" :458, "length" : 4, "type" : "num", "divide" : 10}, "BatDischarVA" : {"value" :466, "length" : 4, "type" : "num", "divide" : 10}, "BatWatt" : {"value" :474, "length" : 4, "type" : "numx", "divide" : 10}, - "invFanSpeed" : {"value" :494, "length" : 2, "type" : "num", "divide" : 1} + "invfanspeed" : {"value" :494, "length" : 2, "type" : "num", "divide" : 1} }} self.recorddict9 = {"T06NNNNXTL3": { From 823f79bb4c9d94f4acc63f9bd8bca0a78408e86f Mon Sep 17 00:00:00 2001 From: Johan Meijer <16508298+johanmeijer@users.noreply.github.com> Date: Mon, 22 Jul 2024 20:52:40 +0200 Subject: [PATCH 21/27] Add MOD layout to examples Added T06NNNNXMOD layout file to examples Copy this in base Grott directory Specify invtype = mod in grott.in Restart Grott --- examples/Record Layout/T06NNNNXMOD.json | 208 ++++++++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100644 examples/Record Layout/T06NNNNXMOD.json diff --git a/examples/Record Layout/T06NNNNXMOD.json b/examples/Record Layout/T06NNNNXMOD.json new file mode 100644 index 0000000..aa6760a --- /dev/null +++ b/examples/Record Layout/T06NNNNXMOD.json @@ -0,0 +1,208 @@ +{"T06NNNNXMOD": { + "decrypt" : {"value" :"true"}, + "datalogserial" : {"value" :16, "length" : 10, "type" : "text", "incl" : "yes"}, + "pvserial" : {"value" :76, "length" : 10, "type" : "text", "divide" : 10}, + "date" : {"value" :136, "divide" : 10}, + "group1start" : {"value" :150, "length" : 2, "type" : "num","incl" : "no"}, + "group1end" : {"value" :154, "length" : 2, "type" : "num","incl" : "no"}, + "pvstatus" : {"value" : 158,"length" : 2,"type" : "num","divide" : 1}, + "pvpowerin" : {"value" : 162,"length": 4,"type" : "numx","divide" : 10,"register" : 3001}, + "pv1voltage" :{"value" : 170,"length": 2,"type" : "numx","divide" : 10,"register" : 3003}, + "pv1current" : {"value" : 174,"length": 2,"type" : "numx","divide" : 10,"register" : 3004}, + "pv1watt" : {"value" : 178,"length": 4,"type" : "numx","divide" : 10,"register" : 3005}, + "pv2voltage" : {"value" : 186,"length": 2,"type" : "numx","divide" : 10,"register" : 3007}, + "pv2current" : {"value" : 190,"length": 2,"type" : "numx","divide" : 10,"register" : 3008}, + "pv2watt" : {"value" : 194,"length": 4,"type" : "numx","divide" : 10,"register" : 3009}, + "pv3voltage" : {"value" : 202,"length": 2,"type" : "numx","divide" : 10,"register" : 3011}, + "pv3current" : {"value" : 206,"length": 2,"type" : "numx","divide" : 10,"register" : 3012}, + "pv3watt" : {"value" : 210,"length": 4,"type" : "numx","divide" : 10,"register" : 3013}, + "pv4voltage" : {"value" : 218,"length": 2,"type" : "numx","divide" : 10,"register" : 3015}, + "pv4current" : {"value" : 222,"length": 2,"type" : "numx","divide" : 10,"register" : 3016}, + "pv4watt" : {"value" : 226,"length": 4,"type" : "numx","divide" : 10,"register" : 3017}, + "pvpowerout" : {"value" : 234,"length": 4,"type" : "numx","divide" : 10,"register" : 3019}, + "qac" : {"value" : 242,"length": 4,"type" : "numx","divide" : 10,"register" : 3021}, + "pac" : {"value" : 250,"length": 4,"type" : "numx","divide" : 10,"register" : 3023}, + "pvfrequency" : {"value" : 258,"length": 2,"type" : "numx","divide" : 100,"register" : 3025}, + "pvgridvoltage" : {"value" : 262,"length": 2,"type" : "numx","divide" : 10,"register" : 3026}, + "pvgridcurrent" : {"value" : 266,"length": 2,"type" : "numx","divide" : 10,"register" : 3027}, + "pvgridpower" : {"value" : 270,"length": 4,"type" : "numx","divide" : 10,"register" : 3028}, + "pvgridvoltage2" : {"value" : 278,"length": 2,"type" : "numx","divide" : 10,"register" : 3030}, + "pvgridcurrent2" : {"value" : 282,"length": 2,"type" : "numx","divide" : 10,"register" : 3031}, + "pvgridpower2" : {"value" : 286,"length": 4,"type" : "numx","divide" : 10,"register" : 3032}, + "pvgridvoltage3" : {"value" : 294,"length": 2,"type" : "numx","divide" : 10,"register" : 3034}, + "pvgridcurrent3" : {"value" : 298,"length": 2,"type" : "numx","divide" : 10,"register" : 3035}, + "pvgridpower3" : {"value" : 302,"length": 4,"type" : "numx","divide" : 10,"register" : 3036}, + "vacrs" : {"value" : 310,"length": 2,"type" : "numx","divide" : 10,"register" : 3038}, + "vacst" : {"value" : 314,"length": 2,"type" : "numx","divide" : 10,"register" : 3039}, + "vactr" : {"value" : 318,"length": 2,"type" : "numx","divide" : 10,"register" : 3040}, + "ptousertotal" : {"value" : 322,"length": 4,"type" : "numx","divide" : 10,"register" : 3041}, + "ptogridtotal" : {"value" : 330,"length": 4,"type" : "numx","divide" : 10,"register" : 3043}, + "ptoloadtotal" : {"value" : 338,"length": 4,"type" : "numx","divide" : 10,"register" : 3045}, + "totworktime" : {"value" : 346,"length": 4,"type" : "numx","divide" : 7200,"register" : 3047}, + "eactoday" : {"value" : 354,"length": 4,"type" : "numx","divide" : 10,"register" : 3049}, + "pvenergytoday" : {"value" : 354,"length": 4,"type" : "numx","divide" : 10,"register" : 3049}, + "eactotal" : {"value" : 362,"length": 4,"type" : "numx","divide" : 10,"register" : 3051}, + "pvenergytotal" : {"value" : 362,"length": 4,"type" : "numx","divide" : 10,"register" : 3051}, + "epvtotal" : {"value" : 370,"length": 4,"type" : "numx","divide" : 10,"register" : 3053}, + "epv1today" : {"value" : 378,"length": 4,"type" : "numx","divide" : 10,"register" : 3055}, + "epv1total" : {"value" : 386,"length": 4,"type" : "numx","divide" : 10,"register" : 3057}, + "epv2today" : {"value" : 394,"length": 4,"type" : "numx","divide" : 10,"register" : 3059}, + "epv2total" : {"value" : 402,"length": 4,"type" : "numx","divide" : 10,"register" : 3061}, + "epv3today" : {"value" : 410,"length": 4,"type" : "numx","divide" : 10,"register" : 3063}, + "epv3total" : {"value" : 418,"length": 4,"type" : "numx","divide" : 10,"register" : 3065}, + "etousertoday" : {"value" : 426,"length": 4,"type" : "numx","divide" : 10,"register" : 3067}, + "etousertotal" : {"value" : 434,"length": 4,"type" : "numx","divide" : 10,"register" : 3069}, + "etogridtoday" : {"value" : 442,"length": 4,"type" : "numx","divide" : 10,"register" : 3071}, + "etogridtotal" : {"value" : 450,"length": 4,"type" : "numx","divide" : 10,"register" : 3073}, + "eloadtoday" : {"value" : 458,"length": 4,"type" : "numx","divide" : 10,"register" : 3075}, + "eloadtotal" : {"value" : 466,"length": 4,"type" : "numx","divide" : 10,"register" : 3077}, + "epv4today" : {"value" : 474,"length": 4,"type" : "numx","divide" : 10,"register" : 3079}, + "epv4total" : {"value" : 482,"length": 4,"type" : "numx","divide" : 10,"register" : 3081}, + "epvtoday" : {"value" : 490,"length": 4,"type" : "numx","divide" : 10,"register" : 3083}, + "reserved3085" : {"value" : 498,"length": 2,"type" : "numx","divide" : 1,"register" : 3085, "incl" : "no"}, + "deratingmode" : {"value" : 502,"length": 2,"type" : "numx","divide" : 1,"register" : 3086}, + "iso" : {"value" : 506,"length": 2,"type" : "numx","divide" : 1,"register" : 3087}, + "dcir" : {"value" : 510,"length": 2,"type" : "numx","divide" : 10,"register" : 3088}, + "dcis" : {"value" : 514,"length": 2,"type" : "numx","divide" : 10,"register" : 3089}, + "dcit" : {"value" : 518,"length": 2,"type" : "numx","divide" : 10,"register" : 3090}, + "gfci" : {"value" : 522,"length": 2,"type" : "numx","divide" : 1,"register" : 3091}, + "busvoltage" : {"value" : 526,"length": 2,"type" : "numx","divide" : 10,"register" : 3092}, + "pvtemperature" : {"value" : 530,"length": 2,"type" : "numx","divide" : 10,"register" : 3093}, + "pvimptemperature" : {"value" : 534,"length": 2,"type" : "numx","divide" : 10,"register" : 3094}, + "boosttemperature" : {"value" : 538,"length": 2,"type" : "numx","divide" : 10,"register" : 3095}, + "temp4" : {"value" : 542,"length": 2,"type" : "numx","divide" : 10,"register" : 3096}, + "comboardtemperature": {"value" : 546,"length": 2,"type" : "numx","divide" : 10,"register" : 3097}, + "pbusvoltage" : {"value" : 550,"length": 2,"type" : "numx","divide" : 10,"register" : 3098}, + "nbusvoltage" : {"value" : 554,"length": 2,"type" : "numx","divide" : 10,"register" : 3099}, + "ipf" : {"value" : 558,"length": 2,"type" : "numx","divide" : 1,"register" : 3100}, + "realoppercent" : {"value" : 562,"length": 2,"type" : "numx","divide" : 1,"register" : 3101}, + "opfullwatt" : {"value" : 566,"length": 4,"type" : "numx","divide" : 10,"register" : 3102}, + "standbyflag" : {"value" : 574,"length": 2,"type" : "numx","divide" : 1,"register" : 3104}, + "faultmaincode" : {"value" : 578,"length": 2,"type" : "numx","divide" : 1,"register" : 3105}, + "warnmaincode" : {"value" : 582,"length": 2,"type" : "numx","divide" : 1,"register" : 3106}, + "faultsubcode" : {"value" : 586,"length": 2,"type" : "numx","divide" : 1,"register" : 3107}, + "warnsubcode" : {"value" : 590,"length": 2,"type" : "numx","divide" : 1,"register" : 3108}, + "reserved3109" : {"value" : 594,"length": 2,"type" : "numx","divide" : 1,"register" : 3109, "incl" : "no"}, + "reserved3110" : {"value" : 598,"length": 2,"type" : "numx","divide" : 1,"register" : 3110, "incl" : "no"}, + "uwpresentfftvaxxxlue[channela]": {"value" : 602,"length": 2,"type" : "numx","divide" : 1,"register" : 3111}, + "bafcistatus" : {"value" : 606,"length": 2,"type" : "numx","divide" : 1,"register" : 3112}, + "uwstrength[channela]": {"value" : 610,"length": 2,"type" : "numx","divide" : 1,"register" : 3113}, + "uwselfcheckvalue[channela]": {"value" : 614,"length": 2,"type" : "numx","divide" : 1,"register" : 3114}, + "invstartdelaytime" : {"value" : 618,"length": 2,"type" : "numx","divide" : 1,"register" : 3115}, + "reserved3116" : {"value" : 622,"length": 2,"type" : "numx","divide" : 1,"register" : 3116, "incl" : "no"}, + "reserved3117" : {"value" : 626,"length": 2,"type" : "numx","divide" : 1,"register" : 3117, "incl" : "no"}, + "bdconoffstate" : {"value" : 630,"length": 2,"type" : "numx","divide" : 1,"register" : 3118}, + "drycontactstate" : {"value" : 634,"length": 2,"type" : "numx","divide" : 1,"register" : 3119}, + "reserved3120" : {"value" : 638,"length": 2,"type" : "numx","divide" : 1,"register" : 3120, "incl" : "no"}, + "pself" : {"value" : 642,"length": 4,"type" : "numx","divide" : 10,"register" : 3121}, + "esystoday" : {"value" : 650,"length": 4,"type" : "numx","divide" : 10,"register" : 3123}, + "edischrtoday" : {"value" : 666,"length": 4,"type" : "numx","divide" : 10,"register" : 3125}, + "edischrtotal" : {"value" : 674,"length": 4,"type" : "numx","divide" : 10,"register" : 3127}, + "echrtoday" : {"value" : 682,"length": 4,"type" : "numx","divide" : 10,"register" : 3129}, + "echrtotal" : {"value" : 690,"length": 4,"type" : "numx","divide" : 10,"register" : 3131}, + "eacchrtoday" : {"value" : 698,"length": 4,"type" : "numx","divide" : 10,"register" : 3133}, + "eacchrtotal" : {"value" : 706,"length": 4,"type" : "numx","divide" : 10,"register" : 3135}, + "esystotal" : {"value" : 714,"length": 4,"type" : "numx","divide" : 1,"register" : 3137}, + "eselftoday" : {"value" : 722,"length": 4,"type" : "numx","divide" : 10,"register" : 3139}, + "eselftotal" : {"value" : 730,"length": 4,"type" : "numx","divide" : 10,"register" : 3141}, + "reserved3143" : {"value" : 738,"length": 2,"type" : "numx","divide" : 1,"register" : 3143}, + "priority" : {"value" : 742,"length": 2,"type" : "numx","divide" : 1,"register" : 3144}, + "epsfac" : {"value" : 746,"length": 2,"type" : "numx","divide" : 100,"register" : 3145}, + "epsvac1" : {"value" : 750,"length": 2,"type" : "numx","divide" : 10,"register" : 3146}, + "epsiac1" : {"value" : 754,"length": 2,"type" : "numx","divide" : 10,"register" : 3147}, + "epspac1" : {"value" : 742,"length": 4,"type" : "numx","divide" : 10,"register" : 3144}, + "epsvac2" : {"value" : 766,"length": 2,"type" : "numx","divide" : 10,"register" : 3150}, + "epsiac2" : {"value" : 770,"length": 2,"type" : "numx","divide" : 10,"register" : 3151}, + "epspac2" : {"value" : 774,"length": 4,"type" : "numx","divide" : 10,"register" : 3152}, + "epsvac3" : {"value" : 782,"length": 2,"type" : "numx","divide" : 10,"register" : 3154}, + "epsiac3" : {"value" : 786,"length": 2,"type" : "numx","divide" : 10,"register" : 3155}, + "epspac3" : {"value" : 790,"length": 4,"type" : "numx","divide" : 10,"register" : 3156}, + "epspac" : {"value" : 798,"length": 4,"type" : "numx","divide" : 10,"register" : 3158}, + "loadpercent" : {"value" : 806,"length": 2,"type" : "numx","divide" : 10,"register" : 3160}, + "pf" : {"value" : 810,"length": 2,"type" : "numx","divide" : 10,"register" : 3161}, + "dcv" : {"value" : 814,"length": 2,"type" : "numx","divide" : 1,"register" : 3162}, + "reserved3163" : {"value" : 818,"length": 2,"type" : "numx","divide" : 1,"register" : 3163, "incl" : "no"}, + "newbdcflag" : {"value" : 822,"length": 2,"type" : "numx","divide" : 1,"register" : 3164}, + "bdcderatingmode" : {"value" : 826,"length": 2,"type" : "numx","divide" : 1,"register" : 3165}, + "sysstatemode" : {"value" : 830,"length": 2,"type" : "numx","divide" : 1,"register" : 3166}, + "faultcode" : {"value" : 834,"length": 2,"type" : "numx","divide" : 1,"register" : 3167}, + "warncode" : {"value" : 838,"length": 2,"type" : "numx","divide" : 1,"register" : 3168}, + "vbat" : {"value" : 842,"length": 2,"type" : "numx","divide" : 100,"register" : 3169}, + "ibat" : {"value" : 846,"length": 2,"type" : "numx","divide" : 10,"register" : 3170}, + "soc" : {"value" : 850,"length": 2,"type" : "numx","divide" : 1,"register" : 3171}, + "vbus1" : {"value" : 854,"length": 2,"type" : "numx","divide" : 10,"register" : 3172}, + "vbus2" : {"value" : 858,"length": 2,"type" : "numx","divide" : 10,"register" : 3173}, + "ibb" : {"value" : 862,"length": 2,"type" : "numx","divide" : 10,"register" : 3174}, + "illc" : {"value" : 866,"length": 2,"type" : "numx","divide" : 10,"register" : 3175}, + "tempa" : {"value" : 870,"length": 2,"type" : "numx","divide" : 10,"register" : 3176}, + "tempb" : {"value" : 874,"length": 2,"type" : "numx","divide" : 10,"register" : 3177}, + "pdischr" : {"value" : 878,"length": 4,"type" : "numx","divide" : 10,"register" : 3178}, + "pchr" : {"value" : 886,"length": 4,"type" : "numx","divide" : 10,"register" : 3180}, + "pchrxxxl" : {"value" : 890,"length": 2,"type" : "numx","divide" : 1,"register" : 3181}, + "edischrtotalstor" : {"value" : 894,"length": 4,"type" : "numx","divide" : 10,"register" : 3182}, + "echrtotalstor" : {"value" : 902,"length": 4,"type" : "numx","divide" : 10,"register" : 3184}, + "reserved3186" : {"value" : 910,"length": 2,"type" : "numx","divide" : 1,"register" : 3186, "incl" : "no"}, + "bdc1flag" : {"value" : 914,"length": 2,"type" : "numx","divide" : 1,"register" : 3187}, + "vbus2low" : {"value" : 918,"length": 2,"type" : "numx","divide" : 10,"register" : 3188}, + "bmsmaxvoltcellno" : {"value" : 922,"length": 2,"type" : "numx","divide" : 1,"register" : 3189}, + "bmsminvoltcellno" : {"value" : 926,"length": 2,"type" : "numx","divide" : 1,"register" : 3190}, + "bmsbatteryavgtemp" : {"value" : 930,"length": 2,"type" : "numx","divide" : 1,"register" : 3191}, + "bmsmaxcelltemp" : {"value" : 934,"length": 2,"type" : "numx","divide" : 10,"register" : 3192}, + "bmsbatteryavgtemp2": {"value" : 938,"length": 2,"type" : "numx","divide" : 10,"register" : 3193}, + "bmsmaxcelltemp2" : {"value" : 942,"length": 2,"type" : "numx","divide" : 1,"register" : 3194}, + "bmsbatteryavgtemp3": {"value" : 946,"length": 2,"type" : "numx","divide" : 1,"register" : 3195}, + "bmsmaxsoc" : {"value" : 950,"length": 2,"type" : "numx","divide" : 1,"register" : 3196}, + "bmsminsoc" : {"value" : 954,"length": 2,"type" : "numx","divide" : 1,"register" : 3197}, + "parallelbatterynum": {"value" : 958,"length": 2,"type" : "numx","divide" : 1,"register" : 3198}, + "bmsderatereason" : {"value" : 962,"length": 2,"type" : "numx","divide" : 1,"register" : 3199}, + "bmsgaugefcc(ah)" : {"value" : 966,"length": 2,"type" : "numx","divide" : 1,"register" : 3200}, + "bmsgaugerm(ah)" : {"value" : 970,"length": 2,"type" : "numx","divide" : 1,"register" : 3201}, + "bmserror" : {"value" : 974,"length": 2,"type" : "numx","divide" : 1,"register" : 3202}, + "bmswarn" : {"value" : 978,"length": 2,"type" : "numx","divide" : 1,"register" : 3203}, + "bmsfault" : {"value" : 982,"length": 2,"type" : "numx","divide" : 1,"register" : 3204}, + "bmsfault2" : {"value" : 986,"length": 2,"type" : "numx","divide" : 1,"register" : 3205}, + "reserved3206" : {"value" : 990,"length": 2,"type" : "numx","divide" : 1,"register" : 3206, "incl" : "no"}, + "reserved3207" : {"value" : 994,"length": 2,"type" : "numx","divide" : 1,"register" : 3207, "incl" : "no"}, + "reserved3208" : {"value" : 998,"length": 2,"type" : "numx","divide" : 1,"register" : 3208, "incl" : "no"}, + "reserved3209" : {"value" : 1002,"length": 2,"type" : "numx","divide" : 1,"register" : 3209, "incl" : "no"}, + "batisostatus" : {"value" : 1006,"length": 2,"type" : "numx","divide" : 1,"register" : 3210}, + "battneedchargerequestflag": {"value" : 1010,"length": 2,"type" : "numx","divide" : 1,"register" : 3211}, + "bmsstatus" : {"value" : 1014,"length": 2,"type" : "numx","divide" : 1,"register" : 3212}, + "bmserror2" : {"value" : 1018,"length": 2,"type" : "numx","divide" : 1,"register" : 3213}, + "bmswarn2" : {"value" : 1022,"length": 2,"type" : "numx","divide" : 1,"register" : 3214}, + "bmssoc" : {"value" : 1026,"length": 2,"type" : "numx","divide" : 1,"register" : 3215}, + "bmsbatteryvolt" : {"value" : 1030,"length": 2,"type" : "numx","divide" : 100,"register" : 3216}, + "bmsbatterycurr" : {"value" : 1034,"length": 2,"type" : "numx","divide" : 100,"register" : 3217}, + "bmsbatterytemp" : {"value" : 1038,"length": 2,"type" : "numx","divide" : 10,"register" : 3218}, + "bmsmaxcurr" : {"value" : 1042,"length": 2,"type" : "numx","divide" : 100,"register" : 3219}, + "bmsmaxdischrcurr" : {"value" : 1046,"length": 2,"type" : "numx","divide" : 100,"register" : 3220}, + "bmscyclecnt" : {"value" : 1050,"length": 2,"type" : "numx","divide" : 1,"register" : 3221}, + "bmssoh" : {"value" : 1054,"length": 2,"type" : "numx","divide" : 1,"register" : 3222}, + "bmschargevoltlimit": {"value" : 1058,"length": 2,"type" : "numx","divide" : 100,"register" : 3223}, + "bmsdischargevoltlimit": {"value" : 1062,"length": 2,"type" : "numx","divide" : 1,"register" : 3224}, + "bmswarn3" : {"value" : 1066,"length": 2,"type" : "numx","divide" : 1,"register" : 3225}, + "bmserror3" : {"value" : 1070,"length": 2,"type" : "numx","divide" : 1,"register" : 3226}, + "reserved3227" : {"value" : 1074,"length": 2,"type" : "numx","divide" : 1,"register" : 3227, "incl" : "no"}, + "reserved3228" : {"value" : 1078,"length": 2,"type" : "numx","divide" : 1,"register" : 3228, "incl" : "no"}, + "reserved3229" : {"value" : 1082,"length": 2,"type" : "numx","divide" : 1,"register" : 3229, "incl" : "no"}, + "bmssinglevoltmax" : {"value" : 1086,"length": 2,"type" : "numx","divide" : 1,"register" : 3230}, + "bmssinglevoltmin" : {"value" : 1090,"length": 2,"type" : "numx","divide" : 1,"register" : 3231}, + "batloadvolt" : {"value" : 1094,"length": 2,"type" : "numx","divide" : 100,"register" : 3232}, + "reserved3233" : {"value" : 1098,"length": 2,"type" : "numx","divide" : 1,"register" : 3233, "incl" : "no"}, + "debugdata1" : {"value" : 1102,"length": 2,"type" : "numx","divide" : 1,"register" : 3234, "incl" : "no"}, + "debugdata2" : {"value" : 1106,"length": 2,"type" : "numx","divide" : 1,"register" : 3235, "incl" : "no"}, + "debugdata3" : {"value" : 1110,"length": 2,"type" : "numx","divide" : 1,"register" : 3236, "incl" : "no"}, + "debugdata4" : {"value" : 1114,"length": 2,"type" : "numx","divide" : 1,"register" : 3237, "incl" : "no"}, + "debugdata5" : {"value" : 1118,"length": 2,"type" : "numx","divide" : 1,"register" : 3238, "incl" : "no"}, + "debugdata6" : {"value" : 1122,"length": 2,"type" : "numx","divide" : 1,"register" : 3239, "incl" : "no"}, + "debugdata7" : {"value" : 1126,"length": 2,"type" : "numx","divide" : 1,"register" : 3240, "incl" : "no"}, + "debugdata8" : {"value" : 1130,"length": 2,"type" : "numx","divide" : 1,"register" : 3241, "incl" : "no"}, + "debugdata9" : {"value" : 1134,"length": 2,"type" : "numx","divide" : 1,"register" : 3242, "incl" : "no"}, + "debugdata10" : {"value" : 1138,"length": 2,"type" : "numx","divide" : 1,"register" : 3243, "incl" : "no"}, + "debugdata11" : {"value" : 1142,"length": 2,"type" : "numx","divide" : 1,"register" : 3244, "incl" : "no"}, + "debugdata12" : {"value" : 1146,"length": 2,"type" : "numx","divide" : 1,"register" : 3245, "incl" : "no"}, + "debugdata13" : {"value" : 1150,"length": 2,"type" : "numx","divide" : 1,"register" : 3246, "incl" : "no"}, + "debugdata14" : {"value" : 1154,"length": 2,"type" : "numx","divide" : 1,"register" : 3247, "incl" : "no"}, + "debugdata15" : {"value" : 1158,"length": 2,"type" : "numx","divide" : 1,"register" : 3248, "incl" : "no"}, + "debugdata16" : {"value" : 1162,"length": 2,"type" : "numx","divide" : 1,"register" : 3249, "incl" : "no"} + }} \ No newline at end of file From 36845fe1df38b7bdcdb6d02e43aecf991d67406a Mon Sep 17 00:00:00 2001 From: Johan Meijer <16508298+johanmeijer@users.noreply.github.com> Date: Mon, 22 Jul 2024 21:20:13 +0200 Subject: [PATCH 22/27] 2.8.3 (build 20240722) change default growatt IP address to server.growatt.com URL, hopefully this will prevent growatt from changing IP address and the need for changing IP address in grott.ini --- Version_history.txt | 5 ++++- grottconf.py | 5 +++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Version_history.txt b/Version_history.txt index ca482a8..befa606 100644 --- a/Version_history.txt +++ b/Version_history.txt @@ -254,4 +254,7 @@ Complement bugfix prepared by KBraham (issue / pull request #41) - Removed space at the end of epvtotal and epv1today keywords in min layout (#481) - Changed bms_batterycurr in MIN layout to numx (issue #362) - Add gpvuplimit environmental for limiting pvoutpy writes in docker container (#346), be ware need new docker image 2.8.3 (will be created in next gen) -- add invfanspeed to SPF layout \ No newline at end of file +- add invfanspeed to SPF layout +2.8.3 (20240722) +- add T06NNNNXMOD MOd type inverter layout to examples directory +- change default growatt IP address to server.growatt.com URL, hopefully this will prevent growatt from changing IP address and the need for changing IP address in grott.ini diff --git a/grottconf.py b/grottconf.py index 76917af..7803091 100644 --- a/grottconf.py +++ b/grottconf.py @@ -1,6 +1,6 @@ # # grottconf process command parameter and settings file -# Updated: 2023-12-04 +# Updated: 2024-07-22 # Version 2.8.3 import configparser, sys, argparse, os, json, io @@ -36,7 +36,8 @@ def __init__(self, vrm): self.tmzone = "local" #set timezone (at this moment only used for influxdb) #Growatt server default - self.growattip = "47.91.67.66" + #self.growattip = "47.91.67.66" + self.growattip = "server.growatt.com" self.growattport = 5279 #MQTT default From cfff02abec4f2e50b417668cf12c7fda544f47da Mon Sep 17 00:00:00 2001 From: Johan Meijer <16508298+johanmeijer@users.noreply.github.com> Date: Fri, 2 Aug 2024 21:18:54 +0200 Subject: [PATCH 23/27] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4dfb5b1..b213bb0 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ ## The Growatt Inverter Monitor [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/donate?business=RQFS46F9JTESQ&item_name=Grott+¤cy_code=EUR) - +#### Before using grott please read: (see: https://github.com/johanmeijer/grott/wiki/@disclaimer,-statement-of-use-and-limitations) ### From 17-05-2024 this version (2.8.3) is the new master Growatt inverters can send performance and status metrics (log data) to the Growatt company servers. The inverters rely on either a ShineWIFI module or a ShineLAN box to relay the data to Growatt. The metrics stored on the Growatt servers then can be viewed on the Growatt website or using the ShinePhone mobile app. From 73f93a603931113be9e0e5032e50ecf61cb48daa Mon Sep 17 00:00:00 2001 From: Johan Meijer <16508298+johanmeijer@users.noreply.github.com> Date: Fri, 2 Aug 2024 21:19:47 +0200 Subject: [PATCH 24/27] Update README.md From 4b6a4ec808d1cf8167ac0430dd35a3b7fbaf2553 Mon Sep 17 00:00:00 2001 From: Johan Meijer <16508298+johanmeijer@users.noreply.github.com> Date: Fri, 2 Aug 2024 21:20:57 +0200 Subject: [PATCH 25/27] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b213bb0..4c17a14 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ ## The Growatt Inverter Monitor [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/donate?business=RQFS46F9JTESQ&item_name=Grott+¤cy_code=EUR) -#### Before using grott please read: (see: https://github.com/johanmeijer/grott/wiki/@disclaimer,-statement-of-use-and-limitations) +#### Before using grott please read: https://github.com/johanmeijer/grott/wiki/@disclaimer,-statement-of-use-and-limitations ### From 17-05-2024 this version (2.8.3) is the new master Growatt inverters can send performance and status metrics (log data) to the Growatt company servers. The inverters rely on either a ShineWIFI module or a ShineLAN box to relay the data to Growatt. The metrics stored on the Growatt servers then can be viewed on the Growatt website or using the ShinePhone mobile app. From 09a99347b0ae79398071b26f198e2630aadd4eb3 Mon Sep 17 00:00:00 2001 From: Johan Meijer <16508298+johanmeijer@users.noreply.github.com> Date: Fri, 2 Aug 2024 21:25:40 +0200 Subject: [PATCH 26/27] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4c17a14..7a8ac61 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ ## The Growatt Inverter Monitor [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/donate?business=RQFS46F9JTESQ&item_name=Grott+¤cy_code=EUR) -#### Before using grott please read: https://github.com/johanmeijer/grott/wiki/@disclaimer,-statement-of-use-and-limitations +#### Before using grott please read disclaimer: https://github.com/johanmeijer/grott/wiki/@disclaimer,-statement-of-use-and-limitations ### From 17-05-2024 this version (2.8.3) is the new master Growatt inverters can send performance and status metrics (log data) to the Growatt company servers. The inverters rely on either a ShineWIFI module or a ShineLAN box to relay the data to Growatt. The metrics stored on the Growatt servers then can be viewed on the Growatt website or using the ShinePhone mobile app. From 3fa7ef970a48d29d477898d4a6041c9e948eda40 Mon Sep 17 00:00:00 2001 From: Johan Meijer <16508298+johanmeijer@users.noreply.github.com> Date: Tue, 3 Sep 2024 20:54:56 +0200 Subject: [PATCH 27/27] Copy MOD inverter layout in base folder --- T06NNNNXMOD.json | 208 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100644 T06NNNNXMOD.json diff --git a/T06NNNNXMOD.json b/T06NNNNXMOD.json new file mode 100644 index 0000000..aa6760a --- /dev/null +++ b/T06NNNNXMOD.json @@ -0,0 +1,208 @@ +{"T06NNNNXMOD": { + "decrypt" : {"value" :"true"}, + "datalogserial" : {"value" :16, "length" : 10, "type" : "text", "incl" : "yes"}, + "pvserial" : {"value" :76, "length" : 10, "type" : "text", "divide" : 10}, + "date" : {"value" :136, "divide" : 10}, + "group1start" : {"value" :150, "length" : 2, "type" : "num","incl" : "no"}, + "group1end" : {"value" :154, "length" : 2, "type" : "num","incl" : "no"}, + "pvstatus" : {"value" : 158,"length" : 2,"type" : "num","divide" : 1}, + "pvpowerin" : {"value" : 162,"length": 4,"type" : "numx","divide" : 10,"register" : 3001}, + "pv1voltage" :{"value" : 170,"length": 2,"type" : "numx","divide" : 10,"register" : 3003}, + "pv1current" : {"value" : 174,"length": 2,"type" : "numx","divide" : 10,"register" : 3004}, + "pv1watt" : {"value" : 178,"length": 4,"type" : "numx","divide" : 10,"register" : 3005}, + "pv2voltage" : {"value" : 186,"length": 2,"type" : "numx","divide" : 10,"register" : 3007}, + "pv2current" : {"value" : 190,"length": 2,"type" : "numx","divide" : 10,"register" : 3008}, + "pv2watt" : {"value" : 194,"length": 4,"type" : "numx","divide" : 10,"register" : 3009}, + "pv3voltage" : {"value" : 202,"length": 2,"type" : "numx","divide" : 10,"register" : 3011}, + "pv3current" : {"value" : 206,"length": 2,"type" : "numx","divide" : 10,"register" : 3012}, + "pv3watt" : {"value" : 210,"length": 4,"type" : "numx","divide" : 10,"register" : 3013}, + "pv4voltage" : {"value" : 218,"length": 2,"type" : "numx","divide" : 10,"register" : 3015}, + "pv4current" : {"value" : 222,"length": 2,"type" : "numx","divide" : 10,"register" : 3016}, + "pv4watt" : {"value" : 226,"length": 4,"type" : "numx","divide" : 10,"register" : 3017}, + "pvpowerout" : {"value" : 234,"length": 4,"type" : "numx","divide" : 10,"register" : 3019}, + "qac" : {"value" : 242,"length": 4,"type" : "numx","divide" : 10,"register" : 3021}, + "pac" : {"value" : 250,"length": 4,"type" : "numx","divide" : 10,"register" : 3023}, + "pvfrequency" : {"value" : 258,"length": 2,"type" : "numx","divide" : 100,"register" : 3025}, + "pvgridvoltage" : {"value" : 262,"length": 2,"type" : "numx","divide" : 10,"register" : 3026}, + "pvgridcurrent" : {"value" : 266,"length": 2,"type" : "numx","divide" : 10,"register" : 3027}, + "pvgridpower" : {"value" : 270,"length": 4,"type" : "numx","divide" : 10,"register" : 3028}, + "pvgridvoltage2" : {"value" : 278,"length": 2,"type" : "numx","divide" : 10,"register" : 3030}, + "pvgridcurrent2" : {"value" : 282,"length": 2,"type" : "numx","divide" : 10,"register" : 3031}, + "pvgridpower2" : {"value" : 286,"length": 4,"type" : "numx","divide" : 10,"register" : 3032}, + "pvgridvoltage3" : {"value" : 294,"length": 2,"type" : "numx","divide" : 10,"register" : 3034}, + "pvgridcurrent3" : {"value" : 298,"length": 2,"type" : "numx","divide" : 10,"register" : 3035}, + "pvgridpower3" : {"value" : 302,"length": 4,"type" : "numx","divide" : 10,"register" : 3036}, + "vacrs" : {"value" : 310,"length": 2,"type" : "numx","divide" : 10,"register" : 3038}, + "vacst" : {"value" : 314,"length": 2,"type" : "numx","divide" : 10,"register" : 3039}, + "vactr" : {"value" : 318,"length": 2,"type" : "numx","divide" : 10,"register" : 3040}, + "ptousertotal" : {"value" : 322,"length": 4,"type" : "numx","divide" : 10,"register" : 3041}, + "ptogridtotal" : {"value" : 330,"length": 4,"type" : "numx","divide" : 10,"register" : 3043}, + "ptoloadtotal" : {"value" : 338,"length": 4,"type" : "numx","divide" : 10,"register" : 3045}, + "totworktime" : {"value" : 346,"length": 4,"type" : "numx","divide" : 7200,"register" : 3047}, + "eactoday" : {"value" : 354,"length": 4,"type" : "numx","divide" : 10,"register" : 3049}, + "pvenergytoday" : {"value" : 354,"length": 4,"type" : "numx","divide" : 10,"register" : 3049}, + "eactotal" : {"value" : 362,"length": 4,"type" : "numx","divide" : 10,"register" : 3051}, + "pvenergytotal" : {"value" : 362,"length": 4,"type" : "numx","divide" : 10,"register" : 3051}, + "epvtotal" : {"value" : 370,"length": 4,"type" : "numx","divide" : 10,"register" : 3053}, + "epv1today" : {"value" : 378,"length": 4,"type" : "numx","divide" : 10,"register" : 3055}, + "epv1total" : {"value" : 386,"length": 4,"type" : "numx","divide" : 10,"register" : 3057}, + "epv2today" : {"value" : 394,"length": 4,"type" : "numx","divide" : 10,"register" : 3059}, + "epv2total" : {"value" : 402,"length": 4,"type" : "numx","divide" : 10,"register" : 3061}, + "epv3today" : {"value" : 410,"length": 4,"type" : "numx","divide" : 10,"register" : 3063}, + "epv3total" : {"value" : 418,"length": 4,"type" : "numx","divide" : 10,"register" : 3065}, + "etousertoday" : {"value" : 426,"length": 4,"type" : "numx","divide" : 10,"register" : 3067}, + "etousertotal" : {"value" : 434,"length": 4,"type" : "numx","divide" : 10,"register" : 3069}, + "etogridtoday" : {"value" : 442,"length": 4,"type" : "numx","divide" : 10,"register" : 3071}, + "etogridtotal" : {"value" : 450,"length": 4,"type" : "numx","divide" : 10,"register" : 3073}, + "eloadtoday" : {"value" : 458,"length": 4,"type" : "numx","divide" : 10,"register" : 3075}, + "eloadtotal" : {"value" : 466,"length": 4,"type" : "numx","divide" : 10,"register" : 3077}, + "epv4today" : {"value" : 474,"length": 4,"type" : "numx","divide" : 10,"register" : 3079}, + "epv4total" : {"value" : 482,"length": 4,"type" : "numx","divide" : 10,"register" : 3081}, + "epvtoday" : {"value" : 490,"length": 4,"type" : "numx","divide" : 10,"register" : 3083}, + "reserved3085" : {"value" : 498,"length": 2,"type" : "numx","divide" : 1,"register" : 3085, "incl" : "no"}, + "deratingmode" : {"value" : 502,"length": 2,"type" : "numx","divide" : 1,"register" : 3086}, + "iso" : {"value" : 506,"length": 2,"type" : "numx","divide" : 1,"register" : 3087}, + "dcir" : {"value" : 510,"length": 2,"type" : "numx","divide" : 10,"register" : 3088}, + "dcis" : {"value" : 514,"length": 2,"type" : "numx","divide" : 10,"register" : 3089}, + "dcit" : {"value" : 518,"length": 2,"type" : "numx","divide" : 10,"register" : 3090}, + "gfci" : {"value" : 522,"length": 2,"type" : "numx","divide" : 1,"register" : 3091}, + "busvoltage" : {"value" : 526,"length": 2,"type" : "numx","divide" : 10,"register" : 3092}, + "pvtemperature" : {"value" : 530,"length": 2,"type" : "numx","divide" : 10,"register" : 3093}, + "pvimptemperature" : {"value" : 534,"length": 2,"type" : "numx","divide" : 10,"register" : 3094}, + "boosttemperature" : {"value" : 538,"length": 2,"type" : "numx","divide" : 10,"register" : 3095}, + "temp4" : {"value" : 542,"length": 2,"type" : "numx","divide" : 10,"register" : 3096}, + "comboardtemperature": {"value" : 546,"length": 2,"type" : "numx","divide" : 10,"register" : 3097}, + "pbusvoltage" : {"value" : 550,"length": 2,"type" : "numx","divide" : 10,"register" : 3098}, + "nbusvoltage" : {"value" : 554,"length": 2,"type" : "numx","divide" : 10,"register" : 3099}, + "ipf" : {"value" : 558,"length": 2,"type" : "numx","divide" : 1,"register" : 3100}, + "realoppercent" : {"value" : 562,"length": 2,"type" : "numx","divide" : 1,"register" : 3101}, + "opfullwatt" : {"value" : 566,"length": 4,"type" : "numx","divide" : 10,"register" : 3102}, + "standbyflag" : {"value" : 574,"length": 2,"type" : "numx","divide" : 1,"register" : 3104}, + "faultmaincode" : {"value" : 578,"length": 2,"type" : "numx","divide" : 1,"register" : 3105}, + "warnmaincode" : {"value" : 582,"length": 2,"type" : "numx","divide" : 1,"register" : 3106}, + "faultsubcode" : {"value" : 586,"length": 2,"type" : "numx","divide" : 1,"register" : 3107}, + "warnsubcode" : {"value" : 590,"length": 2,"type" : "numx","divide" : 1,"register" : 3108}, + "reserved3109" : {"value" : 594,"length": 2,"type" : "numx","divide" : 1,"register" : 3109, "incl" : "no"}, + "reserved3110" : {"value" : 598,"length": 2,"type" : "numx","divide" : 1,"register" : 3110, "incl" : "no"}, + "uwpresentfftvaxxxlue[channela]": {"value" : 602,"length": 2,"type" : "numx","divide" : 1,"register" : 3111}, + "bafcistatus" : {"value" : 606,"length": 2,"type" : "numx","divide" : 1,"register" : 3112}, + "uwstrength[channela]": {"value" : 610,"length": 2,"type" : "numx","divide" : 1,"register" : 3113}, + "uwselfcheckvalue[channela]": {"value" : 614,"length": 2,"type" : "numx","divide" : 1,"register" : 3114}, + "invstartdelaytime" : {"value" : 618,"length": 2,"type" : "numx","divide" : 1,"register" : 3115}, + "reserved3116" : {"value" : 622,"length": 2,"type" : "numx","divide" : 1,"register" : 3116, "incl" : "no"}, + "reserved3117" : {"value" : 626,"length": 2,"type" : "numx","divide" : 1,"register" : 3117, "incl" : "no"}, + "bdconoffstate" : {"value" : 630,"length": 2,"type" : "numx","divide" : 1,"register" : 3118}, + "drycontactstate" : {"value" : 634,"length": 2,"type" : "numx","divide" : 1,"register" : 3119}, + "reserved3120" : {"value" : 638,"length": 2,"type" : "numx","divide" : 1,"register" : 3120, "incl" : "no"}, + "pself" : {"value" : 642,"length": 4,"type" : "numx","divide" : 10,"register" : 3121}, + "esystoday" : {"value" : 650,"length": 4,"type" : "numx","divide" : 10,"register" : 3123}, + "edischrtoday" : {"value" : 666,"length": 4,"type" : "numx","divide" : 10,"register" : 3125}, + "edischrtotal" : {"value" : 674,"length": 4,"type" : "numx","divide" : 10,"register" : 3127}, + "echrtoday" : {"value" : 682,"length": 4,"type" : "numx","divide" : 10,"register" : 3129}, + "echrtotal" : {"value" : 690,"length": 4,"type" : "numx","divide" : 10,"register" : 3131}, + "eacchrtoday" : {"value" : 698,"length": 4,"type" : "numx","divide" : 10,"register" : 3133}, + "eacchrtotal" : {"value" : 706,"length": 4,"type" : "numx","divide" : 10,"register" : 3135}, + "esystotal" : {"value" : 714,"length": 4,"type" : "numx","divide" : 1,"register" : 3137}, + "eselftoday" : {"value" : 722,"length": 4,"type" : "numx","divide" : 10,"register" : 3139}, + "eselftotal" : {"value" : 730,"length": 4,"type" : "numx","divide" : 10,"register" : 3141}, + "reserved3143" : {"value" : 738,"length": 2,"type" : "numx","divide" : 1,"register" : 3143}, + "priority" : {"value" : 742,"length": 2,"type" : "numx","divide" : 1,"register" : 3144}, + "epsfac" : {"value" : 746,"length": 2,"type" : "numx","divide" : 100,"register" : 3145}, + "epsvac1" : {"value" : 750,"length": 2,"type" : "numx","divide" : 10,"register" : 3146}, + "epsiac1" : {"value" : 754,"length": 2,"type" : "numx","divide" : 10,"register" : 3147}, + "epspac1" : {"value" : 742,"length": 4,"type" : "numx","divide" : 10,"register" : 3144}, + "epsvac2" : {"value" : 766,"length": 2,"type" : "numx","divide" : 10,"register" : 3150}, + "epsiac2" : {"value" : 770,"length": 2,"type" : "numx","divide" : 10,"register" : 3151}, + "epspac2" : {"value" : 774,"length": 4,"type" : "numx","divide" : 10,"register" : 3152}, + "epsvac3" : {"value" : 782,"length": 2,"type" : "numx","divide" : 10,"register" : 3154}, + "epsiac3" : {"value" : 786,"length": 2,"type" : "numx","divide" : 10,"register" : 3155}, + "epspac3" : {"value" : 790,"length": 4,"type" : "numx","divide" : 10,"register" : 3156}, + "epspac" : {"value" : 798,"length": 4,"type" : "numx","divide" : 10,"register" : 3158}, + "loadpercent" : {"value" : 806,"length": 2,"type" : "numx","divide" : 10,"register" : 3160}, + "pf" : {"value" : 810,"length": 2,"type" : "numx","divide" : 10,"register" : 3161}, + "dcv" : {"value" : 814,"length": 2,"type" : "numx","divide" : 1,"register" : 3162}, + "reserved3163" : {"value" : 818,"length": 2,"type" : "numx","divide" : 1,"register" : 3163, "incl" : "no"}, + "newbdcflag" : {"value" : 822,"length": 2,"type" : "numx","divide" : 1,"register" : 3164}, + "bdcderatingmode" : {"value" : 826,"length": 2,"type" : "numx","divide" : 1,"register" : 3165}, + "sysstatemode" : {"value" : 830,"length": 2,"type" : "numx","divide" : 1,"register" : 3166}, + "faultcode" : {"value" : 834,"length": 2,"type" : "numx","divide" : 1,"register" : 3167}, + "warncode" : {"value" : 838,"length": 2,"type" : "numx","divide" : 1,"register" : 3168}, + "vbat" : {"value" : 842,"length": 2,"type" : "numx","divide" : 100,"register" : 3169}, + "ibat" : {"value" : 846,"length": 2,"type" : "numx","divide" : 10,"register" : 3170}, + "soc" : {"value" : 850,"length": 2,"type" : "numx","divide" : 1,"register" : 3171}, + "vbus1" : {"value" : 854,"length": 2,"type" : "numx","divide" : 10,"register" : 3172}, + "vbus2" : {"value" : 858,"length": 2,"type" : "numx","divide" : 10,"register" : 3173}, + "ibb" : {"value" : 862,"length": 2,"type" : "numx","divide" : 10,"register" : 3174}, + "illc" : {"value" : 866,"length": 2,"type" : "numx","divide" : 10,"register" : 3175}, + "tempa" : {"value" : 870,"length": 2,"type" : "numx","divide" : 10,"register" : 3176}, + "tempb" : {"value" : 874,"length": 2,"type" : "numx","divide" : 10,"register" : 3177}, + "pdischr" : {"value" : 878,"length": 4,"type" : "numx","divide" : 10,"register" : 3178}, + "pchr" : {"value" : 886,"length": 4,"type" : "numx","divide" : 10,"register" : 3180}, + "pchrxxxl" : {"value" : 890,"length": 2,"type" : "numx","divide" : 1,"register" : 3181}, + "edischrtotalstor" : {"value" : 894,"length": 4,"type" : "numx","divide" : 10,"register" : 3182}, + "echrtotalstor" : {"value" : 902,"length": 4,"type" : "numx","divide" : 10,"register" : 3184}, + "reserved3186" : {"value" : 910,"length": 2,"type" : "numx","divide" : 1,"register" : 3186, "incl" : "no"}, + "bdc1flag" : {"value" : 914,"length": 2,"type" : "numx","divide" : 1,"register" : 3187}, + "vbus2low" : {"value" : 918,"length": 2,"type" : "numx","divide" : 10,"register" : 3188}, + "bmsmaxvoltcellno" : {"value" : 922,"length": 2,"type" : "numx","divide" : 1,"register" : 3189}, + "bmsminvoltcellno" : {"value" : 926,"length": 2,"type" : "numx","divide" : 1,"register" : 3190}, + "bmsbatteryavgtemp" : {"value" : 930,"length": 2,"type" : "numx","divide" : 1,"register" : 3191}, + "bmsmaxcelltemp" : {"value" : 934,"length": 2,"type" : "numx","divide" : 10,"register" : 3192}, + "bmsbatteryavgtemp2": {"value" : 938,"length": 2,"type" : "numx","divide" : 10,"register" : 3193}, + "bmsmaxcelltemp2" : {"value" : 942,"length": 2,"type" : "numx","divide" : 1,"register" : 3194}, + "bmsbatteryavgtemp3": {"value" : 946,"length": 2,"type" : "numx","divide" : 1,"register" : 3195}, + "bmsmaxsoc" : {"value" : 950,"length": 2,"type" : "numx","divide" : 1,"register" : 3196}, + "bmsminsoc" : {"value" : 954,"length": 2,"type" : "numx","divide" : 1,"register" : 3197}, + "parallelbatterynum": {"value" : 958,"length": 2,"type" : "numx","divide" : 1,"register" : 3198}, + "bmsderatereason" : {"value" : 962,"length": 2,"type" : "numx","divide" : 1,"register" : 3199}, + "bmsgaugefcc(ah)" : {"value" : 966,"length": 2,"type" : "numx","divide" : 1,"register" : 3200}, + "bmsgaugerm(ah)" : {"value" : 970,"length": 2,"type" : "numx","divide" : 1,"register" : 3201}, + "bmserror" : {"value" : 974,"length": 2,"type" : "numx","divide" : 1,"register" : 3202}, + "bmswarn" : {"value" : 978,"length": 2,"type" : "numx","divide" : 1,"register" : 3203}, + "bmsfault" : {"value" : 982,"length": 2,"type" : "numx","divide" : 1,"register" : 3204}, + "bmsfault2" : {"value" : 986,"length": 2,"type" : "numx","divide" : 1,"register" : 3205}, + "reserved3206" : {"value" : 990,"length": 2,"type" : "numx","divide" : 1,"register" : 3206, "incl" : "no"}, + "reserved3207" : {"value" : 994,"length": 2,"type" : "numx","divide" : 1,"register" : 3207, "incl" : "no"}, + "reserved3208" : {"value" : 998,"length": 2,"type" : "numx","divide" : 1,"register" : 3208, "incl" : "no"}, + "reserved3209" : {"value" : 1002,"length": 2,"type" : "numx","divide" : 1,"register" : 3209, "incl" : "no"}, + "batisostatus" : {"value" : 1006,"length": 2,"type" : "numx","divide" : 1,"register" : 3210}, + "battneedchargerequestflag": {"value" : 1010,"length": 2,"type" : "numx","divide" : 1,"register" : 3211}, + "bmsstatus" : {"value" : 1014,"length": 2,"type" : "numx","divide" : 1,"register" : 3212}, + "bmserror2" : {"value" : 1018,"length": 2,"type" : "numx","divide" : 1,"register" : 3213}, + "bmswarn2" : {"value" : 1022,"length": 2,"type" : "numx","divide" : 1,"register" : 3214}, + "bmssoc" : {"value" : 1026,"length": 2,"type" : "numx","divide" : 1,"register" : 3215}, + "bmsbatteryvolt" : {"value" : 1030,"length": 2,"type" : "numx","divide" : 100,"register" : 3216}, + "bmsbatterycurr" : {"value" : 1034,"length": 2,"type" : "numx","divide" : 100,"register" : 3217}, + "bmsbatterytemp" : {"value" : 1038,"length": 2,"type" : "numx","divide" : 10,"register" : 3218}, + "bmsmaxcurr" : {"value" : 1042,"length": 2,"type" : "numx","divide" : 100,"register" : 3219}, + "bmsmaxdischrcurr" : {"value" : 1046,"length": 2,"type" : "numx","divide" : 100,"register" : 3220}, + "bmscyclecnt" : {"value" : 1050,"length": 2,"type" : "numx","divide" : 1,"register" : 3221}, + "bmssoh" : {"value" : 1054,"length": 2,"type" : "numx","divide" : 1,"register" : 3222}, + "bmschargevoltlimit": {"value" : 1058,"length": 2,"type" : "numx","divide" : 100,"register" : 3223}, + "bmsdischargevoltlimit": {"value" : 1062,"length": 2,"type" : "numx","divide" : 1,"register" : 3224}, + "bmswarn3" : {"value" : 1066,"length": 2,"type" : "numx","divide" : 1,"register" : 3225}, + "bmserror3" : {"value" : 1070,"length": 2,"type" : "numx","divide" : 1,"register" : 3226}, + "reserved3227" : {"value" : 1074,"length": 2,"type" : "numx","divide" : 1,"register" : 3227, "incl" : "no"}, + "reserved3228" : {"value" : 1078,"length": 2,"type" : "numx","divide" : 1,"register" : 3228, "incl" : "no"}, + "reserved3229" : {"value" : 1082,"length": 2,"type" : "numx","divide" : 1,"register" : 3229, "incl" : "no"}, + "bmssinglevoltmax" : {"value" : 1086,"length": 2,"type" : "numx","divide" : 1,"register" : 3230}, + "bmssinglevoltmin" : {"value" : 1090,"length": 2,"type" : "numx","divide" : 1,"register" : 3231}, + "batloadvolt" : {"value" : 1094,"length": 2,"type" : "numx","divide" : 100,"register" : 3232}, + "reserved3233" : {"value" : 1098,"length": 2,"type" : "numx","divide" : 1,"register" : 3233, "incl" : "no"}, + "debugdata1" : {"value" : 1102,"length": 2,"type" : "numx","divide" : 1,"register" : 3234, "incl" : "no"}, + "debugdata2" : {"value" : 1106,"length": 2,"type" : "numx","divide" : 1,"register" : 3235, "incl" : "no"}, + "debugdata3" : {"value" : 1110,"length": 2,"type" : "numx","divide" : 1,"register" : 3236, "incl" : "no"}, + "debugdata4" : {"value" : 1114,"length": 2,"type" : "numx","divide" : 1,"register" : 3237, "incl" : "no"}, + "debugdata5" : {"value" : 1118,"length": 2,"type" : "numx","divide" : 1,"register" : 3238, "incl" : "no"}, + "debugdata6" : {"value" : 1122,"length": 2,"type" : "numx","divide" : 1,"register" : 3239, "incl" : "no"}, + "debugdata7" : {"value" : 1126,"length": 2,"type" : "numx","divide" : 1,"register" : 3240, "incl" : "no"}, + "debugdata8" : {"value" : 1130,"length": 2,"type" : "numx","divide" : 1,"register" : 3241, "incl" : "no"}, + "debugdata9" : {"value" : 1134,"length": 2,"type" : "numx","divide" : 1,"register" : 3242, "incl" : "no"}, + "debugdata10" : {"value" : 1138,"length": 2,"type" : "numx","divide" : 1,"register" : 3243, "incl" : "no"}, + "debugdata11" : {"value" : 1142,"length": 2,"type" : "numx","divide" : 1,"register" : 3244, "incl" : "no"}, + "debugdata12" : {"value" : 1146,"length": 2,"type" : "numx","divide" : 1,"register" : 3245, "incl" : "no"}, + "debugdata13" : {"value" : 1150,"length": 2,"type" : "numx","divide" : 1,"register" : 3246, "incl" : "no"}, + "debugdata14" : {"value" : 1154,"length": 2,"type" : "numx","divide" : 1,"register" : 3247, "incl" : "no"}, + "debugdata15" : {"value" : 1158,"length": 2,"type" : "numx","divide" : 1,"register" : 3248, "incl" : "no"}, + "debugdata16" : {"value" : 1162,"length": 2,"type" : "numx","divide" : 1,"register" : 3249, "incl" : "no"} + }} \ No newline at end of file