first prototype-like version
This commit is contained in:
parent
77a00745fd
commit
e553bfbafc
11 changed files with 253 additions and 0 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -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
32
app/__init__.py
Normal 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
9
app/requirements.txt
Normal 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
62
app/static/app.css
Normal 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
12
app/static/app.js
Normal 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
20
app/templates/base.html
Normal 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
18
app/templates/index.html
Normal 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
9
requirements.txt
Normal 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
61
rgb.py
Executable 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
12
rgb.service
Normal 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
14
rgb.wsgi
Normal 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'
|
Loading…
Reference in a new issue