first prototype-like version

This commit is contained in:
Finn Christiansen 2023-05-28 12:38:23 +01:00
parent 77a00745fd
commit e553bfbafc
11 changed files with 253 additions and 0 deletions

4
.gitignore vendored
View file

@ -128,6 +128,8 @@ venv/
ENV/ ENV/
env.bak/ env.bak/
venv.bak/ venv.bak/
bin/
pyvenv.cfg
# Spyder project settings # Spyder project settings
.spyderproject .spyderproject
@ -160,3 +162,5 @@ cython_debug/
# option (not recommended) you can uncomment the following to ignore the entire idea folder. # option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/ #.idea/
# ignore socket which is used to communicate with backend
rgb_socket

32
app/__init__.py Normal file
View file

@ -0,0 +1,32 @@
from flask import Flask, render_template
import socket
import os
import json
app = Flask(__name__)
@app.route('/')
def index():
return render_template('index.html')
@app.route('/color/<color>', methods=['POST'])
def color(color):
socket_path = '/var/www/vhosts/rgb.local/rgb_socket'
if os.path.exists(socket_path):
client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
client.connect(socket_path)
client.sendall(color.encode())
client.close()
return (json.dumps({'success': True}),
200,
{'ContentType': 'application/json'})
else:
return (json.dumps({'success': False}),
500,
{'ContentType': 'application/json'})
if __name__ == "__main__":
app.run()

9
app/requirements.txt Normal file
View file

@ -0,0 +1,9 @@
blinker==1.6.2
click==8.1.3
Flask==2.3.2
importlib-metadata==6.6.0
itsdangerous==2.1.2
Jinja2==3.1.2
MarkupSafe==2.1.2
Werkzeug==2.3.4
zipp==3.15.0

62
app/static/app.css Normal file
View file

@ -0,0 +1,62 @@
html {
font-family: sans-serif;
}
h1 {
text-align: center;
}
div.container {
display: flex;
flex-wrap: wrap;
}
div.center {
margin: auto;
max-width: 500px;
}
a.color {
display: block;
width: calc(100vw - 24px);
height: 100px;
border: 1px solid black;
text-align: center;
vertical-align: middle;
line-height:100px;
font-size:18px;
font-weight: bold;
cursor: pointer;
margin-bottom: 16px;
border-radius: 12px;
}
a.red {
background-color: #FF0000;
}
a.green {
background-color: #00FF00;
}
a.blue {
background-color: #0000FF;
color: #FFFFFF;
}
a.cyan {
background-color: #00FFFF;
}
a.yellow {
background-color: #FFFF00;
}
a.orange {
background-color: #FF8000;
}
a.purple {
background-color: #FF00FF;
}
a.white {
background-color: #FFFFFF;
}
a.black {
background-color: #000000;
color: #FFFFFF;
}

12
app/static/app.js Normal file
View file

@ -0,0 +1,12 @@
document.addEventListener("DOMContentLoaded", () => {
document.querySelectorAll("a.color").forEach(button => {
button.addEventListener("click", event => {
console.log("button");
console.log(button.dataset.color);
fetch("/color/" + button.dataset.color, {method: "POST"});
});
});
});

20
app/templates/base.html Normal file
View file

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html>
<head>
<title>RGB {% block title %}{% endblock %} </title>
<meta name="viewport" content="width=device-width, initial-scale=1 maximum-scale=1.0, user-scalable=0" />
<link rel="stylesheet" href="{{ url_for('static', filename='app.css') }}">
<script type="text/javascript" src="{{ url_for('static', filename='app.js') }}"></script>
</head>
<body>
<div>
<h1>RGB controller</h1>
<div class="center">
{% block content %}{% endblock %}
</div>
</div>
</body>
</html>

18
app/templates/index.html Normal file
View file

@ -0,0 +1,18 @@
{% extends "base.html" %}
{% block title %} yay {% endblock %}
{% block content %}
<div class="container">
<a class="color red" data-color="FF0000">red</a>
<a class="color green" data-color="00FF00">green</a>
<a class="color blue" data-color="0000FF">blue</a>
<a class="color cyan" data-color="00FFFF">cyan</a>
<a class="color yellow" data-color="FFFF00">yellow</a>
<a class="color orange" data-color="FF8000">orange</a>
<a class="color purple" data-color="FF00FF">purple</a>
<a class="color white" data-color="FFFFFF">white</a>
<a class="color black" data-color="000000">off</a>
</div>
{% endblock %}

9
requirements.txt Normal file
View file

@ -0,0 +1,9 @@
blinker==1.6.2
click==8.1.3
Flask==2.3.2
importlib-metadata==6.6.0
itsdangerous==2.1.2
Jinja2==3.1.2
MarkupSafe==2.1.2
Werkzeug==2.3.4
zipp==3.15.0

61
rgb.py Executable file
View file

@ -0,0 +1,61 @@
#!/usr/bin/python
import socket
import os
import re
import time
from rpi_ws281x import Adafruit_NeoPixel, Color
# Define functions which animate LEDs in various ways.
def colorWipe(strip, color, wait_ms=50):
"""Wipe color across display a pixel at a time."""
for i in range(strip.numPixels()):
strip.setPixelColor(i, color)
strip.show()
time.sleep(wait_ms / 1000.0)
if __name__ == "__main__":
TOTAL_LED_COUNT = 24
R = 0
G = 0
B = 0
strip = Adafruit_NeoPixel(TOTAL_LED_COUNT, 18, 800000, 5, False, 255)
strip.begin()
colorWipe(strip, Color(0, 0, 0))
# Set the path for the Unix socket
socket_path = '/var/www/vhosts/rgb.local/rgb_socket'
# remove the socket file if it already exists
try:
os.unlink(socket_path)
except OSError:
if os.path.exists(socket_path):
raise
server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
server.bind(socket_path)
server.listen(1)
os.chown(socket_path, 33, 33)
connection, client_address = server.accept()
try:
while True:
data = connection.recv(1024)
if not data:
connection, client_address = server.accept()
continue
rgb = data.decode()
if re.match("^[0-9A-Fa-f]{6}$", rgb):
colorWipe(strip, Color(
int(rgb[0:2], 16),
int(rgb[2:4], 16),
int(rgb[4:6], 16)),
10
)
finally:
colorWipe(strip, Color(0, 0, 0), 10)
connection.close()
os.unlink(socket_path)

12
rgb.service Normal file
View file

@ -0,0 +1,12 @@
[Unit]
Description=RGB Service
[Service]
Type=simple
User=root
ExecStart=/usr/local/bin/rgb.py
Restart=always
RestartSec=30
[Install]
WantedBy=multi-user.target

14
rgb.wsgi Normal file
View file

@ -0,0 +1,14 @@
#!/usr/bin/python
import os
import sys
import logging
logging.basicConfig(stream=sys.stderr)
sys.path.insert(0, os.path.dirname(__file__))
activate_this = os.path.join(os.path.dirname(__file__), 'bin/activate_this.py')
with open(activate_this) as file_:
exec(file_.read(), dict(__file__=activate_this))
from app import app as application
application.secret_key = 'something super SUPER secret'