This commit is contained in:
Manuel Kamper 2023-01-07 17:56:55 +00:00
parent 578865daf6
commit 9d26fe5abe
3 changed files with 551 additions and 553 deletions

View File

@ -1,46 +1,44 @@
import machine import machine
import uos as os import os
import TimeUtils import TimeUtils
class Logger(): class Logger():
def __init__(self, hk_days): def __init__(self, hk_days):
self.hk_days = hk_days self.hk_days = hk_days
self.TimeUtils = TimeUtils.TimeUtils() self.TimeUtils = TimeUtils.TimeUtils()
if not "logs" in os.listdir(): if not "logs" in os.listdir():
os.mkdir("/logs") os.mkdir("/logs")
def LogMessage(self, message): def LogMessage(self, message):
print(message) print(message)
dt = machine.RTC().datetime() dt = machine.RTC().datetime()
file = open(("/logs/%04d-%02d-%02d.txt" % (dt[0], dt[1], dt[2])), "a") file = open(("/logs/%04d-%02d-%02d.txt" % (dt[0], dt[1], dt[2])), "a")
file.write(self.TimeUtils.DateTimeNow() + ";" + message + "\n") file.write(self.TimeUtils.DateTimeNow() + ";" + message + "\n")
file.close() file.close()
def Housekeeping(self): def Housekeeping(self):
for file in os.listdir("/logs"): for file in os.listdir("/logs"):
if (file.endswith(".txt")): if (file.endswith(".txt")):
fd = file.split('.')[0].split('-') fd = file.split('.')[0].split('-')
if (self.TimeUtils.IsOlderThanDays(fd, self.hk_days)): if (self.TimeUtils.IsOlderThanDays(fd, self.hk_days)):
os.remove("/logs/" + file) os.remove("/logs/" + file)
self.LogMessage("Housekeeping: deleted logfile " + file) self.LogMessage("Housekeeping: deleted logfile " + file)
def LastLogs(self, lines): def LastLogs(self, lines):
dt = machine.RTC().datetime() dt = machine.RTC().datetime()
logfilename = "/logs/%04d-%02d-%02d.txt" % (dt[0], dt[1], dt[2]) logfilename = ("/logs/%04d-%02d-%02d.txt" % (dt[0], dt[1], dt[2]))
bufsize = 4096 bufsize = 4096
fsize = os.stat(logfilename).st_size print(os.stat(logfilename)[6])
iter = 0 fsize = os.stat(logfilename)[6]
logfile = open(logfilename, "r") iter = 0
#lines = file.readlines() with open(logfilename, "r") as logfile:
if bufsize > fsize: fetched_lines = []
bufsize = fsize - 1 if bufsize > fsize:
fetched_lines = [] bufsize = fsize - 1
while True: while True:
iter += 1 iter += 1
logfile.seek(fsize-bufsize * iter) logfile.seek(fsize - bufsize * iter)
fetched_lines.extend(logfile.readlines()) fetched_lines.extend(logfile.readlines())
if len(fetched_lines) >= lines or logfile.tell() == 0: if len(fetched_lines) >= lines or logfile.tell() == 0:
#print(''.join(fetched_lines[-N:])) #todo if lines from todays logfile are less than the number of requested lines, read missing lines from yesterdays logfile
break return "<br>".join(fetched_lines[-lines:])
file.close()
return "<br>".join(fetched_lines[-lines:])

View File

@ -1,80 +1,80 @@
import network import network
import ubinascii import ubinascii
import Logger import Logger
import machine import machine
import time import time
wlan = network.WLAN(network.STA_IF) wlan = network.WLAN(network.STA_IF)
mac = ubinascii.hexlify(network.WLAN().config('mac'),':').decode() mac = ubinascii.hexlify(network.WLAN().config('mac'),':').decode()
led = machine.Pin('LED', machine.Pin.OUT) led = machine.Pin('LED', machine.Pin.OUT)
class Networking(): class Networking():
def __init__(self, Logger, ssid, pw): def __init__(self, Logger, ssid, pw):
self.ssid = ssid self.ssid = ssid
self.pw = pw self.pw = pw
self.Logger = Logger self.Logger = Logger
def Connect(self, disable_wifi_powersavingmode): def Connect(self, disable_wifi_powersavingmode):
wlan.active(True) wlan.active(True)
#wlan.config(hostname=configs['hostname']) #wlan.config(hostname=configs['hostname'])
if(disable_wifi_powersavingmode): if(disable_wifi_powersavingmode):
wlan.config(pm = 0xa11140) wlan.config(pm = 0xa11140)
self.Logger.LogMessage("mac = " + str(mac)) self.Logger.LogMessage("mac = " + str(mac))
self.Logger.LogMessage("channel = " + str(wlan.config('channel'))) self.Logger.LogMessage("channel = " + str(wlan.config('channel')))
self.Logger.LogMessage("essid = " + str(wlan.config('essid'))) self.Logger.LogMessage("essid = " + str(wlan.config('essid')))
self.Logger.LogMessage("txpower = " + str(wlan.config('txpower'))) self.Logger.LogMessage("txpower = " + str(wlan.config('txpower')))
wlan.connect(self.ssid, self.pw) wlan.connect(self.ssid, self.pw)
timeout = 30 timeout = 30
while timeout > 0: while timeout > 0:
if wlan.status() < 0 or wlan.status() >= 3: if wlan.status() < 0 or wlan.status() >= 3:
break break
timeout -= 1 timeout -= 1
self.Logger.LogMessage('Waiting for connection...') self.Logger.LogMessage('Waiting for connection...')
time.sleep(1) time.sleep(1)
# Wlan Status on LED # Wlan Status on LED
# Handle connection error # Handle connection error
# Error meanings # Error meanings
# 0 Link Down # 0 Link Down
# 1 Link Join # 1 Link Join
# 2 Link NoIp # 2 Link NoIp
# 3 Link Up # 3 Link Up
# -1 Link Fail # -1 Link Fail
# -2 Link NoNet # -2 Link NoNet
# -3 Link BadAuth # -3 Link BadAuth
def Status(self): def Status(self):
global wlan global wlan
wlan_status = wlan.status() wlan_status = wlan.status()
self.BlinkOnboardLED(wlan_status) self.BlinkOnboardLED(wlan_status)
if wlan_status != 3: if wlan_status != 3:
#raise RuntimeError('Wi-Fi connection failed') #raise RuntimeError('Wi-Fi connection failed')
return False return False
else: else:
self.Logger.LogMessage('Connected') self.Logger.LogMessage('Connected')
status = wlan.ifconfig() status = wlan.ifconfig()
self.Logger.LogMessage('ip = ' + status[0]) self.Logger.LogMessage('ip = ' + status[0])
return True return True
# Blinkfunktion der Onboard-LED für Errorcodes # Blinkfunktion der Onboard-LED für Errorcodes
def BlinkOnboardLED(self, num_blinks): def BlinkOnboardLED(self, num_blinks):
global led global led
for i in range(num_blinks): for i in range(num_blinks):
led.on() led.on()
time.sleep(.2) time.sleep(.2)
led.off() led.off()
time.sleep(.2) time.sleep(.2)
def GetMACAddress(self): def GetMACAddress(self):
global mac global mac
return mac return mac
def GetIPAddress(self): def GetIPAddress(self):
global wlan global wlan
return wlan.ifconfig()[0] return wlan.ifconfig()[0]
def IsWifiConnected(self): def IsWifiConnected(self):
global wlan global wlan
if wlan.status() == 3: if wlan.status() == 3:
return "W" return "W"
else: else:
return " " return " "

856
main.py
View File

@ -1,429 +1,429 @@
import rp2 import rp2
import TimeUtils import TimeUtils
import Logger import Logger
import Oled import Oled
import Networking import Networking
import NTP import NTP
import uasyncio as asyncio import uasyncio as asyncio
import utime as time import utime as time
import Keypad import Keypad
import usocket as socket import usocket as socket
from machine import ADC, Pin from machine import ADC, Pin
from secrets import secrets from secrets import secrets
from configs import configs from configs import configs
rp2.country(configs['country']) rp2.country(configs['country'])
version = "0.8-beta" version = "0.8-beta"
Oled = Oled.Oled() Oled = Oled.Oled()
TimeUtils = TimeUtils.TimeUtils() TimeUtils = TimeUtils.TimeUtils()
Logger = Logger.Logger(configs['log_housekeeping_days']) Logger = Logger.Logger(configs['log_housekeeping_days'])
Networking = Networking.Networking(Logger, secrets['ssid'], secrets['pw']) Networking = Networking.Networking(Logger, secrets['ssid'], secrets['pw'])
NTP = NTP.NTP(Logger) NTP = NTP.NTP(Logger)
Keypad = Keypad.Keypad() Keypad = Keypad.Keypad()
boottime = time.time() boottime = time.time()
lastActionTicks = 0 lastActionTicks = 0
subscreen = 0 subscreen = 0
sc = 0 sc = 0
partyMode = False partyMode = False
displayOff = False displayOff = False
busline = ADC(28) busline = ADC(28)
triggerline = Pin(15, Pin.OUT) triggerline = Pin(15, Pin.OUT)
writerActive = False writerActive = False
# Reboots the Pico W (f.e. in case of an error) # Reboots the Pico W (f.e. in case of an error)
def Reboot(): def Reboot():
Logger.LogMessage("Performing Reboot") Logger.LogMessage("Performing Reboot")
machine.reset() machine.reset()
# Calculates the uptime in hours and minutes # Calculates the uptime in hours and minutes
def Uptime(): def Uptime():
global boottime global boottime
seconds = (time.time() - boottime) % (24 * 3600) seconds = (time.time() - boottime) % (24 * 3600)
hours = seconds // 3600 hours = seconds // 3600
seconds %= 3600 seconds %= 3600
minutes = seconds // 60 minutes = seconds // 60
seconds %= 60 seconds %= 60
return "%d:%02d" % (hours, minutes) return "%d:%02d" % (hours, minutes)
# Shows text on the display # Shows text on the display
def ShowText(line1, line2, line3): def ShowText(line1, line2, line3):
Oled.fill(0x0000) Oled.fill(0x0000)
Oled.text(line1,1,2,Oled.white) Oled.text(line1,1,2,Oled.white)
Oled.text(line2,1,12,Oled.white) Oled.text(line2,1,12,Oled.white)
Oled.text(line3,1,22,Oled.white) Oled.text(line3,1,22,Oled.white)
Oled.show() Oled.show()
# Main method for display output # Main method for display output
heartbeat = "H" heartbeat = "H"
def BuildScreen(): def BuildScreen():
global subscreen, partyMode, lastActionTicks, displayOff, heartbeat global subscreen, partyMode, lastActionTicks, displayOff, heartbeat
if heartbeat == "H": if heartbeat == "H":
heartbeat = " " heartbeat = " "
else: else:
heartbeat = "H" heartbeat = "H"
lastActionTicks += 1 lastActionTicks += 1
# after 10 Seconds of no activity, go back to main screen # after 10 Seconds of no activity, go back to main screen
if (lastActionTicks >= 20): if (lastActionTicks >= 20):
subscreen = 0 subscreen = 0
# after X Minutes turn off the display # after X Minutes turn off the display
if (lastActionTicks >= (configs['displayoff'] * 60 * 2)): if (lastActionTicks >= (configs['displayoff'] * 60 * 2)):
displayOff = True displayOff = True
ShowText("","","") ShowText("","","")
else: else:
if (subscreen == 0): if (subscreen == 0):
ShowText("TCS<->FHEM " + PartyModeActive() + " " + Networking.IsWifiConnected() + " " + heartbeat + "", TimeUtils.DateTimeNow(), "Auf LiG PaM Chk") ShowText("TCS<->FHEM " + PartyModeActive() + " " + Networking.IsWifiConnected() + " " + heartbeat + "", TimeUtils.DateTimeNow(), "Auf LiG PaM Chk")
elif (subscreen == 1): elif (subscreen == 1):
ShowText("Eingangstuer", "getriggert", " Ext") ShowText("Eingangstuer", "getriggert", " Ext")
elif (subscreen == 2): elif (subscreen == 2):
ShowText("Licht im Gang", "getriggert", " Ext") ShowText("Licht im Gang", "getriggert", " Ext")
elif (subscreen == 3): elif (subscreen == 3):
ShowText("Party-Mode", ("aktiviert" if partyMode else "deaktiviert"), " Ext") ShowText("Party-Mode", ("aktiviert" if partyMode else "deaktiviert"), " Ext")
elif (subscreen == 4): elif (subscreen == 4):
ShowText("Hostname:", configs['hostname'], "Up Dwn Ext") ShowText("Hostname:", configs['hostname'], "Up Dwn Ext")
elif (subscreen == 5): elif (subscreen == 5):
ShowText("MAC Address:", Networking.GetMACAddress(), "Up Dwn Ext") ShowText("MAC Address:", Networking.GetMACAddress(), "Up Dwn Ext")
elif (subscreen == 6): elif (subscreen == 6):
ShowText("IP Address:", Networking.GetIPAddress(), "Up Dwn Ext") ShowText("IP Address:", Networking.GetIPAddress(), "Up Dwn Ext")
elif (subscreen == 7): elif (subscreen == 7):
ShowText("API key:", secrets['api'], "Up Dwn Ext") ShowText("API key:", secrets['api'], "Up Dwn Ext")
elif (subscreen == 8): elif (subscreen == 8):
ShowText("CPU Frequency:", str(machine.freq()/1000000) + " MHz", "Up Dwn Ext") ShowText("CPU Frequency:", str(machine.freq()/1000000) + " MHz", "Up Dwn Ext")
elif (subscreen == 9): elif (subscreen == 9):
ShowText("Firmware vers.:", version, "Up Dwn Ext") ShowText("Firmware vers.:", version, "Up Dwn Ext")
elif (subscreen == 10): elif (subscreen == 10):
ShowText("Uptime (H:m):", Uptime(), "Up Dwn Ext") ShowText("Uptime (H:m):", Uptime(), "Up Dwn Ext")
elif (subscreen == 11): elif (subscreen == 11):
ShowText("Perform", "reboot now", "Up Dwn OK Ext") ShowText("Perform", "reboot now", "Up Dwn OK Ext")
else: else:
ShowText("Error","Invalid Screen", " Ext") ShowText("Error","Invalid Screen", " Ext")
# Helper-method for the systemcheck-output on the display # Helper-method for the systemcheck-output on the display
def ShowSystemCheck(screen): def ShowSystemCheck(screen):
global subscreen, sc global subscreen, sc
if (screen == "start"): if (screen == "start"):
Logger.LogMessage("Showing Systemcheck") Logger.LogMessage("Showing Systemcheck")
sc = 0 sc = 0
elif (screen == "next"): elif (screen == "next"):
sc = sc + 1 sc = sc + 1
else: else:
sc = sc - 1 sc = sc - 1
if (sc > 7): if (sc > 7):
sc = 0 sc = 0
elif (sc < 0): elif (sc < 0):
sc = 7 sc = 7
subscreen = sc + 4 subscreen = sc + 4
# Activates/deactivates the party-mode # Activates/deactivates the party-mode
def TogglePartyMode(): def TogglePartyMode():
global subscreen, partyMode global subscreen, partyMode
if (partyMode): if (partyMode):
partyMode = False partyMode = False
Logger.LogMessage("Party-Mode off") Logger.LogMessage("Party-Mode off")
else: else:
partyMode = True partyMode = True
Logger.LogMessage("Party-Mode on") Logger.LogMessage("Party-Mode on")
subscreen = 3 subscreen = 3
# Sets the party-mode active/inactive # Sets the party-mode active/inactive
def SetPartyMode(newValue): def SetPartyMode(newValue):
global partyMode global partyMode
partyMode = newValue partyMode = newValue
Logger.LogMessage("Setting Party-Mode via API to " + PartyModeState()) Logger.LogMessage("Setting Party-Mode via API to " + PartyModeState())
# Returns status of party-mode # Returns status of party-mode
def PartyModeState(): def PartyModeState():
global partyMode global partyMode
if (partyMode): if (partyMode):
return "enabled" return "enabled"
else: else:
return "disabled" return "disabled"
# Returns status symbol if party-mode is active # Returns status symbol if party-mode is active
def PartyModeActive(): def PartyModeActive():
global PartyMode global PartyMode
if (partyMode): if (partyMode):
return "P" return "P"
else: else:
return " " return " "
##################################################################### #####################################################################
# Helper-method to allow error handling and output in asyncio # Helper-method to allow error handling and output in asyncio
def set_global_exception(): def set_global_exception():
def handle_exception(loop, context): def handle_exception(loop, context):
Logger.LogMessage("Fatal error: " + str(context["exception"])) Logger.LogMessage("Fatal error: " + str(context["exception"]))
import sys import sys
sys.print_exception(context["exception"]) sys.print_exception(context["exception"])
sys.exit() sys.exit()
Reboot() Reboot()
loop = asyncio.get_event_loop() loop = asyncio.get_event_loop()
loop.set_exception_handler(handle_exception) loop.set_exception_handler(handle_exception)
# Main method for all the UI-handling # Main method for all the UI-handling
async def UiHandling(): async def UiHandling():
global interrupt_flag, lastActionTicks, displayOff, subscreen global interrupt_flag, lastActionTicks, displayOff, subscreen
Logger.LogMessage("UI handling started") Logger.LogMessage("UI handling started")
while True: while True:
BuildScreen() BuildScreen()
if Keypad.interrupt_flag is 1: if Keypad.interrupt_flag is 1:
Keypad.interrupt_flag = 0 Keypad.interrupt_flag = 0
lastActionTicks = 0 lastActionTicks = 0
if (displayOff): if (displayOff):
displayOff = False displayOff = False
else: else:
Keypad.interrupt_flag = 0 Keypad.interrupt_flag = 0
if (subscreen == 0): if (subscreen == 0):
if (Keypad.tastePressed == Keypad.taste1): if (Keypad.tastePressed == Keypad.taste1):
ShowSystemCheck("start") ShowSystemCheck("start")
elif (Keypad.tastePressed == Keypad.taste2): elif (Keypad.tastePressed == Keypad.taste2):
TogglePartyMode() TogglePartyMode()
elif (Keypad.tastePressed == Keypad.taste3): elif (Keypad.tastePressed == Keypad.taste3):
subscreen = 2 subscreen = 2
Logger.LogMessage("Triggering Licht") Logger.LogMessage("Triggering Licht")
await TCSBusWriter(configs['light_trigger_message']) await TCSBusWriter(configs['light_trigger_message'])
elif (Keypad.tastePressed == Keypad.taste4): elif (Keypad.tastePressed == Keypad.taste4):
subscreen = 1 subscreen = 1
Logger.LogMessage("Triggering Door") Logger.LogMessage("Triggering Door")
await TCSBusWriter(configs['door_trigger_message']) await TCSBusWriter(configs['door_trigger_message'])
else: else:
Logger.LogMessage("Error: Invalid Button pressed!") Logger.LogMessage("Error: Invalid Button pressed!")
elif (subscreen == 1 or subscreen == 2 or subscreen == 3): elif (subscreen == 1 or subscreen == 2 or subscreen == 3):
if (Keypad.tastePressed == Keypad.taste1): if (Keypad.tastePressed == Keypad.taste1):
subscreen = 0 subscreen = 0
elif (subscreen == 4 or subscreen == 5 or subscreen == 6 or subscreen == 7 or subscreen == 8 or subscreen == 9 or subscreen == 10 or subscreen == 11): elif (subscreen == 4 or subscreen == 5 or subscreen == 6 or subscreen == 7 or subscreen == 8 or subscreen == 9 or subscreen == 10 or subscreen == 11):
if (Keypad.tastePressed == Keypad.taste1): if (Keypad.tastePressed == Keypad.taste1):
subscreen = 0 subscreen = 0
elif (subscreen == 11 and Keypad.tastePressed == Keypad.taste2): elif (subscreen == 11 and Keypad.tastePressed == Keypad.taste2):
Reboot() Reboot()
elif (Keypad.tastePressed == Keypad.taste3): elif (Keypad.tastePressed == Keypad.taste3):
ShowSystemCheck("next") ShowSystemCheck("next")
elif (Keypad.tastePressed == Keypad.taste4): elif (Keypad.tastePressed == Keypad.taste4):
ShowSystemCheck("prev") ShowSystemCheck("prev")
else: else:
Logger.LogMessage("Error: Invalid Button pressed!") Logger.LogMessage("Error: Invalid Button pressed!")
else: else:
if (Keypad.tastePressed == Keypad.taste1): if (Keypad.tastePressed == Keypad.taste1):
subscreen = 0 subscreen = 0
await asyncio.sleep(0.5) await asyncio.sleep(0.5)
# Main method for the API # Main method for the API
html = """<!DOCTYPE html> html = """<!DOCTYPE html>
<html> <html>
<head> <title>TCS<->FHEM</title> </head> <head> <title>TCS<->FHEM</title> </head>
<body> <h1>TCS<->FHEM</h1> <body> <h1>TCS<->FHEM</h1>
<p>%s</p> <p>%s</p>
</body> </body>
</html>""" </html>"""
json = """{ "TCS<->FHEM API":"%s" }""" json = """{ "TCS<->FHEM API":"%s" }"""
async def APIHandling(reader, writer): async def APIHandling(reader, writer):
request_line = await reader.readline() request_line = await reader.readline()
while await reader.readline() != b"\r\n": while await reader.readline() != b"\r\n":
pass pass
request = str(request_line) request = str(request_line)
try: try:
request = request.split()[1] request = request.split()[1]
except IndexError: except IndexError:
pass pass
client_ip = writer.get_extra_info('peername')[0] client_ip = writer.get_extra_info('peername')[0]
Logger.LogMessage("API request: " + request + " - from client IP: " + client_ip) Logger.LogMessage("API request: " + request + " - from client IP: " + client_ip)
if (configs['api_client_ip'] != "") and (configs['api_client_ip'] != client_ip): if (configs['api_client_ip'] != "") and (configs['api_client_ip'] != client_ip):
Logger.LogMessage("Unauthorized client! Aborting API Handling now.") 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!" 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 response = html % stateis
writer.write('HTTP/1.0 401 Unauthorized\r\nContent-type: text/html\r\n\r\n') writer.write('HTTP/1.0 401 Unauthorized\r\nContent-type: text/html\r\n\r\n')
else: else:
req = request.split('/') req = request.split('/')
stateis = "" stateis = ""
if (len(req) == 3 or len(req) == 4): if (len(req) == 3 or len(req) == 4):
if (req[1] == secrets['api']): if (req[1] == secrets['api']):
if (req[2] == "triggerdoor"): if (req[2] == "triggerdoor"):
Logger.LogMessage("Triggering Door") Logger.LogMessage("Triggering Door")
await TCSBusWriter(configs['door_trigger_message']) await TCSBusWriter(configs['door_trigger_message'])
stateis = "Triggered front door opener" stateis = "Triggered front door opener"
elif (req[2] == "triggerlight"): elif (req[2] == "triggerlight"):
Logger.LogMessage("Triggering Licht") Logger.LogMessage("Triggering Licht")
await TCSBusWriter(configs['light_trigger_message']) await TCSBusWriter(configs['light_trigger_message'])
stateis = "Triggered light" stateis = "Triggered light"
elif (req[2] == "togglepartymode"): elif (req[2] == "togglepartymode"):
TogglePartyMode() TogglePartyMode()
stateis = "Toggled Party-Mode" stateis = "Toggled Party-Mode"
elif (req[2] == "partymodeon"): elif (req[2] == "partymodeon"):
SetPartyMode(True) SetPartyMode(True)
stateis = "Enabled Party-Mode" stateis = "Enabled Party-Mode"
elif (req[2] == "partymodeoff"): elif (req[2] == "partymodeoff"):
SetPartyMode(False) SetPartyMode(False)
stateis = "Disabled Party-Mode" stateis = "Disabled Party-Mode"
elif (req[2] == "partymodestate"): elif (req[2] == "partymodestate"):
stateis = "Party-Mode is " + PartyModeState() stateis = "Party-Mode is " + PartyModeState()
elif (req[2] == "ping"): elif (req[2] == "ping"):
stateis = "OK" stateis = "OK"
elif (req[2] == "stats"): 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>Display off time (mins): " + str(configs['displayoff']) + "<br>Log incoming bus messages: " + str(configs['log_incoming_bus_messages']) + "<br>Housekeep logfiles after days: " + str(configs['log_housekeeping_days']) + "<br>Message 'Front door ringing': " + str(configs['frontdoor_ringing_message']) + "<br>Message 'Door ringing': " + str(configs['door_ringing_message']) + "<br>Message 'Door opener triggered': " + str(configs['door_trigger_message']) + "<br>Message 'Light triggered': " + str(configs['light_trigger_message']) + "<br>CPU frequency (MHz): " + str(machine.freq()/1000000) 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>Display off time (mins): " + str(configs['displayoff']) + "<br>Log incoming bus messages: " + str(configs['log_incoming_bus_messages']) + "<br>Housekeep logfiles after days: " + str(configs['log_housekeeping_days']) + "<br>Message 'Front door ringing': " + str(configs['frontdoor_ringing_message']) + "<br>Message 'Door ringing': " + str(configs['door_ringing_message']) + "<br>Message 'Door opener triggered': " + str(configs['door_trigger_message']) + "<br>Message 'Light triggered': " + str(configs['light_trigger_message']) + "<br>CPU frequency (MHz): " + str(machine.freq()/1000000)
elif (req[2] == "reboot"): elif (req[2] == "reboot"):
stateis = "Rebooting device now..." stateis = "Rebooting device now..."
Reboot() Reboot()
elif (req[2] == "ringdoor"): elif (req[2] == "ringdoor"):
stateis = "Ringing doorbell" stateis = "Ringing doorbell"
await TCSBusWriter(configs['door_ringing_message']) await TCSBusWriter(configs['door_ringing_message'])
elif (req[2] == "ringfrontdoor"): elif (req[2] == "ringfrontdoor"):
stateis = "Ringing front doorbell" stateis = "Ringing front doorbell"
await TCSBusWriter(configs['frontdoor_ringing_message']) await TCSBusWriter(configs['frontdoor_ringing_message'])
elif (req[2] == "logs"): elif (req[2] == "logs"):
stateis = Logger.LastLogs() stateis = Logger.LastLogs(50)
else: else:
stateis = "<b>Error:</b> Unknown command!" stateis = "<b>Error:</b> Unknown command!"
else: else:
stateis = "<b>Error:</b> API key is invalid!" stateis = "<b>Error:</b> API key is invalid!"
if (len(req) == 4 and req[3] == "json"): if (len(req) == 4 and req[3] == "json"):
response = json % stateis response = json % stateis
writer.write('HTTP/1.0 200 OK\r\nContent-type: text/json\r\n\r\n') writer.write('HTTP/1.0 200 OK\r\nContent-type: text/json\r\n\r\n')
else: else:
response = html % stateis response = html % stateis
writer.write('HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n') writer.write('HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n')
else: 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." 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 response = html % stateis
writer.write('HTTP/1.0 400 Bad Request\r\nContent-type: text/html\r\n\r\n') writer.write('HTTP/1.0 400 Bad Request\r\nContent-type: text/html\r\n\r\n')
writer.write(response) writer.write(response)
await writer.drain() await writer.drain()
await writer.wait_closed() await writer.wait_closed()
microsFlanke = 0 microsFlanke = 0
def microsSeitLetzterFlanke(): def microsSeitLetzterFlanke():
global microsFlanke global microsFlanke
return time.ticks_us() - microsFlanke return time.ticks_us() - microsFlanke
# Main method for the TCS:Bus reader # Main method for the TCS:Bus reader
async def TCSBusReader(): async def TCSBusReader():
global busline, microsFlanke, partyMode, writerActive global busline, microsFlanke, partyMode, writerActive
zustand = False zustand = False
Logger.LogMessage("TCS Busreader started") Logger.LogMessage("TCS Busreader started")
message = [] message = []
while True: while True:
if not writerActive: if not writerActive:
busValue = busline.read_u16() busValue = busline.read_u16()
val = 1 val = 1
if (busValue >= 50000): #voltage on TCS:Bus 0...65535 if (busValue >= 50000): #voltage on TCS:Bus 0...65535
val = 1 val = 1
else: else:
val = 0 val = 0
#measure voltage changes and time in between #measure voltage changes and time in between
dauer = microsSeitLetzterFlanke() dauer = microsSeitLetzterFlanke()
if (dauer > 10000) and (message): #handle recieved message, and reset message if (dauer > 10000) and (message): #handle recieved message, and reset message
message.pop(0) #remove first timing, because we do not need it message.pop(0) #remove first timing, because we do not need it
for i in range(len(message)): #encode message for i in range(len(message)): #encode message
message[i] = int(((round(message[i] / 1000.0) * 1000.0) / 2000) - 1) message[i] = int(((round(message[i] / 1000.0) * 1000.0) / 2000) - 1)
if (message == configs['light_trigger_message']): if (message == configs['light_trigger_message']):
if (configs['log_incoming_bus_messages']): if (configs['log_incoming_bus_messages']):
Logger.LogMessage("Incoming TCS:Bus message for triggering light: " + str(message)) Logger.LogMessage("Incoming TCS:Bus message for triggering light: " + str(message))
#nothing else to do #nothing else to do
elif (message == configs['door_trigger_message']): elif (message == configs['door_trigger_message']):
if (configs['log_incoming_bus_messages']): if (configs['log_incoming_bus_messages']):
Logger.LogMessage("Incoming TCS:Bus message for door trigger: " + str(message)) Logger.LogMessage("Incoming TCS:Bus message for door trigger: " + str(message))
#nothing else to do #nothing else to do
elif (message == configs['door_ringing_message']): elif (message == configs['door_ringing_message']):
if (configs['log_incoming_bus_messages']): if (configs['log_incoming_bus_messages']):
Logger.LogMessage("Incoming TCS:Bus message for door ringing: " + str(message)) Logger.LogMessage("Incoming TCS:Bus message for door ringing: " + str(message))
print ("türklingel") print ("türklingel")
#todo trigger external api #todo trigger external api
elif (message == configs['frontdoor_ringing_message']): elif (message == configs['frontdoor_ringing_message']):
if (configs['log_incoming_bus_messages']): if (configs['log_incoming_bus_messages']):
Logger.LogMessage("Incoming TCS:Bus message for frontdoor ringing: " + str(message)) Logger.LogMessage("Incoming TCS:Bus message for frontdoor ringing: " + str(message))
if (partyMode): if (partyMode):
asyncio.sleep(1) asyncio.sleep(1)
Logger.LogMessage("Triggering Door and Light for Party-Mode") Logger.LogMessage("Triggering Door and Light for Party-Mode")
await TCSBusWriter(configs['door_trigger_message']) await TCSBusWriter(configs['door_trigger_message'])
asyncio.sleep(1) asyncio.sleep(1)
await TCSBusWriter(configs['light_trigger_message']) await TCSBusWriter(configs['light_trigger_message'])
print ("haustürklingel") print ("haustürklingel")
#todo trigger external api #todo trigger external api
else: else:
if (configs['log_incoming_bus_messages']): if (configs['log_incoming_bus_messages']):
Logger.LogMessage("Unknown TCS:Bus message: " + str(message)) Logger.LogMessage("Unknown TCS:Bus message: " + str(message))
message = [] message = []
else: else:
if (val == 0 and zustand == False): if (val == 0 and zustand == False):
message.append(dauer) message.append(dauer)
zustand = True zustand = True
microsFlanke = time.ticks_us() microsFlanke = time.ticks_us()
if (val == 1 and zustand == True): if (val == 1 and zustand == True):
message.append(dauer) message.append(dauer)
zustand = False zustand = False
microsFlanke = time.ticks_us() microsFlanke = time.ticks_us()
await asyncio.sleep(0) await asyncio.sleep(0)
# Main method for the TCS:Bus writer # Main method for the TCS:Bus writer
async def TCSBusWriter(message): async def TCSBusWriter(message):
global writerActive global writerActive
if (message): if (message):
busMessage = list(message) busMessage = list(message)
writerActive = True writerActive = True
for i in range(len(busMessage)): #decode message for i in range(len(busMessage)): #decode message
busMessage[i] = int((busMessage[i] + 1) * 2000) busMessage[i] = int((busMessage[i] + 1) * 2000)
#start sending message #start sending message
sendZero = True sendZero = True
triggerline.on() triggerline.on()
for i in range(len(busMessage)): for i in range(len(busMessage)):
time.sleep_us(busMessage[i]) time.sleep_us(busMessage[i])
sendZero = not sendZero sendZero = not sendZero
if sendZero: if sendZero:
triggerline.on() triggerline.on()
else: else:
triggerline.off() triggerline.off()
#finally end sending message #finally end sending message
triggerline.off() triggerline.off()
writerActive = False writerActive = False
# Main method for daily housekeeping # Main method for daily housekeeping
async def Housekeeper(): async def Housekeeper():
Logger.LogMessage("Housekeeper started") Logger.LogMessage("Housekeeper started")
while True: while True:
Logger.LogMessage("Housekeeper is performing actions") Logger.LogMessage("Housekeeper is performing actions")
Logger.Housekeeping() Logger.Housekeeping()
#todo also do a ntp sync #todo also do a ntp sync
await asyncio.sleep(86400) await asyncio.sleep(86400)
# Main entry point after booting # Main entry point after booting
async def Main(): async def Main():
set_global_exception() set_global_exception()
Logger.LogMessage("Entering MainLoop") Logger.LogMessage("Entering MainLoop")
boottime = time.time() boottime = time.time()
loop = asyncio.get_event_loop() loop = asyncio.get_event_loop()
ShowText("Booting [3/3]", "API Setup: key", secrets['api']) ShowText("Booting [3/3]", "API Setup: key", secrets['api'])
Logger.LogMessage("Setting up API on port " + str(configs['api_port']) + " with key " + secrets['api']) 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'])) loop.create_task(asyncio.start_server(APIHandling, Networking.GetIPAddress(), configs['api_port']))
Logger.LogMessage("API started") Logger.LogMessage("API started")
ShowText("TCS<->FHEM", "Firmware:", version) ShowText("TCS<->FHEM", "Firmware:", version)
Logger.LogMessage("Booting complete with Firmware " + version) Logger.LogMessage("Booting complete with Firmware " + version)
loop.create_task(UiHandling()) loop.create_task(UiHandling())
loop.create_task(TCSBusReader()) loop.create_task(TCSBusReader())
loop.create_task(Housekeeper()) loop.create_task(Housekeeper())
loop.run_forever() loop.run_forever()
# Booting the device # Booting the device
def Boot(): def Boot():
ShowText("Booting [1/3]", "Conn. Wifi:", secrets['ssid'] + "...") ShowText("Booting [1/3]", "Conn. Wifi:", secrets['ssid'] + "...")
ShowText("Booting [1/3]", "Conn. Wifi: MAC", Networking.GetMACAddress()) ShowText("Booting [1/3]", "Conn. Wifi: MAC", Networking.GetMACAddress())
Networking.Connect(configs['disable_wifi_powersavingmode']) Networking.Connect(configs['disable_wifi_powersavingmode'])
if (Networking.Status()): if (Networking.Status()):
ShowText("Booting [1/3]", "Conn. Wifi: IP", Networking.GetIPAddress()) ShowText("Booting [1/3]", "Conn. Wifi: IP", Networking.GetIPAddress())
ShowText("Booting [2/3]", "NTP time:", configs['ntp_host']) ShowText("Booting [2/3]", "NTP time:", configs['ntp_host'])
NTP.SetRTCTimeFromNTP(configs['ntp_host'], configs['gmt_offset'], configs['auto_summertime']) NTP.SetRTCTimeFromNTP(configs['ntp_host'], configs['gmt_offset'], configs['auto_summertime'])
ShowText("Booting [2/3]", "NTP time:", TimeUtils.DateTimeNow()) ShowText("Booting [2/3]", "NTP time:", TimeUtils.DateTimeNow())
else: else:
ShowText("Booting [1/3]", "Conn. Wifi:", "ERROR!") ShowText("Booting [1/3]", "Conn. Wifi:", "ERROR!")
##################################################################### #####################################################################
Boot() Boot()
try: try:
asyncio.run(Main()) asyncio.run(Main())
except KeyboardInterrupt: except KeyboardInterrupt:
Logger.LogMessage("Shutdown.") Logger.LogMessage("Shutdown.")
Oled.fill(0x0000) Oled.fill(0x0000)
Oled.show() Oled.show()
finally: finally:
asyncio.new_event_loop() asyncio.new_event_loop()