Dateien hochladen nach „“
This commit is contained in:
parent
142732917e
commit
2f45904da3
|
@ -0,0 +1,16 @@
|
|||
import utime as time
|
||||
|
||||
class TimeUtils():
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def DateTimeNow(self):
|
||||
tm = time.gmtime()
|
||||
return "%02d.%02d.%04d,%02d:%02d" % (tm[2], tm[1], tm[0], tm[3], tm[4])
|
||||
|
||||
def IsOlderThanDays(self, date, days):
|
||||
fdate = time.mktime((int(date[0]), int(date[1]), int(date[2]), 0, 0, 0, 0, 0))
|
||||
if (fdate + (int(days) * 86400) <= time.time()):
|
||||
return True
|
||||
else:
|
||||
return False
|
|
@ -0,0 +1,283 @@
|
|||
import rp2, re, os
|
||||
import TimeUtils, Logger, Networking, NTP
|
||||
import uasyncio as asyncio
|
||||
import utime as time
|
||||
import usocket as socket
|
||||
import urequests as requests
|
||||
from machine import ADC, Pin
|
||||
|
||||
from secrets import secrets
|
||||
from configs import configs
|
||||
|
||||
rp2.country(configs['country'])
|
||||
version = "0.3"
|
||||
|
||||
TimeUtils = TimeUtils.TimeUtils()
|
||||
Logger = Logger.Logger(configs['log_housekeeping_days'])
|
||||
Networking = Networking.Networking(Logger, secrets['ssid'], secrets['pw'])
|
||||
NTP = NTP.NTP(Logger)
|
||||
|
||||
boottime = time.time()
|
||||
tempSensor = ADC(4) # internal temperature sensor
|
||||
s0Pin = Pin(5, Pin.IN, Pin.PULL_UP) # gpio 5
|
||||
temperature = 0.0
|
||||
zaehlerStand = 0 # Wh
|
||||
momentanVerbrauch = 0.000 # W
|
||||
interrupt = False
|
||||
debounce_time = 0
|
||||
secPerImp = 3600.0 / float(configs['ticks_per_kWh'])
|
||||
lastSaved = 0
|
||||
lastTick = 0
|
||||
impulses = 0
|
||||
lastImpTime = 0
|
||||
|
||||
# Handle s0 interrupts
|
||||
def callback(s0Pin):
|
||||
global interrupt, debounce_time
|
||||
if (time.ticks_ms() - debounce_time) > 20:
|
||||
interrupt = True
|
||||
debounce_time = time.ticks_ms()
|
||||
s0Pin.irq(trigger = Pin.IRQ_RISING, handler = callback)
|
||||
|
||||
# Checks if string is integer
|
||||
def IsInt(possibleint):
|
||||
try:
|
||||
int(possibleint)
|
||||
except:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
# Reboots the Pico W (f.e. in case of an error)
|
||||
def Reboot():
|
||||
Logger.LogMessage("Performing Reboot")
|
||||
machine.reset()
|
||||
|
||||
# Calculates the uptime in hours and minutes
|
||||
def Uptime():
|
||||
global boottime
|
||||
seconds = (time.time() - boottime) % (24 * 3600)
|
||||
hours = seconds // 3600
|
||||
seconds %= 3600
|
||||
minutes = seconds // 60
|
||||
seconds %= 60
|
||||
return "%d:%02d" % (hours, minutes)
|
||||
|
||||
# Reads the onboard temperature sensor's value
|
||||
def ReadTemp():
|
||||
global temperature
|
||||
temperature = 27 - ((tempSensor.read_u16() * (3.3 / (65535))) - 0.706) / 0.001721
|
||||
|
||||
# Checks the count of the Powermeter (reboot safe)
|
||||
def CheckPowermeterCount():
|
||||
global zaehlerStand
|
||||
try:
|
||||
file = open("powercount.txt", "r")
|
||||
except:
|
||||
file = open("powercount.txt", "w")
|
||||
file.write("0.000")
|
||||
countFile = float(file.read())
|
||||
file.close()
|
||||
if zaehlerStand < countFile:
|
||||
zaehlerStand = countFile
|
||||
Logger.LogMessage("Powermeter count is " + str(zaehlerStand / 1000) + " kWh.")
|
||||
|
||||
# store zaehlerStand and momentanVerbrauch via API in DB
|
||||
def SendAPI():
|
||||
global lastSaved, zaehlerStand, momentanVerbrauch
|
||||
if ((time.ticks_ms() - lastSaved) > configs['db_api_interval_minutes'] * 60000):
|
||||
lastSaved = time.ticks_ms()
|
||||
response = requests.get(configs['db_api'] + "?action=send&total=" + str(zaehlerStand) + "¤t=" + str(momentanVerbrauch))
|
||||
response.close()
|
||||
|
||||
# calculate current power consumption and increase the total power consuption
|
||||
def PowerMeasurement():
|
||||
global interrupt, secPerImp, lastTick, impulses, lastImpTime
|
||||
if interrupt:
|
||||
#current power consumption (watt)
|
||||
nowTick = time.ticks_ms()
|
||||
timeDiff = nowTick - lastTick
|
||||
#1000 (watt) / (timeDiff in s / secPerImp)
|
||||
momentanVerbrauch = 1000.0 / ((timeDiff / 1000.0) / secPerImp)
|
||||
lastTick = nowTick
|
||||
impulses = impulses + 1
|
||||
#powermeter total count (wh)
|
||||
if (time.ticks_ms() - lastImpTime) > 60000: #every minute
|
||||
nowImpTime = time.ticks_ms()
|
||||
timeImpDiff = nowImpTime - lastImpTime
|
||||
#energy = power times time (in hour)
|
||||
newConsumption = (((1000.0 * impulses) / ((timeImpDiff / 1000.0) / secPerImp)) * (timeImpDiff / 3600000.0))
|
||||
AddZaehlerstand(newConsumption)
|
||||
impulses = 0
|
||||
lastImpTime = nowImpTime
|
||||
interrupt = False
|
||||
|
||||
# get power meter total in kWh
|
||||
def GetZaehlerstand():
|
||||
global zaehlerStand
|
||||
return str((zaehlerStand / 1000))
|
||||
|
||||
# set power meter total in Wh
|
||||
def SetZaehlerstand(setStand):
|
||||
global zaehlerStand
|
||||
zaehlerStand = float(setStand)
|
||||
file = open("powercount.txt", "w")
|
||||
file.write(str(zaehlerStand))
|
||||
file.close()
|
||||
|
||||
# Adds power consumption to power meter total in Wh
|
||||
def AddZaehlerstand(power):
|
||||
global zaehlerStand
|
||||
SetZaehlerstand(zaehlerstand + float(power))
|
||||
|
||||
#####################################################################
|
||||
|
||||
# Helper-method to allow error handling and output in asyncio
|
||||
def set_global_exception():
|
||||
def handle_exception(loop, context):
|
||||
Logger.LogMessage("Fatal error: " + str(context["exception"]))
|
||||
import sys
|
||||
sys.print_exception(context["exception"])
|
||||
sys.exit()
|
||||
Reboot()
|
||||
loop = asyncio.get_event_loop()
|
||||
loop.set_exception_handler(handle_exception)
|
||||
|
||||
# Main method for all the Watering-handling
|
||||
async def PowermeterHandling():
|
||||
Logger.LogMessage("Powermeter handling started")
|
||||
while True:
|
||||
ReadTemp()
|
||||
PowerMeasurement()
|
||||
SendAPI()
|
||||
await asyncio.sleep(0.01)
|
||||
|
||||
# Main method for the API
|
||||
html = """<!DOCTYPE html>
|
||||
<html>
|
||||
<head> <title>Powermeter</title> </head>
|
||||
<body> <h1>Powermeter</h1>
|
||||
<p>%s</p>
|
||||
</body>
|
||||
</html>"""
|
||||
json = """{ "Powermeter": { "%s" } }"""
|
||||
async def APIHandling(reader, writer):
|
||||
request_line = await reader.readline()
|
||||
while await reader.readline() != b"\r\n":
|
||||
pass
|
||||
request = str(request_line)
|
||||
try:
|
||||
request = request.split()[1]
|
||||
except IndexError:
|
||||
pass
|
||||
client_ip = writer.get_extra_info('peername')[0]
|
||||
if request != "/favicon.ico":
|
||||
Logger.LogMessage("API request: " + request + " - from client IP: " + client_ip)
|
||||
if (configs['api_client_ip'] != "") and (configs['api_client_ip'] != client_ip):
|
||||
Logger.LogMessage("Unauthorized client! Aborting API Handling now.")
|
||||
stateis = "<b>Error 401:</b> Client '" + client_ip + "' is not authorized to use the API!<br><br>Set authorized client IP in configs.py!"
|
||||
response = html % stateis
|
||||
writer.write('HTTP/1.0 401 Unauthorized\r\nContent-type: text/html\r\n\r\n')
|
||||
else:
|
||||
req = request.split('/')
|
||||
stateis = ""
|
||||
if (len(req) == 3 or len(req) == 4 or len(req) == 5):
|
||||
if (req[1] == secrets['api']):
|
||||
if (req[2] == "ping"):
|
||||
stateis = "ping: OK"
|
||||
elif (req[2] == "stand"):
|
||||
stateis = "Zaehlerstand: " + GetZaehlerstand() + " kWh"
|
||||
elif (req[2] == "stats"):
|
||||
stateis = "IP address: " + Networking.GetIPAddress() + "<br>MAC address: " + Networking.GetMACAddress() + "<br>Hostname: " + configs['hostname'] + "<br>API Port: " + str(configs['api_port']) + "<br>Uptime (h:m): " + Uptime() + "<br>Date/Time: " + TimeUtils.DateTimeNow() + "<br>Version: " + version + "<br>GMT Timezone Offset (hours): " + str(configs['gmt_offset']) + "<br>Auto summertime: " + str(configs['auto_summertime']) + "<br>Housekeep logfiles after days: " + str(configs['log_housekeeping_days']) + "<br>CPU frequency (MHz): " + str(machine.freq()/1000000) + "<br>Temperature (°C): " + "%.2f" % temperature + "<br>Powermeter count (kWh): " + GetZaehlerstand() + "<br>Interval to store via API in DB (minutes): " + str(configs['db_api_interval_minutes']) + "<br>Ticks per kWh: " + str(configs['ticks_per_kWh'])
|
||||
elif (req[2] == "reboot"):
|
||||
stateis = "Rebooting device: now..."
|
||||
Reboot()
|
||||
elif (req[2] == "set"):
|
||||
if (len(req) == 4):
|
||||
if (IsInt(req[3])):
|
||||
SetZaehlerstand(req[3])
|
||||
stateis = "Zaehlerstand: " + GetZaehlerstand() + " kWh"
|
||||
else:
|
||||
stateis = "<b>Error:</b> Parameter for set not an integer!"
|
||||
else:
|
||||
stateis = "<b>Error:</b> Unknown command!"
|
||||
elif (req[2] == "logs"):
|
||||
if (len(req) >= 4 and req[3] != "json"):
|
||||
if (IsInt(req[3])):
|
||||
stateis = Logger.LastLogs(int(req[3]))
|
||||
else:
|
||||
stateis = "<b>Error:</b> Parameter for log length not an integer!"
|
||||
else:
|
||||
stateis = Logger.LastLogs(10)
|
||||
else:
|
||||
stateis = "<b>Error:</b> Unknown command!"
|
||||
else:
|
||||
stateis = "<b>Error:</b> API key is invalid!"
|
||||
if ((len(req) == 4 and req[3] == "json") or (len(req) == 5 and req[4] == "json")):
|
||||
if (req[2] != "logs"):
|
||||
stateis = stateis.replace(": ", "\":\"")
|
||||
stateis = stateis.replace("<br>", "\", \"")
|
||||
stateis = stateis.replace("°", "°")
|
||||
else:
|
||||
stateis = stateis.replace(";", "\":\"")
|
||||
stateis = stateis.replace("<br>", "\", \"")
|
||||
stateis = stateis.replace("\n", "").replace("\r", "")
|
||||
response = json % stateis
|
||||
writer.write('HTTP/1.0 200 OK\r\nContent-type: text/json\r\n\r\n')
|
||||
else:
|
||||
response = html % stateis
|
||||
writer.write('HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n')
|
||||
else:
|
||||
stateis = "<b>Error 400:</b> Invalid usage of API!<br><br><u>Usage:</u> http://servername/api_key/command[/json]<br><br><u>Commands:</u><ul><li>triggerdoor</li><li>triggerlight</li><li>togglepartymode</li><li>partymodeon</li><li>partymodeoff</li><li>partymodestate</li><li>ping</li><li>stats</li><li>reboot</li><li>ringdoor</li><li>ringfrontdoor</li></ul><br><u>API Key:</u> set 'api' in secrets.py file."
|
||||
response = html % stateis
|
||||
writer.write('HTTP/1.0 400 Bad Request\r\nContent-type: text/html\r\n\r\n')
|
||||
writer.write(response)
|
||||
await writer.drain()
|
||||
await writer.wait_closed()
|
||||
|
||||
# Main method for daily housekeeping
|
||||
async def Housekeeper():
|
||||
Logger.LogMessage("Housekeeper started")
|
||||
while True:
|
||||
Logger.LogMessage("Housekeeper is performing actions")
|
||||
Logger.Housekeeping()
|
||||
Logger.LogMessage("Housekeeper is performing NTP sync")
|
||||
NTP.SetRTCTimeFromNTP(configs['ntp_host'], configs['gmt_offset'], configs['auto_summertime'])
|
||||
Logger.LogMessage("Housekeeper has finished its jobs")
|
||||
await asyncio.sleep(86400)
|
||||
|
||||
# Main entry point after booting
|
||||
async def Main():
|
||||
global autoState
|
||||
set_global_exception()
|
||||
Logger.LogMessage("Entering MainLoop")
|
||||
boottime = time.time()
|
||||
loop = asyncio.get_event_loop()
|
||||
Logger.LogMessage("Setting up API on port " + str(configs['api_port']) + " with key " + secrets['api'])
|
||||
loop.create_task(asyncio.start_server(APIHandling, Networking.GetIPAddress(), configs['api_port']))
|
||||
Logger.LogMessage("API started")
|
||||
Logger.LogMessage("Booting complete with Firmware " + version)
|
||||
CheckPowermeterCount()
|
||||
loop.create_task(PowermeterHandling())
|
||||
loop.create_task(Housekeeper())
|
||||
loop.run_forever()
|
||||
|
||||
# Booting the device
|
||||
def Boot():
|
||||
Networking.Connect(configs['disable_wifi_powersavingmode'])
|
||||
if (Networking.Status()):
|
||||
if (NTP.SetRTCTimeFromNTP(configs['ntp_host'], configs['gmt_offset'], configs['auto_summertime'])):
|
||||
Logger.DisableTempLogfile()
|
||||
else:
|
||||
time.sleep(3)
|
||||
Reboot()
|
||||
|
||||
#####################################################################
|
||||
|
||||
Boot()
|
||||
try:
|
||||
asyncio.run(Main())
|
||||
except KeyboardInterrupt:
|
||||
Logger.LogMessage("Shutdown.")
|
||||
finally:
|
||||
asyncio.new_event_loop()
|
|
@ -0,0 +1,5 @@
|
|||
secrets = {
|
||||
'ssid': 'your-wifi-name',
|
||||
'pw': 'your-wifi-password',
|
||||
'api': 'test',
|
||||
}
|
Loading…
Reference in New Issue