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.