Compare commits

...

7 Commits
v0.3 ... main

3 changed files with 15 additions and 7 deletions

1
.env
View File

@ -17,5 +17,4 @@ POWER_TARGET_MIN=0 # minimum percentage for the inverter output
POWER_TARGET_MAX=100 # maximum percentage for the inverter output limit, default: 100.0 [%] POWER_TARGET_MAX=100 # maximum percentage for the inverter output limit, default: 100.0 [%]
POWER_DAMPING_FACTOR=0.7 # damping factor for changes of the inverter output limit (between 0-1), default: 0.3 POWER_DAMPING_FACTOR=0.7 # damping factor for changes of the inverter output limit (between 0-1), default: 0.3
POWER_LIMIT_CHANGE_TRESHOLD=0.3 # set a treshold for the api calls: they will not be executed if the new limit isn't that much higher, default: 0.5 POWER_LIMIT_CHANGE_TRESHOLD=0.3 # set a treshold for the api calls: they will not be executed if the new limit isn't that much higher, default: 0.5
POWER_LIMIT_TYPE=1 # the power limit type; DON'T CHANGE if you don't know what you're doing, default: 1 (see https://github.com/tbnobody/OpenDTU/discussions/742)
PYTHONUNBUFFERED=1 # for use in docker images (for fast logs, ...) PYTHONUNBUFFERED=1 # for use in docker images (for fast logs, ...)

View File

@ -1,6 +1,9 @@
# SolarControl # SolarControl
Enforce a zero export (or whatever consumption you like) policy with an OpenDTU-controlled inverter and energy data from MQTT ![Version badge](https://shields.privacynerd.de/badge/Latest-v1.1-green?style=flat-square)
![DockerHub pulls badge](https://shields.privacynerd.de/docker/pulls/bluefox42/solarcontrol?style=flat-square)
Enforce a zero export (or whatever consumption you like) policy with an OpenDTU-controlled inverter and energy data from MQTT.
## Configuring and usage ## Configuring and usage
@ -42,6 +45,7 @@ docker login # login to docker hub
docker buildx create --name buildx-multi-arch docker buildx create --name buildx-multi-arch
docker buildx use buildx-multi-arch docker buildx use buildx-multi-arch
docker buildx build --no-cache --platform linux/amd64,linux/386,linux/arm/v5,linux/arm/v7,linux/arm64/v8,linux/ppc64le,linux/s390x -t bluefox42/solarcontrol:<VERSION> -t bluefox42/solarcontrol:latest . --push docker buildx build --no-cache --platform linux/amd64,linux/386,linux/arm/v5,linux/arm/v7,linux/arm64/v8,linux/ppc64le,linux/s390x -t bluefox42/solarcontrol:<VERSION> -t bluefox42/solarcontrol:latest . --push
docker logout # so that credentials are no longer stored in clear but are deleted
``` ```

View File

@ -8,7 +8,6 @@ from time import time, sleep
import json import json
from threading import Thread from threading import Thread
import requests import requests
import urllib3 # only for exception handling
import math import math
@ -45,9 +44,12 @@ power_target_min = float(os.getenv('POWER_TARGET_MIN', 0))
power_target_max = float(os.getenv('POWER_TARGET_MAX', 100)) power_target_max = float(os.getenv('POWER_TARGET_MAX', 100))
power_damping_factor = float(os.getenv('POWER_DAMPING_FACTOR', 0.3)) power_damping_factor = float(os.getenv('POWER_DAMPING_FACTOR', 0.3))
power_limit_change_treshold = float(os.getenv('POWER_LIMIT_CHANGE_TRESHOLD', 0.5)) power_limit_change_treshold = float(os.getenv('POWER_LIMIT_CHANGE_TRESHOLD', 0.5))
power_limit_type = int(os.getenv('POWER_LIMIT_TYPE', 1))
dry_run = bool(int(os.getenv('DRY_RUN', 1))) dry_run = bool(int(os.getenv('DRY_RUN', 1)))
# set other important variables
power_limit_type = 1 # only set the limit temporary to avoid memory damage; see also: https://github.com/tbnobody/OpenDTU/discussions/742
version = "v1.1" # the version number
# some checks for the correctness of supplied data # some checks for the correctness of supplied data
if power_target_min < 0: power_target_min = 0 if power_target_min < 0: power_target_min = 0
if power_target_max > 100: power_target_max = 100 if power_target_max > 100: power_target_max = 100
@ -55,6 +57,9 @@ if power_damping_factor < 0: power_damping_factor = 0.0
if power_damping_factor > 1: power_damping_factor = 1.0 if power_damping_factor > 1: power_damping_factor = 1.0
print(f"{bcolors.OKCYAN}Welcome to {bcolors.ENDC}{bcolors.OKBLUE}{bcolors.BOLD}SolarControl{bcolors.ENDC}{bcolors.OKCYAN} ({version})!{bcolors.ENDC}")
# create the powers dict (containing the current use) and data variables (for thread sharing) # create the powers dict (containing the current use) and data variables (for thread sharing)
powers_raw = {"solar": 0, "solar_ts": 0, "house": 0, "house_ts": 0} powers_raw = {"solar": 0, "solar_ts": 0, "house": 0, "house_ts": 0}
powers = {"total": None, "total_house": None, "total_solar": None, "timestamp": 0} powers = {"total": None, "total_house": None, "total_solar": None, "timestamp": 0}
@ -106,8 +111,8 @@ def threaded_solar_power_limit_setting():
# Get current openDTU current limit status # Get current openDTU current limit status
try: try:
status = requests.get(opendtu_address.strip("/") + "/api/limit/status", auth=(opendtu_user, opendtu_pwd)).json().copy() status = requests.get(opendtu_address.strip("/") + "/api/limit/status", auth=(opendtu_user, opendtu_pwd)).json().copy()
except (requests.exceptions.RequestException, urllib3.exceptions.HTTPError) as e: except BaseException as e:
print(f"{bcolors.ERROR}Some error occured while trying to reach out to OpenDTU to get latest data about the inverter limit status. Skipping for now.{bcolors.ENDC}\n==== START OF EXCEPTION ====\n{e}\n==== END OF EXCEPTION ====") print(f"{bcolors.FAIL}Some error occured while trying to reach out to OpenDTU to get latest data about the inverter limit status. Skipping for now.{bcolors.ENDC}\n==== START OF EXCEPTION ====\n{e}\n==== END OF EXCEPTION ====")
sleep(0.2) # wait some time (to avaid cpu overload on continuous unavailability of the service) sleep(0.2) # wait some time (to avaid cpu overload on continuous unavailability of the service)
continue # skip this loop pass continue # skip this loop pass
@ -156,7 +161,7 @@ def threaded_solar_power_limit_setting():
else: else:
print(f"{bcolors.WARNING}Now the new limit would be set via the API (but DRY_RUN is either not specified or True): {str(new_limit)}%") print(f"{bcolors.WARNING}Now the new limit would be set via the API (but DRY_RUN is either not specified or True): {str(new_limit)}%")
elif status[opendtu_inverter_sn]["max_power"] == 0: elif status[opendtu_inverter_sn]["max_power"] == 0:
print(f"{bcolors.ERROR}OpenDTU is reporting strange values for the inverter's maximum power output. Skipping it for now.{bcolors.ENDC}") print(f"{bcolors.FAIL}OpenDTU is reporting strange values for the inverter's maximum power output. Skipping it for now.{bcolors.ENDC}")
while (time() - last_time) < limit_update_interval: while (time() - last_time) < limit_update_interval: