From 6abf9ee2f575fc3dcdf8ccc40bf0cef10a08efdd Mon Sep 17 00:00:00 2001 From: reserve85 <111107925+reserve85@users.noreply.github.com> Date: Fri, 19 Apr 2024 06:54:42 +0200 Subject: [PATCH 1/5] Add files via upload ## V1.91 ### script * support Home Assistant over HTTPS (https://github.com/reserve85/HoymilesZeroExport/issues/178) * Support login credentials for Tasmota (https://github.com/reserve85/HoymilesZeroExport/issues/159) ### config * add `[HOMEASSISTANT]`: `HA_HTTPS` * add `[INTERMEDIATE_HOMEASSISTANT]`: `HA_HTTPS_INTERMEDIATE` * add `[TASMOTA]`: `USER` and `PASS` * add `[TASMOTA_INTERMEDIATE]`: `USER_INTERMEDIATE` and `PASS_INTERMEDIATE` --- CHANGELOG.md | 10 ++++++++++ HoymilesZeroExport.py | 27 +++++++++++++++++++++------ HoymilesZeroExport_Config.ini | 12 +++++++++++- README.md | 6 +++--- 4 files changed, 45 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 321858f..e732cbc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## V1.91 +### script +* support Home Assistant over HTTPS (https://github.com/reserve85/HoymilesZeroExport/issues/178) +* Support login credentials for Tasmota (https://github.com/reserve85/HoymilesZeroExport/issues/159) +### config +* add `[HOMEASSISTANT]`: `HA_HTTPS` +* add `[INTERMEDIATE_HOMEASSISTANT]`: `HA_HTTPS_INTERMEDIATE` +* add `[TASMOTA]`: `USER` and `PASS` +* add `[TASMOTA_INTERMEDIATE]`: `USER_INTERMEDIATE` and `PASS_INTERMEDIATE` + ## V1.90 ### script * fix HOY_BATTERY_THRESHOLD_NORMAL_LIMIT_IN_V, see https://github.com/reserve85/HoymilesZeroExport/issues/174 diff --git a/HoymilesZeroExport.py b/HoymilesZeroExport.py index 393a640..6cc3ecc 100644 --- a/HoymilesZeroExport.py +++ b/HoymilesZeroExport.py @@ -15,7 +15,7 @@ # along with this program. If not, see . __author__ = "Tobias Kraft" -__version__ = "1.90" +__version__ = "1.91" import requests import time @@ -628,8 +628,10 @@ def GetPowermeterWatts(self) -> int: raise NotImplementedError() class Tasmota(Powermeter): - def __init__(self, ip: str, json_status: str, json_payload_mqtt_prefix: str, json_power_mqtt_label: str, json_power_input_mqtt_label: str, json_power_output_mqtt_label: str, json_power_calculate: bool): + def __init__(self, ip: str, user: str, password: str, json_status: str, json_payload_mqtt_prefix: str, json_power_mqtt_label: str, json_power_input_mqtt_label: str, json_power_output_mqtt_label: str, json_power_calculate: bool): self.ip = ip + self.user = user + self.password = password self.json_status = json_status self.json_payload_mqtt_prefix = json_payload_mqtt_prefix self.json_power_mqtt_label = json_power_mqtt_label @@ -637,12 +639,15 @@ def __init__(self, ip: str, json_status: str, json_payload_mqtt_prefix: str, jso self.json_power_output_mqtt_label = json_power_output_mqtt_label self.json_power_calculate = json_power_calculate - def GetJson(self, path): + def GetJson(self, path): url = f'http://{self.ip}{path}' return session.get(url, timeout=10).json() def GetPowermeterWatts(self): - ParsedData = self.GetJson('/cm?cmnd=status%2010') + if not self.user: + ParsedData = self.GetJson('/cm?cmnd=status%2010') + else: + ParsedData = self.GetJson(f'/cm?user={self.user}&password={self.password}&cmnd=status%2010') if not self.json_power_calculate: return CastToInt(ParsedData[self.json_status][self.json_payload_mqtt_prefix][self.json_power_mqtt_label]) else: @@ -766,9 +771,10 @@ def GetPowermeterWatts(self): return CastToInt(input - output) class HomeAssistant(Powermeter): - def __init__(self, ip: str, port: str, access_token: str, current_power_entity: str, power_calculate: bool, power_input_alias: str, power_output_alias: str): + def __init__(self, ip: str, port: str, use_https: bool, access_token: str, current_power_entity: str, power_calculate: bool, power_input_alias: str, power_output_alias: str): self.ip = ip self.port = port + self.use_https = use_https self.access_token = access_token self.current_power_entity = current_power_entity self.power_calculate = power_calculate @@ -776,7 +782,10 @@ def __init__(self, ip: str, port: str, access_token: str, current_power_entity: self.power_output_alias = power_output_alias def GetJson(self, path): - url = f"http://{self.ip}:{self.port}{path}" + if self.use_https: + url = f"https://{self.ip}:{self.port}{path}" + else: + url = f"http://{self.ip}:{self.port}{path}" headers = {"Authorization": "Bearer " + self.access_token, "content-type": "application/json"} return session.get(url, headers=headers, timeout=10).json() @@ -1119,6 +1128,8 @@ def CreatePowermeter() -> Powermeter: elif config.getboolean('SELECT_POWERMETER', 'USE_TASMOTA'): return Tasmota( config.get('TASMOTA', 'TASMOTA_IP'), + config.get('TASMOTA', 'TASMOTA_USER'), + config.get('TASMOTA', 'TASMOTA_PASS'), config.get('TASMOTA', 'TASMOTA_JSON_STATUS'), config.get('TASMOTA', 'TASMOTA_JSON_PAYLOAD_MQTT_PREFIX'), config.get('TASMOTA', 'TASMOTA_JSON_POWER_MQTT_LABEL'), @@ -1151,6 +1162,7 @@ def CreatePowermeter() -> Powermeter: return HomeAssistant( config.get('HOMEASSISTANT', 'HA_IP'), config.get('HOMEASSISTANT', 'HA_PORT'), + config.getboolean('HOMEASSISTANT', 'HA_HTTPS', fallback=False), config.get('HOMEASSISTANT', 'HA_ACCESSTOKEN'), config.get('HOMEASSISTANT', 'HA_CURRENT_POWER_ENTITY'), config.getboolean('HOMEASSISTANT', 'HA_POWER_CALCULATE'), @@ -1180,6 +1192,8 @@ def CreateIntermediatePowermeter(dtu: DTU) -> Powermeter: if config.getboolean('SELECT_INTERMEDIATE_METER', 'USE_TASMOTA_INTERMEDIATE'): return Tasmota( config.get('INTERMEDIATE_TASMOTA', 'TASMOTA_IP_INTERMEDIATE'), + config.get('INTERMEDIATE_TASMOTA', 'TASMOTA_USER_INTERMEDIATE'), + config.get('INTERMEDIATE_TASMOTA', 'TASMOTA_PASS_INTERMEDIATE'), config.get('INTERMEDIATE_TASMOTA', 'TASMOTA_JSON_STATUS_INTERMEDIATE'), config.get('INTERMEDIATE_TASMOTA', 'TASMOTA_JSON_PAYLOAD_MQTT_PREFIX_INTERMEDIATE'), config.get('INTERMEDIATE_TASMOTA', 'TASMOTA_JSON_POWER_MQTT_LABEL_INTERMEDIATE'), @@ -1229,6 +1243,7 @@ def CreateIntermediatePowermeter(dtu: DTU) -> Powermeter: return HomeAssistant( config.get('INTERMEDIATE_HOMEASSISTANT', 'HA_IP_INTERMEDIATE'), config.get('INTERMEDIATE_HOMEASSISTANT', 'HA_PORT_INTERMEDIATE'), + config.getboolean('INTERMEDIATE_HOMEASSISTANT', 'HA_HTTPS_INTERMEDIATE', fallback=False), config.get('INTERMEDIATE_HOMEASSISTANT', 'HA_ACCESSTOKEN_INTERMEDIATE'), config.get('INTERMEDIATE_HOMEASSISTANT', 'HA_CURRENT_POWER_ENTITY_INTERMEDIATE'), config.getboolean('INTERMEDIATE_HOMEASSISTANT', 'HA_POWER_CALCULATE_INTERMEDIATE', fallback=False), diff --git a/HoymilesZeroExport_Config.ini b/HoymilesZeroExport_Config.ini index 3a3190e..c5ee501 100644 --- a/HoymilesZeroExport_Config.ini +++ b/HoymilesZeroExport_Config.ini @@ -19,7 +19,7 @@ # --------------------------------------------------------------------- [VERSION] -VERSION = 1.89 +VERSION = 1.91 [SELECT_DTU] # --- define your DTU (only one) --- @@ -54,6 +54,9 @@ OPENDTU_PASS = [TASMOTA] # --- defines for Tasmota Smartmeter Modul--- TASMOTA_IP = xxx.xxx.xxx.xxx +# if you restricted the web login enter your username and password +TASMOTA_USER = +TASMOTA_PASS = # the following three constants describes how to navigate through the Tasmota-JSON. # if you do not know the format of your Tasmota reader, open a browser and put in the following string replacing xxx with the IP address of your Tasmota device: http://xxx.xxx.xxx.xxx/cm?cmnd=status%2010 # e.g. JSON_Result = {"StatusSNS":{"Time":"2023-02-28T12:49:49","SML":{"total_kwh":15011.575,"curr_w":-71}}} @@ -105,6 +108,8 @@ IOBROKER_POWER_OUTPUT_ALIAS = alias.0.Zaehler.Zaehler_CurrentOutputWatt # --- defines for HOME ASSISTANT --- HA_IP = xxx.xxx.xxx.xxx HA_PORT = 8123 +# if you run your HomeAssistant with "https" then define it here +HA_HTTPS = false # you need to create a Long-Lived Access Token in your Home Assistant: click on your profile, then scroll down to the bottom HA_ACCESSTOKEN = xxx HA_CURRENT_POWER_ENTITY = sensor.dtz541_sml_curr_w @@ -148,6 +153,9 @@ USE_VZLOGGER_INTERMEDIATE = false [INTERMEDIATE_TASMOTA] # --- defines for Tasmota Smartmeter Modul--- TASMOTA_IP_INTERMEDIATE = xxx.xxx.xxx.xxx +# if you restricted the web login enter your username and password +TASMOTA_USER_INTERMEDIATE = +TASMOTA_PASS_INTERMEDIATE = # the following three constants describes how to navigate through the Tasmota-JSON # e.g. JSON_Result = {"StatusSNS":{"Time":"2023-02-28T12:49:49","SML":{"curr_w":500}}} TASMOTA_JSON_STATUS_INTERMEDIATE = StatusSNS @@ -190,6 +198,8 @@ IOBROKER_CURRENT_POWER_ALIAS_INTERMEDIATE = alias.0.Zaehler.Zaehler_SolarCurrent # --- defines for HOME ASSISTANT (you need to create a Long-Lived Access Token in your profile) --- HA_IP_INTERMEDIATE = xxx.xxx.xxx.xxx HA_PORT_INTERMEDIATE = 8123 +# if you run your HomeAssistant with "https" then define it here +HA_HTTPS_INTERMEDIATE = false HA_ACCESSTOKEN_INTERMEDIATE = xxx HA_CURRENT_POWER_ENTITY_INTERMEDIATE = sensor.dtz541_sml_curr_w diff --git a/README.md b/README.md index 05d2439..f11bcf3 100644 --- a/README.md +++ b/README.md @@ -24,10 +24,10 @@ This script does not use MQTT, it's based on webapi communication. - [Ahoy](https://github.com/lumapu/ahoy) - this script is developed with AHOY and therefore i recommend it - [OpenDTU](https://github.com/tbnobody/OpenDTU) - Hoymiles MI, HM, HMS and HMT-Series Inverter (--> all inverters that are supported by AHOY / OpenDTU) -- **Note:** The Hoymiles inverts with build-in DTU devices communicating via WiFi, like HMS-xxxxW Series, **are not supported!** +- **Note:** The Hoymiles inverters with a build-in DTU, like the HMS-xxxxW Series, **are not supported!** ### Support of battery powered Hoymiles Inverters -With Version 1.28 and higher you can set various limits to support battery powered Hoymiles Inverters +You can set various limits to support battery powered Hoymiles Inverters - power-off limit - limit reduction if battery voltage drops - auto-power-on if battery voltage rises @@ -164,4 +164,4 @@ services: ## Donate and become a Sponsor [![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://paypal.me/TobiasWKraft/5) -Please support me if you like this project by spending me a coffee instead of giving away your electricity. +Please support me if you like this project by spending me a coffee instead of giving away your electricity. \ No newline at end of file From ae329dec319a795b0beb137e61e63b76b2c2b87b Mon Sep 17 00:00:00 2001 From: reserve85 <111107925+reserve85@users.noreply.github.com> Date: Fri, 19 Apr 2024 08:54:03 +0200 Subject: [PATCH 2/5] add argument option run update.sh with custom URL --- CHANGELOG.md | 1 + update.sh | 19 ++++++++++++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e732cbc..d7b4a6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### script * support Home Assistant over HTTPS (https://github.com/reserve85/HoymilesZeroExport/issues/178) * Support login credentials for Tasmota (https://github.com/reserve85/HoymilesZeroExport/issues/159) +* update.sh supports custom URL, for example a dev path. usage: ./update.sh https://github.com/reserve85/HoymilesZeroExport/archive/refs/heads/dev.zip ### config * add `[HOMEASSISTANT]`: `HA_HTTPS` * add `[INTERMEDIATE_HOMEASSISTANT]`: `HA_HTTPS_INTERMEDIATE` diff --git a/update.sh b/update.sh index d67e456..7a7af8e 100644 --- a/update.sh +++ b/update.sh @@ -1,9 +1,22 @@ #!/bin/bash -SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +# Check if an argument is provided +if [ $# -gt 1 ]; then + echo "Error: Too many arguments" +elif [ $# -eq 1 ]; then + # Use custom URL if provided + DOWNLOAD_URL="$1" +else + # Default URL if no argument provided + DOWNLOAD_URL="https://github.com/reserve85/HoymilesZeroExport/archive/refs/heads/main.zip" +fi + +# Change directory to the script's directory +SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) -echo "start update of HoymilesZeroExport" +echo "Start update of HoymilesZeroExport" -wget https://github.com/reserve85/HoymilesZeroExport/archive/refs/heads/main.zip +# Download and unzip the file +wget "$DOWNLOAD_URL" -O main.zip unzip main.zip rm main.zip From fba1775c94b2d08e43dd2a5d60fda29834bb5dab Mon Sep 17 00:00:00 2001 From: reserve85 <111107925+reserve85@users.noreply.github.com> Date: Sat, 20 Apr 2024 19:51:14 +0200 Subject: [PATCH 3/5] Add files via upload --- CHANGELOG.md | 2 +- update.sh | 16 +++++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d7b4a6b..76b0b3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ ### script * support Home Assistant over HTTPS (https://github.com/reserve85/HoymilesZeroExport/issues/178) * Support login credentials for Tasmota (https://github.com/reserve85/HoymilesZeroExport/issues/159) -* update.sh supports custom URL, for example a dev path. usage: ./update.sh https://github.com/reserve85/HoymilesZeroExport/archive/refs/heads/dev.zip +* update.sh supports custom branch, for example to update to dev path. usage: ./update.sh dev ### config * add `[HOMEASSISTANT]`: `HA_HTTPS` * add `[INTERMEDIATE_HOMEASSISTANT]`: `HA_HTTPS_INTERMEDIATE` diff --git a/update.sh b/update.sh index 7a7af8e..42e2749 100644 --- a/update.sh +++ b/update.sh @@ -4,11 +4,13 @@ if [ $# -gt 1 ]; then echo "Error: Too many arguments" elif [ $# -eq 1 ]; then # Use custom URL if provided - DOWNLOAD_URL="$1" + BRANCH="$1" else # Default URL if no argument provided - DOWNLOAD_URL="https://github.com/reserve85/HoymilesZeroExport/archive/refs/heads/main.zip" + BRANCH="main" fi +BASE_URL="https://github.com/reserve85/HoymilesZeroExport/archive/refs/heads/" +DOWNLOAD_URL="$BASE_URL$BRANCH.zip" # Change directory to the script's directory SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) @@ -16,17 +18,17 @@ SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) echo "Start update of HoymilesZeroExport" # Download and unzip the file -wget "$DOWNLOAD_URL" -O main.zip -unzip main.zip -rm main.zip +wget "$DOWNLOAD_URL" -O $BRANCH.zip +unzip $BRANCH.zip +rm $BRANCH.zip if [ $(dpkg-query -W -f='${Status}' rsync 2>/dev/null | grep -c "ok installed") -eq 0 ]; then apt-get install rsync; fi -rsync -a HoymilesZeroExport-main/ ./ -rm -r HoymilesZeroExport-main/ +rsync -a HoymilesZeroExport-$BRANCH/ ./ +rm -r HoymilesZeroExport-$BRANCH/ chmod +x $SCRIPT_DIR/install.sh chmod +x $SCRIPT_DIR/HoymilesZeroExport.py From 174cf0b46d2c13968e3e423feaa60ccf17616ea4 Mon Sep 17 00:00:00 2001 From: reserve85 <111107925+reserve85@users.noreply.github.com> Date: Sun, 21 Apr 2024 13:55:40 +0200 Subject: [PATCH 4/5] add enable/disable specific inverter add option to enable / disable a specific inverter --- CHANGELOG.md | 4 +++- HoymilesZeroExport.py | 4 +++- HoymilesZeroExport_Config.ini | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 76b0b3f..a125a6c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,11 +5,13 @@ * support Home Assistant over HTTPS (https://github.com/reserve85/HoymilesZeroExport/issues/178) * Support login credentials for Tasmota (https://github.com/reserve85/HoymilesZeroExport/issues/159) * update.sh supports custom branch, for example to update to dev path. usage: ./update.sh dev +* option to disable a inverter ### config * add `[HOMEASSISTANT]`: `HA_HTTPS` * add `[INTERMEDIATE_HOMEASSISTANT]`: `HA_HTTPS_INTERMEDIATE` * add `[TASMOTA]`: `USER` and `PASS` -* add `[TASMOTA_INTERMEDIATE]`: `USER_INTERMEDIATE` and `PASS_INTERMEDIATE` +* add `[TASMOTA_INTERMEDIATE]`: `USER_INTERMEDIATE` and `PASS_INTERMEDIATE` +* add `[INVERTER_x]`: `ENABLED` ## V1.90 ### script diff --git a/HoymilesZeroExport.py b/HoymilesZeroExport.py index 6cc3ecc..e5e00ea 100644 --- a/HoymilesZeroExport.py +++ b/HoymilesZeroExport.py @@ -339,7 +339,7 @@ def GetHoymilesAvailable(): for i in range(INVERTER_COUNT): try: WasAvail = AVAILABLE[i] - AVAILABLE[i] = DTU.GetAvailable(i) + AVAILABLE[i] = ENABLED[i] and DTU.GetAvailable(i) if AVAILABLE[i]: GetHoymilesAvailable = True if not WasAvail: @@ -1321,6 +1321,7 @@ def CreateDTU() -> DTU: SET_INVERTER_TO_MIN_ON_POWERMETER_ERROR = config.getboolean('COMMON', 'SET_INVERTER_TO_MIN_ON_POWERMETER_ERROR', fallback=False) powermeter_target_point = config.getint('CONTROL', 'POWERMETER_TARGET_POINT') SERIAL_NUMBER = [] +ENABLED = [] NAME = [] TEMPERATURE = [] HOY_MAX_WATT = [] @@ -1341,6 +1342,7 @@ def CreateDTU() -> DTU: HOY_BATTERY_AVERAGE_CNT = [] for i in range(INVERTER_COUNT): SERIAL_NUMBER.append(config.get('INVERTER_' + str(i + 1), 'SERIAL_NUMBER', fallback='')) + ENABLED.append(config.getboolean('INVERTER_' + str(i + 1), 'ENABLED', fallback = True)) NAME.append(str('yet unknown')) TEMPERATURE.append(str('--- degC')) HOY_MAX_WATT.append(config.getint('INVERTER_' + str(i + 1), 'HOY_MAX_WATT')) diff --git a/HoymilesZeroExport_Config.ini b/HoymilesZeroExport_Config.ini index c5ee501..9c8ec55 100644 --- a/HoymilesZeroExport_Config.ini +++ b/HoymilesZeroExport_Config.ini @@ -280,6 +280,8 @@ POWERMETER_MAX_POINT = 0 [INVERTER_1] # serial number of your inverter, if empty it is automatically read out of the API. If you have more than one inverter you should define the serial number here (prevents mix-up). SERIAL_NUMBER = +# enable (true) / disable (false) this inverter +ENABLED = true # manufacturer power rating of your inverter. HOY_INVERTER_WATT = # max. power output of your inverter (e.g. if you have a 1500W Inverter and you only want to output max. 1000W) @@ -321,6 +323,8 @@ HOY_BATTERY_AVERAGE_CNT = 1 [INVERTER_2] # serial number of your inverter, if empty it is automatically read out of the API. If you have more than one inverter you should define the serial number here (prevents mix-up). SERIAL_NUMBER = +# enable (true) / disable (false) this inverter +ENABLED = true # manufacturer power rating of your inverter. HOY_INVERTER_WATT = # max. power output of your inverter (e.g. if you have a 1500W Inverter and you only want to output max. 1000W) @@ -362,6 +366,8 @@ HOY_BATTERY_AVERAGE_CNT = 1 [INVERTER_3] # serial number of your inverter, if empty it is automatically read out of the API. If you have more than one inverter you should define the serial number here (prevents mix-up). SERIAL_NUMBER = +# enable (true) / disable (false) this inverter +ENABLED = true # manufacturer power rating of your inverter. HOY_INVERTER_WATT = # max. power output of your inverter (e.g. if you have a 1500W Inverter and you only want to output max. 1000W) @@ -403,6 +409,8 @@ HOY_BATTERY_AVERAGE_CNT = 1 [INVERTER_4] # serial number of your inverter, if empty it is automatically read out of the API. If you have more than one inverter you should define the serial number here (prevents mix-up). SERIAL_NUMBER = +# enable (true) / disable (false) this inverter +ENABLED = true # manufacturer power rating of your inverter. HOY_INVERTER_WATT = # max. power output of your inverter (e.g. if you have a 1500W Inverter and you only want to output max. 1000W) @@ -444,6 +452,8 @@ HOY_BATTERY_AVERAGE_CNT = 1 [INVERTER_5] # serial number of your inverter, if empty it is automatically read out of the API. If you have more than one inverter you should define the serial number here (prevents mix-up). SERIAL_NUMBER = +# enable (true) / disable (false) this inverter +ENABLED = true # manufacturer power rating of your inverter. HOY_INVERTER_WATT = # max. power output of your inverter (e.g. if you have a 1500W Inverter and you only want to output max. 1000W) @@ -485,6 +495,8 @@ HOY_BATTERY_AVERAGE_CNT = 1 [INVERTER_6] # serial number of your inverter, if empty it is automatically read out of the API. If you have more than one inverter you should define the serial number here (prevents mix-up). SERIAL_NUMBER = +# enable (true) / disable (false) this inverter +ENABLED = true # manufacturer power rating of your inverter. HOY_INVERTER_WATT = # max. power output of your inverter (e.g. if you have a 1500W Inverter and you only want to output max. 1000W) @@ -526,6 +538,8 @@ HOY_BATTERY_AVERAGE_CNT = 1 [INVERTER_7] # serial number of your inverter, if empty it is automatically read out of the API. If you have more than one inverter you should define the serial number here (prevents mix-up). SERIAL_NUMBER = +# enable (true) / disable (false) this inverter +ENABLED = true # manufacturer power rating of your inverter. HOY_INVERTER_WATT = # max. power output of your inverter (e.g. if you have a 1500W Inverter and you only want to output max. 1000W) @@ -567,6 +581,8 @@ HOY_BATTERY_AVERAGE_CNT = 1 [INVERTER_8] # serial number of your inverter, if empty it is automatically read out of the API. If you have more than one inverter you should define the serial number here (prevents mix-up). SERIAL_NUMBER = +# enable (true) / disable (false) this inverter +ENABLED = true # manufacturer power rating of your inverter. HOY_INVERTER_WATT = # max. power output of your inverter (e.g. if you have a 1500W Inverter and you only want to output max. 1000W) @@ -608,6 +624,8 @@ HOY_BATTERY_AVERAGE_CNT = 1 [INVERTER_9] # serial number of your inverter, if empty it is automatically read out of the API. If you have more than one inverter you should define the serial number here (prevents mix-up). SERIAL_NUMBER = +# enable (true) / disable (false) this inverter +ENABLED = true # manufacturer power rating of your inverter. HOY_INVERTER_WATT = # max. power output of your inverter (e.g. if you have a 1500W Inverter and you only want to output max. 1000W) @@ -649,6 +667,8 @@ HOY_BATTERY_AVERAGE_CNT = 1 [INVERTER_10] # serial number of your inverter, if empty it is automatically read out of the API. If you have more than one inverter you should define the serial number here (prevents mix-up). SERIAL_NUMBER = +# enable (true) / disable (false) this inverter +ENABLED = true # manufacturer power rating of your inverter. HOY_INVERTER_WATT = # max. power output of your inverter (e.g. if you have a 1500W Inverter and you only want to output max. 1000W) @@ -690,6 +710,8 @@ HOY_BATTERY_AVERAGE_CNT = 1 [INVERTER_11] # serial number of your inverter, if empty it is automatically read out of the API. If you have more than one inverter you should define the serial number here (prevents mix-up). SERIAL_NUMBER = +# enable (true) / disable (false) this inverter +ENABLED = true # manufacturer power rating of your inverter. HOY_INVERTER_WATT = # max. power output of your inverter (e.g. if you have a 1500W Inverter and you only want to output max. 1000W) @@ -731,6 +753,8 @@ HOY_BATTERY_AVERAGE_CNT = 1 [INVERTER_12] # serial number of your inverter, if empty it is automatically read out of the API. If you have more than one inverter you should define the serial number here (prevents mix-up). SERIAL_NUMBER = +# enable (true) / disable (false) this inverter +ENABLED = true # manufacturer power rating of your inverter. HOY_INVERTER_WATT = # max. power output of your inverter (e.g. if you have a 1500W Inverter and you only want to output max. 1000W) @@ -772,6 +796,8 @@ HOY_BATTERY_AVERAGE_CNT = 1 [INVERTER_13] # serial number of your inverter, if empty it is automatically read out of the API. If you have more than one inverter you should define the serial number here (prevents mix-up). SERIAL_NUMBER = +# enable (true) / disable (false) this inverter +ENABLED = true # manufacturer power rating of your inverter. HOY_INVERTER_WATT = # max. power output of your inverter (e.g. if you have a 1500W Inverter and you only want to output max. 1000W) @@ -813,6 +839,8 @@ HOY_BATTERY_AVERAGE_CNT = 1 [INVERTER_14] # serial number of your inverter, if empty it is automatically read out of the API. If you have more than one inverter you should define the serial number here (prevents mix-up). SERIAL_NUMBER = +# enable (true) / disable (false) this inverter +ENABLED = true # manufacturer power rating of your inverter. HOY_INVERTER_WATT = # max. power output of your inverter (e.g. if you have a 1500W Inverter and you only want to output max. 1000W) @@ -854,6 +882,8 @@ HOY_BATTERY_AVERAGE_CNT = 1 [INVERTER_15] # serial number of your inverter, if empty it is automatically read out of the API. If you have more than one inverter you should define the serial number here (prevents mix-up). SERIAL_NUMBER = +# enable (true) / disable (false) this inverter +ENABLED = true # manufacturer power rating of your inverter. HOY_INVERTER_WATT = # max. power output of your inverter (e.g. if you have a 1500W Inverter and you only want to output max. 1000W) @@ -895,6 +925,8 @@ HOY_BATTERY_AVERAGE_CNT = 1 [INVERTER_16] # serial number of your inverter, if empty it is automatically read out of the API. If you have more than one inverter you should define the serial number here (prevents mix-up). SERIAL_NUMBER = +# enable (true) / disable (false) this inverter +ENABLED = true # manufacturer power rating of your inverter. HOY_INVERTER_WATT = # max. power output of your inverter (e.g. if you have a 1500W Inverter and you only want to output max. 1000W) From fe441e1fa09348f78d139b8eca9e7caca521f0f2 Mon Sep 17 00:00:00 2001 From: reserve85 <111107925+reserve85@users.noreply.github.com> Date: Mon, 22 Apr 2024 06:46:48 +0200 Subject: [PATCH 5/5] emeter-index for ShellyEM add a specific emeter-index for ShellyEM --- CHANGELOG.md | 5 ++++- HoymilesZeroExport.py | 26 ++++++++++++++++---------- HoymilesZeroExport_Config.ini | 4 ++++ 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a125a6c..6a847f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,13 +5,16 @@ * support Home Assistant over HTTPS (https://github.com/reserve85/HoymilesZeroExport/issues/178) * Support login credentials for Tasmota (https://github.com/reserve85/HoymilesZeroExport/issues/159) * update.sh supports custom branch, for example to update to dev path. usage: ./update.sh dev -* option to disable a inverter +* option to disable a inverter (@tester277) +* option to specify a specific emeter-index for ShellyEM [possible values: 0...1]. (https://github.com/reserve85/HoymilesZeroExport/issues/181) ### config * add `[HOMEASSISTANT]`: `HA_HTTPS` * add `[INTERMEDIATE_HOMEASSISTANT]`: `HA_HTTPS_INTERMEDIATE` * add `[TASMOTA]`: `USER` and `PASS` * add `[TASMOTA_INTERMEDIATE]`: `USER_INTERMEDIATE` and `PASS_INTERMEDIATE` * add `[INVERTER_x]`: `ENABLED` +* add `[SHELLY]`: `EMETER_INDEX` +* add `[INTERMEDIATE_SHELLY]`: `EMETER_INDEX` ## V1.90 ### script diff --git a/HoymilesZeroExport.py b/HoymilesZeroExport.py index e5e00ea..4e47f29 100644 --- a/HoymilesZeroExport.py +++ b/HoymilesZeroExport.py @@ -656,10 +656,11 @@ def GetPowermeterWatts(self): return CastToInt(input - ouput) class Shelly(Powermeter): - def __init__(self, ip: str, user: str, password: str): + def __init__(self, ip: str, user: str, password: str, emeterindex: str): self.ip = ip self.user = user self.password = password + self.emeterindex = emeterindex def GetJson(self, path): url = f'http://{self.ip}{path}' @@ -684,7 +685,10 @@ def GetPowermeterWatts(self): class ShellyEM(Shelly): def GetPowermeterWatts(self): - return sum(CastToInt(emeter['power']) for emeter in self.GetJson('/status')['emeters']) + if self.emeterindex: + return CastToInt(self.GetJson(f'/emeter/{self.emeterindex}')['power']) + else: + return sum(CastToInt(emeter['power']) for emeter in self.GetJson('/status')['emeters']) class Shelly3EM(Shelly): def GetPowermeterWatts(self): @@ -1119,12 +1123,13 @@ def CreatePowermeter() -> Powermeter: shelly_ip = config.get('SHELLY', 'SHELLY_IP') shelly_user = config.get('SHELLY', 'SHELLY_USER') shelly_pass = config.get('SHELLY', 'SHELLY_PASS') + shelly_emeterindex = config.get('SHELLY', 'EMETER_INDEX') if config.getboolean('SELECT_POWERMETER', 'USE_SHELLY_EM'): - return ShellyEM(shelly_ip, shelly_user, shelly_pass) + return ShellyEM(shelly_ip, shelly_user, shelly_pass, shelly_emeterindex) elif config.getboolean('SELECT_POWERMETER', 'USE_SHELLY_3EM'): - return Shelly3EM(shelly_ip, shelly_user, shelly_pass) + return Shelly3EM(shelly_ip, shelly_user, shelly_pass, shelly_emeterindex) elif config.getboolean('SELECT_POWERMETER', 'USE_SHELLY_3EM_PRO'): - return Shelly3EMPro(shelly_ip, shelly_user, shelly_pass) + return Shelly3EMPro(shelly_ip, shelly_user, shelly_pass, shelly_emeterindex) elif config.getboolean('SELECT_POWERMETER', 'USE_TASMOTA'): return Tasmota( config.get('TASMOTA', 'TASMOTA_IP'), @@ -1189,6 +1194,7 @@ def CreateIntermediatePowermeter(dtu: DTU) -> Powermeter: shelly_ip = config.get('INTERMEDIATE_SHELLY', 'SHELLY_IP_INTERMEDIATE') shelly_user = config.get('INTERMEDIATE_SHELLY', 'SHELLY_USER_INTERMEDIATE') shelly_pass = config.get('INTERMEDIATE_SHELLY', 'SHELLY_PASS_INTERMEDIATE') + shelly_emeterindex = config.get('INTERMEDIATE_SHELLY', 'EMETER_INDEX') if config.getboolean('SELECT_INTERMEDIATE_METER', 'USE_TASMOTA_INTERMEDIATE'): return Tasmota( config.get('INTERMEDIATE_TASMOTA', 'TASMOTA_IP_INTERMEDIATE'), @@ -1202,15 +1208,15 @@ def CreateIntermediatePowermeter(dtu: DTU) -> Powermeter: config.getboolean('INTERMEDIATE_TASMOTA', 'TASMOTA_JSON_POWER_CALCULATE_INTERMEDIATE', fallback=False) ) elif config.getboolean('SELECT_INTERMEDIATE_METER', 'USE_SHELLY_EM_INTERMEDIATE'): - return ShellyEM(shelly_ip, shelly_user, shelly_pass) + return ShellyEM(shelly_ip, shelly_user, shelly_pass, shelly_emeterindex) elif config.getboolean('SELECT_INTERMEDIATE_METER', 'USE_SHELLY_3EM_INTERMEDIATE'): - return Shelly3EM(shelly_ip, shelly_user, shelly_pass) + return Shelly3EM(shelly_ip, shelly_user, shelly_pass, shelly_emeterindex) elif config.getboolean('SELECT_INTERMEDIATE_METER', 'USE_SHELLY_3EM_PRO_INTERMEDIATE'): - return Shelly3EMPro(shelly_ip, shelly_user, shelly_pass) + return Shelly3EMPro(shelly_ip, shelly_user, shelly_pass, shelly_emeterindex) elif config.getboolean('SELECT_INTERMEDIATE_METER', 'USE_SHELLY_1PM_INTERMEDIATE'): - return Shelly1PM(shelly_ip, shelly_user, shelly_pass) + return Shelly1PM(shelly_ip, shelly_user, shelly_pass, shelly_emeterindex) elif config.getboolean('SELECT_INTERMEDIATE_METER', 'USE_SHELLY_PLUS_1PM_INTERMEDIATE'): - return ShellyPlus1PM(shelly_ip, shelly_user, shelly_pass) + return ShellyPlus1PM(shelly_ip, shelly_user, shelly_pass, shelly_emeterindex) elif config.getboolean('SELECT_INTERMEDIATE_METER', 'USE_ESPHOME_INTERMEDIATE'): return ESPHome( config.get('INTERMEDIATE_ESPHOME', 'ESPHOME_IP_INTERMEDIATE'), diff --git a/HoymilesZeroExport_Config.ini b/HoymilesZeroExport_Config.ini index 9c8ec55..0cf81a1 100644 --- a/HoymilesZeroExport_Config.ini +++ b/HoymilesZeroExport_Config.ini @@ -78,6 +78,8 @@ SHELLY_IP = xxx.xxx.xxx.xxx # if you restricted the web login enter your username and password, for Shelly "Generation 2" devices the username is always "admin" (e.g. Shelly pro 3EM) SHELLY_USER = SHELLY_PASS = +# you can specify a specific emeter-index [possible values: 0...1] (if you have a Shelly-EM). If not defined, totalpower is calculated over all inputs. +EMETER_INDEX = [SHRDZM] # --- defines for SHRDZM Smartmeter Modul --- @@ -170,6 +172,8 @@ SHELLY_IP_INTERMEDIATE = xxx.xxx.xxx.xxx # if you restricted the web login enter your username and password, for Shelly "Generation 2" devices the username is always "admin" (e.g. Shelly pro 3EM) SHELLY_USER_INTERMEDIATE = SHELLY_PASS_INTERMEDIATE = +# you can specify a specific emeter-index [possible values: 0...1] (if you have a Shelly-EM). If not defined, totalpower is calculated over all inputs. +EMETER_INDEX = [INTERMEDIATE_ESPHOME] ESPHOME_IP_INTERMEDIATE = xxx.xxx.xxx.xxx