Själva styrningen av ljuset består av två saker. Dels en webbaserad frontend där man anger villkoren (vilka skrivs till databasen) och dels en backend som läser databasen och skickar rätt kommandon till servern som styr själva LED-stripen. I front-enden väljer man mellan automatiskt läge där man skapar ett schema för hur ljuset ska variera över dygnet och ett manuellt läge där man kan ange värdena för varje färg och intensitet. Jag har även lagt med en liten Kelvin till RGB-omvandlare för att enkelt kunna ange olika färgtemperaturer. Känsliga tittare varnas, det finns ingen avancerad layout på sidorna utan vi pratar ren HTML, tänk sent 90-tal. Men vill man slänga på en CSS som gör det lite snyggare är det förstås valfritt.
Först kommer front-enden. Den ska ligga i /var/www/cgi-bin och har man följt överkursen i Repetition av temperatur fungerar det direkt, annars får man gå tillbaka dit och fixa så att Apache pratar Python.
#!/usr/bin/python # Import needed libraries import mysql.connector import cgi, cgitb import sys import math # Initalize values konvKelvin = 0 konvR = 0 konvG = 0 konvB = 0 # Open the connection to the database mydb = mysql.connector.connect( host="localhost", user="username", passwd="password", database="aqua-light" ) # Start output of HTML print("Content-Type: text/html") print() print("<html><head><title>Aqua-Light för kar 3</title></head><body>") # Check if something is passed to the script form = cgi.FieldStorage() # Check mode switch and shortcuts if (form.getvalue('mode_select') == 'Automatiskt'): sql = "UPDATE config SET Value = 1 WHERE Name = 'automatic'" mycursor = mydb.cursor() mycursor.execute(sql) mydb.commit() if (form.getvalue('mode_select') == 'Manuellt'): sql = "UPDATE config SET Value = 0 WHERE Name = 'automatic'" mycursor = mydb.cursor() mycursor.execute(sql) mydb.commit() if (form.getvalue('max_select') == 'Max'): sql = "UPDATE config SET Value = 0 WHERE Name = 'automatic'" mycursor = mydb.cursor() mycursor.execute(sql) mydb.commit() sql = "UPDATE manual SET R = 255, G = 255, B = 255, I = 255 WHERE ID = " + form.getvalue("ID") mycursor = mydb.cursor() mycursor.execute(sql) mydb.commit() if (form.getvalue('min_select') == 'Min'): sql = "UPDATE config SET Value = 0 WHERE Name = 'automatic'" mycursor = mydb.cursor() mycursor.execute(sql) mydb.commit() sql = "UPDATE manual SET R = 0, G = 0, B = 0, I = 0 WHERE ID = " + form.getvalue("ID") mycursor = mydb.cursor() mycursor.execute(sql) mydb.commit() # Save new manual values if (form.getvalue('save_manual') == 'Spara'): sql = "UPDATE manual SET R = " + form.getvalue('R') sql = sql + ", G = " + form.getvalue('G') sql = sql + ", B = " + form.getvalue('B') sql = sql + ", I = " + form.getvalue('I') sql = sql + " WHERE ID = " + form.getvalue('ID') mycursor = mydb.cursor() mycursor.execute(sql) mydb.commit() # Add new row for automatic values if (form.getvalue('auto_addnew') == 'Skapa'): sql = "INSERT INTO basedata (H, M, S, R, G, B, I) VALUES (" sql = sql + form.getvalue('autoH') sql = sql + ", "+ form.getvalue('autoM') sql = sql + ", "+ form.getvalue('autoS') sql = sql + ", "+ form.getvalue('autoR') sql = sql + ", "+ form.getvalue('autoG') sql = sql + ", "+ form.getvalue('autoB') sql = sql + ", "+ form.getvalue('autoI') sql = sql + ")" mycursor = mydb.cursor() mycursor.execute(sql) mydb.commit() # Delete row for automatic values if (form.getvalue('auto_del') == 'Ta bort'): sql = "DELETE FROM basedata WHERE ID=" + form.getvalue('ID') mycursor = mydb.cursor() mycursor.execute(sql) mydb.commit() # Change row for automatic values if (form.getvalue('auto_change') == 'Modifiera'): sql = "UPDATE basedata SET" sql = sql + " H=" + form.getvalue('autoH') sql = sql + ", M="+ form.getvalue('autoM') sql = sql + ", S="+ form.getvalue('autoS') sql = sql + ", R="+ form.getvalue('autoR') sql = sql + ", G="+ form.getvalue('autoG') sql = sql + ", B="+ form.getvalue('autoB') sql = sql + ", I="+ form.getvalue('autoI') sql = sql + " WHERE ID=" + form.getvalue('ID') mycursor = mydb.cursor() mycursor.execute(sql) mydb.commit() # Fill the database if (form.getvalue('fill_db') == 'Fyll databasen'): sql = "DELETE FROM automatic" mycursor = mydb.cursor() mycursor.execute(sql) mydb.commit() Sec = 0 R = 0 G = 0 B = 0 I = 0 sql = "SELECT * FROM basedata ORDER BY H, M, S" mycursor = mydb.cursor() mycursor.execute(sql) myresult = mycursor.fetchall() for result in myresult: targetSec = result[1] * 3600 + result[2] * 60 + result[3] deltaR = (result[4] - R) / (targetSec - Sec) deltaG = (result[5] - G) / (targetSec - Sec) deltaB = (result[6] - B) / (targetSec - Sec) deltaI = (result[7] - I) / (targetSec - Sec) for n in range(Sec, targetSec): sql = "INSERT INTO automatic (Sec, R, G, B, I) VALUES (" sql = sql + str(n) sql = sql + ", " + str(int(R)) sql = sql + ", " + str(int(G)) sql = sql + ", " + str(int(B)) sql = sql + ", " + str(int(I)) + ")" mycursor = mydb.cursor() mycursor.execute(sql) mydb.commit() R = R + deltaR G = G + deltaG B = B + deltaB I = I + deltaI R = result[4] G = result[5] B = result[6] I = result[7] Sec = targetSec targetSec = 86400 deltaR = (0 - R) / (targetSec - Sec) deltaG = (0 - G) / (targetSec - Sec) deltaB = (0 - B) / (targetSec - Sec) deltaI = (0 - I) / (targetSec - Sec) for n in range(Sec, 86400): sql = "INSERT INTO automatic (Sec, R, G, B, I) VALUES (" sql = sql + str(n) sql = sql + ", " + str(int(R)) sql = sql + ", " + str(int(G)) sql = sql + ", " + str(int(B)) sql = sql + ", " + str(int(I)) + ")" mycursor = mydb.cursor() mycursor.execute(sql) mydb.commit() R = R + deltaR G = G + deltaG B = B + deltaB I = I + deltaI # Konvert K to RGB if (form.getvalue('save_konvert') == 'Konvertera'): konvKelvin = float(form.getvalue('konvKelvin')) if konvKelvin < 1000: konvKelvin = 1000 if konvKelvin > 40000: konvKelvin = 40000 tmpKelvin = konvKelvin / 100 if tmpKelvin <= 66: konvR = 255 else: tmpCalc = tmpKelvin - 60 tmpCalc = 329.698727446 * (tmpCalc ** -0.1332047592) konvR = int(tmpCalc) if konvR < 0: konvR = 0 elif konvR > 255: konvR = 255 if tmpKelvin <= 66: tmpCalc = tmpKelvin tmpCalc = 99.4708025861 * math.log(tmpCalc) - 161.1195681661 konvG = int(tmpCalc) else: tmpCalc = tmpKelvin - 60 tmpCalc = 288.1221695283 * (tmpCalc ** -0.0755148492) konvG = int(tmpCalc) if konvG < 0: konvG = 0 elif konvG > 255: konvG = 255 if tmpKelvin >= 66: konvB = 255 elif tmpKelvin <= 19: konvB = 0 else: tmpCalc = tmpKelvin - 10 tmpCalc = 138.5177312231 * math.log(tmpCalc) - 305.0447927307 konvB = int(tmpCalc) if konvB < 0: konvB = 0 elif konvB > 255: konvB = 255 # Show mode of operation mycursor = mydb.cursor() mycursor.execute("SELECT * FROM config WHERE Name='automatic'") myresult = mycursor.fetchone() print("<h1>Aktuellt läge: ") if (int(myresult[2]) == 0): print("Manuell styrning") mode = 0 else: print("Automatiskt") mode = 1 print("</h1>") # Fetch manual values mycursor = mydb.cursor() mycursor.execute("SELECT * FROM manual") myresult = mycursor.fetchone() manID = str(myresult[0]) manR = str(myresult[1]) manG = str(myresult[2]) manB = str(myresult[3]) manI = str(myresult[4]) # Selection of mode and shortcuts print("<table><tr>") strHTML = "<td><form action='light.py' method='post'><input type='submit' name='mode_select' value='" if (mode == 0): strHTML = strHTML + "Automatiskt" else: strHTML = strHTML + "Manuellt" strHTML = strHTML + "'></form></td>" print(strHTML) print("<td><form action='light.py' method='post'><input type='hidden' name='ID' value='"+ manID + "'><input type='submit' name='max_select' value='Max'></form></td>") print("<td><form action='light.py' method='post'><input type='hidden' name='ID' value='"+ manID + "'><input type='submit' name='min_select' value='Min'></form></td>") print("</tr></table>") # Manual values print("<h2>Inställningar för manuellt läge:</h2>") print("<form action='light.py' method='post'><input type='hidden' name='ID' value='{}'><table>".format(manID)) print("<tr><th>R</th><th>G</th><th>B</th><th>I</th><th></th></tr><tr>") print("<td><input type='text' maxlength=3 size=3 name='R' value='{}'></td>".format(manR)) print("<td><input type='text' maxlength=3 size=3 name='G' value='{}'></td>".format(manG)) print("<td><input type='text' maxlength=3 size=3 name='B' value='{}'></td>".format(manB)) print("<td><input type='text' maxlength=3 size=3 name='I' value='{}'></td>".format(manI)) print("<td><input type='submit' name='save_manual' value='Spara'></td>") print("</tr></table></form>") # Scheme for automatic mode print("<h2>Schema för automatiskt läge</h2>") print("<table>") print("<tr><th>H</th><th>M</th><th>S</th><th>R</th><th>G</th><th>B</th><th>I</th><th></th></tr>") sql = "SELECT * FROM basedata ORDER BY H, M, S" mycursor = mydb.cursor() mycursor.execute(sql) myresult = mycursor.fetchall() for result in myresult: print("<form action='light.py' method='post'><tr><td><input type='hidden' name='ID' value='{}'><input type='text' size=2 maxlength=2 name='autoH' value='{}'></td><td><input type='text' size=2 maxlength=2 name='autoM' value='{}'></td><td><input type='text' size=2 maxlength=2 name='autoS' value='{}'></td><td><input type='text' size=3 maxlength=3 name='autoR' value='{}'></td><td><input type='text' size=3 maxlength=3 name='autoG' value='{}'></td><td><input type='text' size=3 maxlength=3 name='autoB' value='{}'></td><td><input type='text' size=3 maxlength=3 name='autoI' value='{}'></td><td><input type='submit' name='auto_change' value='Modifiera'><input type='submit' name='auto_del' value='Ta bort'></td></tr></form>".format(result[0], result[1], result[2], result[3], result[4], result[5], result[6], result[7])) print("<form action='light.py' method='post'><tr><td><input type='text' size=2 maxlength=2 name='autoH'></td><td><input type='text' size=2 maxlength=2 name='autoM'></td><td><input type='text' size=2 maxlength=2 name='autoS'></td><td><input type='text' size=3 maxlength=3 name='autoR'></td><td><input type='text' size=3 maxlength=3 name='autoG'></td><td><input type='text' size=3 maxlength=3 name='autoB'></td><td><input type='text' size=3 maxlength=3 name='autoI'></td><td><input type='submit' name='auto_addnew' value='Skapa'></td></tr></form>") print("</table>") print("<form action='light.py' method='post'><input type='submit' name='fill_db' value='Fyll databasen'></form>") # Kelvin to RGB converter print("<h2>Kelvin till RGB</h2>") print("<form action='light.py' method='post'><table>") print("<tr><th>Kelvin</th><th>R</th><th>G</th><th>B</th><th></th></tr>") print("<tr><td><input type='text' name='konvKelvin' value='{}' maxlength=7 size=7></td><td>{}</td><td>{}</td><td>{}</td><td><input type='submit' name='save_konvert' value='Konvertera'></td></tr>".format(konvKelvin, konvR, konvG, konvB)) print("</table></form>") print("</body></html>") mydb.close
Sedan kommer back-end, betydligt kortare. Den kan man placera var man vill, jag har min i /usr/local/bin/aquacontrol.
#!/usr/bin/python # Import needed libraries import mysql.connector import socket import time import datetime # Initalize values NUM_LEDS = 588; LED_TYPE = 3; # Open the connection to the database mydb = mysql.connector.connect( host="localhost", user="username", passwd="password", database="aqua-light" ) # Initialize the LED server s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(("127.0.0.1", 9999)) message = "setup 1," + str(NUM_LEDS) + "," + str(LED_TYPE) + ";init;" s.send(message.encode()) # Main loop while True: # Check running mode in database mycursor = mydb.cursor() mycursor.execute("SELECT * FROM config WHERE Name='automatic'") myresult = mycursor.fetchone() mydb.commit() if (int(myresult[2]) == 0): # manual mode mycursor = mydb.cursor() mycursor.execute("SELECT * FROM manual") myresult = mycursor.fetchone() mydb.commit() message = "fill 1,"+format(myresult[1], "02x")+format(myresult[2], "02x")+format(myresult[3], "02x")+";brightness 1,"+str(myresult[4])+";render;" s.send(message.encode()) else: # automatic mode now = datetime.datetime.now() midnight = now.replace(hour=0, minute=0, second=0, microsecond=0) seconds = (now - midnight).seconds mycursor = mydb.cursor() mycursor.execute("SELECT * FROM automatic WHERE Sec=" + str(seconds)) myresult = mycursor.fetchone() mydb.commit() message = "fill 1,"+format(myresult[2], "02x")+format(myresult[3], "02x")+format(myresult[4], "02x")+";brightness 1,"+str(myresult[5])+";render;" s.send(message.encode()) time.sleep(1)
För att back-end ska starta automatiskt behöver man skapa filen aqua-light.service i /lib/systemd/system med följande innehåll:
[Unit] Description=Aqua-Light After=multi-user.target [Service] Type=idle ExecStart=/usr/local/bin/aquacontrol/aqua-light.py [Install] WantedBy=multi-user.target
Kör sedan
sudo chmod +x /usr/local/bin/aquacontrol/aqua-light.py sudo chmod 644 /lib/systemd/system/aqua-light.service sudo systemctl daemon-reload sudo systemctl enable aqua-light.service
så ska det starta automatiskt vid omstart. Att fylla tabellen med de 86400 raderna för den automatiska styrningen tar närmare en halvtimma på min Raspberry Pi. För att inte Apache ska tröttna på att vänta har jag lagt till
TimeOut 2400
i /etc/apache2/sites-available/000-default.conf. När man surfar till http://adress_till_din_paj/cgi-bin/light.py ser det ut ungefär som följer:

I mitt fall ovan börjar det hända saker 11:00:00 då det fram till 11:10:00 tänds ett månljus som är tänt till 12:00:00. Då byter det till en soluppgång fram till 12:10:00 som sedan tonar upp till full styrka fram till 13:00:00. Det är tänt fram till 21:00:00 då en solnedgång börjar och håller på till 21:50:00. Då byter den till ett månljus under tio minuter fram till 22:00:00. Detta är tänt till 22:50:00 då det börjar tona ut till helt släckt 23:00:00.