6 Commits

3 changed files with 26 additions and 9 deletions

3
.env
View File

@@ -9,7 +9,7 @@ OPENDTU_ADDR=<ADDRESS OF OPENDTU> # address of opendtu (format: http(s)://<IP-
OPENDTU_USER=<OPENDTU USER> # username for opendtu auth OPENDTU_USER=<OPENDTU USER> # username for opendtu auth
OPENDTU_PWD=<OPENDTU PWD> # password for opendtu auth OPENDTU_PWD=<OPENDTU PWD> # password for opendtu auth
OPENDTU_INVERTER_SN=<SERIAL NR> # serial number of the inverter to control OPENDTU_INVERTER_SN=<SERIAL NR> # serial number of the inverter to control
LIMIT_CORRECTION_FACTOR=2 # correction factor for limit setting (e.g.: when only 2 strings of 4 are connected, you always need to set 2x the power), default: 2 LIMIT_CORRECTION_FACTOR=2 # correction factor for limit setting (e.g.: when only 2 strings of 4 are connected, you always need to set 2x the power), default: 1.0
LIMIT_UPDATE_INTERVAL=5 # interval in which the limit shall be updated, default: 5 [sec] LIMIT_UPDATE_INTERVAL=5 # interval in which the limit shall be updated, default: 5 [sec]
DRY_RUN=0 # if the limit shall be set or not; default: 1 (0: False, 1: True) DRY_RUN=0 # if the limit shall be set or not; default: 1 (0: False, 1: True)
POWER_TARGET=15 # the target power consumption of the house, default: 50 [Watts] POWER_TARGET=15 # the target power consumption of the house, default: 50 [Watts]
@@ -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,17 +1,23 @@
# SolarControl # SolarControl
Enforce a zero export (or whatever consumption you like) policy with an OpenDTU-controlled inverter and energy data from MQTT 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
### Docker ### docker-compose
Todo To run it using docker, try the `docker-compose.yaml` file present in this repository. You will need the .env file too for that reasons, so the easiest thing is to just run the following commands:
```bash
git clone https://git.privacynerd.de/BlueFox/SolarControl.git && cd SolarControl
vi .env # adjust the script to your needs
docker-compose up -d && docker-compose logs -f
```
### Bare-bone ### barebone
The script can be configured using the .env file where you can adjust it (hopefully perfect) to your needs. After that, just run the script: The script can be configured using the .env file where you can adjust it (hopefully perfect) to your needs. After that, just run the script:
@@ -22,7 +28,12 @@ python3 lge320reader.py
Please note: the .env file needs to be in the same folder or any other folder higher up in the directory structure as the script (more specifically, the WORKDIR). It is just a help, actually, the script searches for specific variables in its environment variables. It only loads the .env file so that you do not need to `export` all the files before running (see https://pypi.org/project/python-dotenv/ for more details). This also means that when using it in docker, you can set the docker containers environment file to that .env file and it will be accepted too. Please note: the .env file needs to be in the same folder or any other folder higher up in the directory structure as the script (more specifically, the WORKDIR). It is just a help, actually, the script searches for specific variables in its environment variables. It only loads the .env file so that you do not need to `export` all the files before running (see https://pypi.org/project/python-dotenv/ for more details). This also means that when using it in docker, you can set the docker containers environment file to that .env file and it will be accepted too.
## Building docker ## Updating
To update, simply use `git pull` to pull the latest changes. Afterwards, you need to restart your script (with docker, just use `docker-compose up -d --force-recreate`).
## Building the docker image
To build the image for docker, simply use the following commands: To build the image for docker, simply use the following commands:

View File

@@ -45,9 +45,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.0" # 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,13 +58,16 @@ 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}
# define mqtt callbacks # define mqtt callbacks
def on_connect(client, userdata, flags, reason_code, properties): def on_connect(client, userdata, flags, reason_code, properties):
print(f"{bcolors.OKGREEN}Connected with result code {bcolors.ENDC}{reason_code}") print(f"{bcolors.OKGREEN}Connected to the MQTT broker ({mq_broker}:{mq_port}) with result code {bcolors.ENDC}{reason_code}{bcolors.OKGREEN}.{bcolors.ENDC}")
client.subscribe("lge320/#") client.subscribe("lge320/#")
client.subscribe("solar/ac/#") client.subscribe("solar/ac/#")
def on_message(client, userdata, msg): def on_message(client, userdata, msg):
@@ -83,6 +89,7 @@ mqttc.connect(mq_broker, mq_port, 60)
mqttc.loop_start() mqttc.loop_start()
def threaded_current_power_calculation(): def threaded_current_power_calculation():
while True: while True:
last_powers = powers_raw.copy() last_powers = powers_raw.copy()