Status: Dit is de laatste versie van dit script.
Beschrijving
Python3 script dat de gegevens van de goedkope EP-0118 UPS combineert met de gegevens van je slimme meter om te bepalen wanneer er een stroomstoring is en of je Raspberry Pi gecontroleerd uitgeschakeld moet worden. In geval van een stroomstoring wordt een notificatie uitgestuurd (uiteraard komt deze niet aan, wanneer er door de stroomstoring ook geen internetverbinding meer is). Op basis van de capaciteit van de batterijen in de UPS en het gemiddelde verbruik over de afgelopen 24 uur wordt bepaald wanneer de Raspberry Pi uitgeschakelt moet worden om beschadiging aan bijvoorbeeld de SD-kaart te voorkomen.
LET OP! Onderstaand script gaat uit van de goedkope EP-0118 UPS, die rechtstreeks op de Raspberry Pi gemonteerd kan worden. Deze UPS beschikt over een INA219 chip waardoor het mogelijk is om de uitgaande spanning, stroom en het verbruik uit te lezen. Er zijn meer soortgelijke UPS-bordjes te krijgen, als deze van de zelfde chip gebruik maken, dan zou dit script ook zo maar kunnen werken (ik kan uiteraard geen enkele garantie geven).
Helaas is het niet mogelijk de spanning uit te lezen waarmee de UPS gevoed wordt. Om te bepalen of er sprake is van een stroomstoring, wordt de informatie van de slimme meter uitgelezen uit Domoticz. Je hebt dus wel ook een slimme meter nodig, aangesloten op Domoticz.
Code
# Steenderen.NET
# UPS controle en automatische uitschakeling – Versie 1.0
# Setup variables.
DOMOTICZ_SERVER = "x.x.x.x:yy" # IP-adres:Poort van je Domoticz server
POWER_OUTAGE_IDX = 1 # IDX voor user variable Stroomstoring
MAIN_USAGE_IDX = 2 # IDX voor Smartmeter Verbruik/Wattage
MAIN_VOLTAGE_IDX = 3 # IDX voor Smartmeter Voltage
UPS_VOLTAGE_IDX = 4 # IDX voor Voltage
UPS_CURRENT_IDX = 5 # IDX voor Amperage
UPS_USAGE_IDX = 6 # IDX voor Wattage
BATTERY_CAPACITY = 6800 # Batterij capaciteit in mAh
BATTERY_VOLTAGE = 3.7 # Batterij voltage
OUTAGE_START_MESSAGE = "Er is een stroomstoring geconstateerd, Domoticz draait nu op UPS batterij."
OUTAGE_SHUTDOWN_MESSAGE = "De Raspberry Pi van Domoticz wordt nu afgesloten n.a.v. de langdurige stroomstoring om schade te voorkomen."
OUTAGE_END_MESSAGE = "De stroomstoring is hersteld, Domoticz draait weer normaal."
# No need to edit below.
print("Start running UPS script.")
# Treshold values, under which we measure an power outage, and battery percentage used to calculate run-time on battery.
TRESHOLD_VOLTAGE = 150
TRESHOLD_WATTAGE = 10
BATTERY_PERCENTAGE = 0.75
# Imports.
import http.client
import json
import requests
from datetime import datetime
from ina219 import INA219
from ina219 import DeviceRangeError
SHUNT_OHMS = 0.05
ina = INA219(SHUNT_OHMS)
ina.configure()
# Wake up UPS sensor, as we put it in sleep mode after each run of this script.
print("- Waking up UPS sensor for new read.")
ina.wake()
# Read voltage, current and power from UPS.
print("- Reading voltage, current and power from UPS sensor.")
UPS_VOLTAGE = round(ina.voltage(), 2)
UPS_CURRENT = round(ina.current() / 1000, 2)
UPS_USAGE = round(ina.power() / 1000, 2)
print(" => Voltage: " + str(UPS_VOLTAGE) + " V")
print(" Current: " + str(UPS_CURRENT) + " A")
print(" Usage: " + str(UPS_USAGE) + " W")
# Update voltage, current and power values in Domoticz.
print("- Updating UPS voltage, current and power values in Domoticz.")
request = requests.get('http://' + DOMOTICZ_SERVER + '/json.htm?type=command¶m=udevice&idx=' + str(UPS_VOLTAGE_IDX) + '&nvalue=0&svalue=' + str(UPS_VOLTAGE))
request = requests.get('http://' + DOMOTICZ_SERVER + '/json.htm?type=command¶m=udevice&idx=' + str(UPS_CURRENT_IDX) + '&nvalue=0&svalue=' + str(UPS_CURRENT))
request = requests.get('http://' + DOMOTICZ_SERVER + '/json.htm?type=command¶m=udevice&idx=' + str(UPS_USAGE_IDX) + '&nvalue=0&svalue=' + str(UPS_USAGE))
# Log UPS values to Domoticz
print("- Writing values to log to Domoticz.")
request = requests.get('http://' + DOMOTICZ_SERVER + '/json.htm?type=command¶m=addlogmessage&message=UPS sensor read => Voltage: ' + str(UPS_VOLTAGE) + ' V, Current: ' + str(UPS_CURRENT) + 'A, Usage: ' + str(UPS_USAGE) + ' W')
# Put sensor back to sleep.
print("- Putting UPS sensor in sleep mode again.")
ina.sleep()
# Check if Domoticz is currently in power outage status.
print("- Checking power outage status of Domoticz (0 = power outage mode off, 1 = power outage mode on).")
request = requests.get('http://' + DOMOTICZ_SERVER + '/json.htm?type=command¶m=getuservariable&idx=' + str(POWER_OUTAGE_IDX))
domoticz_data = request.json()
data_items = domoticz_data['result']
for data_item in data_items:
# Read voltage data from 'data field' in JSON file.
power_outage_name = data_item['Name'] # Name of user variable needed, as updating on idx is not possible.
power_outage_status = data_item['Value']
power_outage_since = data_item['LastUpdate']
if power_outage_status == "1":
power_outage_time = datetime.now() - datetime.strptime(power_outage_since, '%Y-%m-%d %H:%M:%S')
power_outage_minutes = round((power_outage_time.days*1440 + power_outage_time.seconds/60))
else:
power_outage_minutes = 0
print(" => Value: " + power_outage_status)
if power_outage_status == "1":
print(" Since: " + str(power_outage_minutes) + " minutes")
# Check current usage and voltage from smartmeter IDXs.
print("- Checking current usage and voltage from smart meter.")
request = requests.get('http://' + DOMOTICZ_SERVER + '/json.htm?type=devices&rid=' + str(MAIN_USAGE_IDX))
domoticz_data = request.json()
data_items = domoticz_data['result']
for data_item in data_items:
# Read usage data from 'Usage field' in JSON file.
usage_data = data_item['Usage']
request = requests.get('http://' + DOMOTICZ_SERVER + '/json.htm?type=devices&rid=' + str(MAIN_VOLTAGE_IDX))
domoticz_data = request.json()
data_items = domoticz_data['result']
for data_item in data_items:
# Read voltage data from 'data field' in JSON file.
voltage_data = data_item['Data']
print(" => Usage: " + usage_data)
print(" Voltage: " + voltage_data)
# Remove possible text (like 'Volt' and 'Watt') from values and convert strings to floats.
current_wattage = float(usage_data.split(" ")[0])
current_voltage = float(voltage_data.split(" ")[0])
# If current wattage is higher than treshold or current voltage is higher than treshold, than no action is needed, otherwise power outage actions are taken.
if current_wattage > TRESHOLD_WATTAGE and current_voltage > TRESHOLD_VOLTAGE:
# If current Domoticz power outage status is enabled then clear status by setting user variable to 0.
if power_outage_status == "1":
print("- No power outage anymore, so clearing Domoticz power outage status.")
request = requests.get('http://' + DOMOTICZ_SERVER + '/json.htm?type=command¶m=updateuservariable&vname=' + power_outage_name + '&vtype=integer&vvalue=0')
print("- Sending notification, if it arrives depends on if internet connection is still working.")
request = requests.get('http://' + DOMOTICZ_SERVER + '/json.htm?type=command¶m=sendnotification&subject=Domoticz power outage&body=' + OUTAGE_END_MESSAGE)
print("- Writing log to Domoticz.")
request = requests.get('http://' + DOMOTICZ_SERVER + '/json.htm?type=command¶m=addlogmessage&message=Power outage is solved, changed user variable ' + power_outage_name + ' back to 0, notifications was send.')
else:
print("- No power outage, so no further action needed.")
# In case of an power outage, check how long this is ongoing, and when it is necessary to initiate a proper shutdown.
else:
print("- WARNING: There is currently a power outage, as usage is 0 Watt, or voltage is lower than " + str(TRESHOLD_VOLTAGE) + " Volt.")
# If current Domoticz power outage status is not yet enabled then set status by setting user variable to 1 and send notification.
if power_outage_status != "1":
print("- Domoticz power outage status is not set, setting it now.")
request = requests.get('http://' + DOMOTICZ_SERVER + '/json.htm?type=command¶m=updateuservariable&vname=' + power_outage_name + '&vtype=integer&vvalue=1')
print("- Sending notification, if it arrives depends on if internet connection is still working.")
request = requests.get('http://' + DOMOTICZ_SERVER + '/json.htm?type=command¶m=sendnotification&subject=Domoticz power outage&body=' + OUTAGE_START_MESSAGE)
print("- Writing log to Domoticz.")
request = requests.get('http://' + DOMOTICZ_SERVER + '/json.htm?type=command¶m=addlogmessage&message=Power outage is resolved, changed user variable ' + power_outage_name + ' to 1, notification was send.')
# Calculate average power consumption over the last 24 hours.
print("- Checking usage of last 24 hours in Domoticz.")
request = requests.get('http://' + DOMOTICZ_SERVER + '/json.htm?type=graph&sensor=counter&idx=' + str(UPS_USAGE_IDX) + '&range=day')
data = request.json()
items = data['result']
avg_power_24h = 0
for item in items:
avg_power_24h += item['u']
avg_power_24h = round(avg_power_24h/len(items), 2)
print(" => Average usage: " + str(avg_power_24h) + " W")
# Calculate battery capacity and expected maximum run-time in minutes based on average user last 24 hours and 75% of battery capacity as safety margin.
print("- Calculating battery capacity and expected run-time based on usages in last 24 hours.")
battery_wh = BATTERY_CAPACITY / 1000 * BATTERY_VOLTAGE
max_run_time = round(battery_wh / avg_power_24h * BATTERY_PERCENTAGE * 60)
print(" => Battery capacity: " + str(battery_wh) + " Wh.")
print(" => Estimated run-time: " + str(max_run_time) + " minutes.")
if power_outage_minutes < max_run_time:
print("- Current outage is not longer then estimated battery run-time, no further action needed.")
else:
print("- Current outage is now longer then estimated battery run-time, shutdown is now initiated.")
print("- Sending notification, if it arrives depends on if internet connection is still working.")
request = requests.get('http://' + DOMOTICZ_SERVER + '/json.htm?type=command¶m=sendnotification&subject=Domoticz power outage&body=' + OUTAGE_SHUTDOWN_MESSAGE)
print("- Writing log to Domoticz.")
request = requests.get('http://' + DOMOTICZ_SERVER + '/json.htm?type=command¶m=addlogmessage&message=Power outage is now longer then estimated battery run-time, shutdown is now initiated.')
request = requests.get('http://' + DOMOTICZ_SERVER + '/json.htm?type=command¶m=system_shutdown')
# Done!
print("Done running UPS script.")
Installatie-instructies
Aanzetten van I2C op je Raspberry Pi
De EP-0118 UPS kan worden uitgelezen via de I2C communicatiebus. In Raspberry PI OS (voorheen Raspbian) staat deze standaard uit. Deze is echter eenvoudig aan te zetten via de Raspberry Pi Software Configuration Tool. Start deze op met het volgende commando.
sudo raspi-config
Binnen deze tool ga je vervolgens naar Interface options en vervolgens kies je hier voor I2C. De vraag of je de I2C interface wilt enablen antwoord je uiteraard Ja of Yes (afhankelijk van de ingestelde taal). Als dat goed is gegaan krijg je de terugkoppeling: The ARM I2C interface is enabled.
PIP installeren
PIP is een package manager uit de hoek van Python. De te installeren Python-bibliotheek kan vanuit hier geinstalleerd worden.
Om te beginnen eerst een algemene update van alle beschikbare packages;
sudo apt-get update
We gebruiken voor dit script Python3, dus installeren we ook de bijbehorende PIP;
sudo apt-get install python3-pip
Het kan voorkomen dat dit niet in één keer werkt, doordat je een oude, niet meer werkende versie geinstalleerd hebt staan. Bij mij hielp het om eerst deze versie te verwijderen met onderstaand commando, om daarna opnieuw te installeren met bovenstaand commando.
sudo apt-get remove python3-pip
pi-ina219 bibliotheek installeren
Voor het uitlezen van de INA 219chip installeren we de pi-ina219 bibliotheek;
sudo pip3 install pi-ina219
Virtuele UPS-sensoren aanmaken in Domoticz
In Domoticz dienen drie virtuele sensoren aangemaakt te worden. Het script vult deze met de waardes uit de UPS-metingen.
Ga in Domoticz naar Instellingen en vervolgens naar Hardware. Maak onder aan de pagina een nieuw item aan met de naam UPS van het type Dummy.
Als je deze hebt aangemaakt kan je in hetzelfde scherm vervolgens op de knop Maak virtuele sensoren klikken. Maak als eerste een sensor aan van het type “Spanning” en geef deze de naam UPS Spanning.
Maak nog een virtuele sensor aan, nu van het type “Ampere (1 Fase)” en geef deze deze naam UPS Stroom.
En tot slot maak je een virtuele sensor aan van het type “Verbruik (Elektra)” en geeft deze de naam UPS Verbruik.
Ga nu naar Instellingen en vervolgens naar Apparaten en zoek in dit scherm de zojuist aangemaakte sensoren op. Deze hebben alle drie een eigen IDX-nummer (eerste kolom in de tabel). Deze IDX-nummers heb je zo nodig om in te vullen in het script.
IDX-nummers opzoeken van je slimme meter
Nu je toch in het Apparaten scherm zit, is het handig om direct de IDX-nummers op te zoeken van je slimme meter. Specifiek heb je de IDX-nummers nodig van je elektriciteitverbruik (Watt) en het voltage (Volt) om zo in te vullen in het script.
Gebruikersvariabele aanmaken in Domoticz
Het script maakt gebruik van een gebruikersvariabele om bij te houden of er sprake is van een stroomstoring of niet. Aan het begin van een stroomstoring wordt de waarde hiervan 1, en zodra deze is opgelost wordt deze weer 0.
Ga in Domoticz naar Instellingen en vervolgens naar Meer opties en dan naar Gebruikersvariabelen. Maak onder aan de pagina een nieuw item aan met de naam Stroomstoring van het type Integer en geeft deze waarde 0.
Zoek in dit scherm vervolgens het IDX-nummer op van de zojuist aangemaakte gebruikersvariabele. Ook deze heb je zo nodig om in te vullen in het script.
Script plaatsen en configureren
Ik hanteer de standaard om alle script t.b.v. Domoticz te plaatsen in de folder /home/pi/domoticz/scripts. Dus ik adviseer je om hetzelfde te doen met dit script, maar verplicht is het natuurlijk niet. Sla het bestand op onder de naam ups.py.
In het begin van het script zijn er een aantal variablen die je zelf dient in te vullen;
Vul bij DOMOTICZ_SERVER op de plaats van x.x.x.x:yy het IP-adres en de gebruikte poort in van Domoticz. Draai je dit script op de Raspberry Pi waarop je ook Domoticz draait en werkt je op de standaard poort van Domoticz, dan kan je hier localhost:8080 invullen.
Bij POWER_OUTAGE_IDX vul je het IDX-nummer in van de gebruikersvariabele Stroomstoring die je eerder hebt aangemaakt.
Vul bij MAIN_USAGE_IDX en MAIN_VOLTAGE_IDX de twee opgezochte IDX-nummers in van je slimme meter.
Bij UPS_VOLTAGE_IDX vul je het IDX-nummer in van de virtuele UPS-sensor UPS Spanning, bij UPS_CURRENT_IDX het IDX-nummer voor de virtuele sensor UPS Stroom en als laatste vul je bij UPS_USAGE_IDX het IDX-nummer is van UPS Verbruik.
Tot slot vullen we de eigenschappen in van de batterijen die je in de UPS hebt geplaatst. Om te beginnen vul je bij BATTERY_CAPACITY de totale batterijcapaciteit in, in mAH. Let daarbij op dat in de EP-0018 UPS twee batterijen passen, dus dat je de capaciteit van beiden batterijen bij elkaar op telt. In mijn geval is dat 2 x 3400 mAH, dus totaal 6800 mAH. Je vult alleen het getal in, mAH laat je weg. Als allerlaatste vul je het voltage van de batterijen in, standaard voor deze UPS is dat 3.7 volt (let daarbij op dat je een punt gebruikt in plaats van een komma, en dat je alleen het getal invult en volt weglaat).
Schedulen van het script
Om voldoende data te verzamelen m.b.t. het verbruik van de Raspberry Pi (en dus een goede inschatting te kunnen maken van hoe lang deze kan doordraaien op de batterijen in geval van een stroomstoring) dient het script vaak genoeg te draaien.
In mijn geval geval draai ik dit script om de 5 minuten. Het inplannen gebeurt uiteraard met een cronjob, middels het volgende commando;
crontab -e
Bij mijzelf laat ik het script 1 keer per 5 minuten draaien. De regel hiervoor in crontab ziet er alsvolgt uit;
*/5 * * * * sudo python3 /home/pi/domoticz/scripts/ups.py
De sudo is in dit geval nodig, aangezien deze rechten nodig zijn voor het eventueel uitschakelen van de Raspberry Pi.
Je zou dit script ook 1 keer per minuut kunnen laten draaien. Dit levert je wellicht een exacter beeld op qua verbruik. Het nadeel is echter een hogere belasting van je Raspberry Pi (bij mijn tests was dit overigens niet merkbaar, maar toch). In het script zit een ruime marge ingebouwd (er wordt standaard met maximaal 75% van de capaciteit van de batterijen gerekend), dus een gedetailleerder beeld van het gemiddelde verbruik voegt niet veel toe.
Testen en wachten
Als je alle stappen goed hebt uitgevoerd, dan ben je klaar. Binnen vijf minuten zou je de eerste resultaten in Domoticz moeten zien verschijnen onder de nieuw aangemaakte virtuele UPS-sensoren.
Mocht je na vijf minuten niets zien, of gewoon willen testen, dan kan je het script handmatig draaien met het volgende commando;
python3 /home/pi/domoticz/scripts/ups.py
Standaard zou het script niet langer dan 5 seconden erover moeten doen en een output zoals dit moeten geven;
Start running UPS script.
- Waking up UPS sensor for new read.
- Reading voltage, current and power from UPS sensor.
=> Voltage: 5.15 V
Current: 0.59 A
Usage: 2.43 W
- Updating UPS voltage, current and power values in Domoticz.
- Writing values to log to Domoticz.
- Putting UPS sensor in sleep mode again.
- Checking power outage status of Domoticz (0 = power outage mode off, 1 = power outage mode on).
=> Value: 0
- Checking current usage and voltage from smart meter.
=> Usage: 209 Watt
Voltage: 228.9 V
- No power outage, so no further action needed.
Done running UPS script.
Mooi stukje om te lezen.
Netjes gemaakt.