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.bak/
|
||||
venv.bak/
|
||||
bin/
|
||||
pyvenv.cfg
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
|
@ -160,3 +162,5 @@ cython_debug/
|
|||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.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