diff --git a/.gitignore b/.gitignore
index 36204ea..996aa71 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,3 +5,4 @@ build
dist
controller/package-lock.json
controller/package.json
+controller.spec
diff --git a/readme.md b/README.md
similarity index 100%
rename from readme.md
rename to README.md
diff --git a/controller/controller.py b/controller/controller.py
index 3c5e031..2311ba0 100755
--- a/controller/controller.py
+++ b/controller/controller.py
@@ -90,53 +90,7 @@ def set_pixels(color):
# @click.option("-v", help="Set HEX Color as base visualizer color: -v ffffff")
def main(arg):
if arg == ():
- # proc = subprocess.Popen(["python", "-m", "streamlit", "run", "/var/lib/lc/gui.py", "--server.headless", "True", "--theme.base", "dark", "--theme.primaryColor", "#9f0000", "--theme.backgroundColor", "#181a1b", "--theme.secondaryBackgroundColor", "#1f2123", "--theme.textColor", "#c4c0b8"], stdout=subprocess.PIPE)
- # for line in proc.stdout:
- # if line == b' You can now view your Streamlit app in your browser.\n':
- # break
-
- import streamlit.web.bootstrap as bootstrap
- from streamlit import config
- import gui as gui
-
- di = {'global_disableWatchdogWarning': None, 'global_showWarningOnDirectExecution': None, 'global_developmentMode': None, 'global_logLevel': None, 'global_unitTest': None, 'global_suppressDeprecationWarnings': None, 'global_minCachedMessageSize': None, 'global_maxCachedMessageAge': None, 'global_dataFrameSerialization': None, 'logger_level': None, 'logger_messageFormat': None, 'logger_enableRich': None, 'client_caching': None, 'client_displayEnabled': None, 'client_showErrorDetails': None, 'runner_magicEnabled': None, 'runner_installTracer': None, 'runner_fixMatplotlib': None, 'runner_postScriptGC': None, 'runner_fastReruns': None, 'server_folderWatchBlacklist': None, 'server_fileWatcherType': None, 'server_cookieSecret': None, 'server_headless': True, 'server_runOnSave': None, 'server_allowRunOnSave': None, 'server_address': None, 'server_port': None, 'server_scriptHealthCheckEnabled': None, 'server_baseUrlPath': None, 'server_enableCORS': None, 'server_enableXsrfProtection': None, 'server_maxUploadSize': None, 'server_maxMessageSize': None, 'server_enableWebsocketCompression': None, 'browser_serverAddress': None, 'browser_gatherUsageStats': None, 'browser_serverPort': None, 'ui_hideTopBar': None, 'ui_hideSidebarNav': None, 'mapbox_token': None, 'deprecation_showfileUploaderEncoding': None, 'deprecation_showImageFormat': None, 'deprecation_showPyplotGlobalUse': None, 'theme_base': None, 'theme_primaryColor': None, 'theme_backgroundColor': None, 'theme_secondaryBackgroundColor': None, 'theme_textColor': None, 'theme_font': None}
-
- import multiprocessing
-
- def run():
- config.set_option('server.headless', True)
- bootstrap.run(gui.__file__, 'streamlit run gui.py --server.headless True', [], di)
-
- p = multiprocessing.Process(target=run)
- p.start()
-
- # import requests
-
- # while True:
- # try:
- # print(requests.get('http://localhost:8501'))
- # except:
- # continue
- # print('asdf')
- # break
- # cli.main_run(str(gui.__file__))
- # from streamlit.web.server import Server
- # server = Server(gui.__file__, 'streamlit run gui.py --server.headless True')
- # import tornado.web
-
- # class MainHandler(tornado.web.RequestHandler):
- # def get(self):
- # self.write("Hello, world")
-
- # application = tornado.web.Application([
- # (r"/", ), ])
- # application.listen(8888)
-
- webview.create_window('LED Control', application)
- webview.start()
- # proc.terminate()
- exit()
-
+ pass
match arg[0]:
case "help":
diff --git a/controller/gui.py b/controller/gui.py
deleted file mode 100644
index cfc2b04..0000000
--- a/controller/gui.py
+++ /dev/null
@@ -1,75 +0,0 @@
-
-def main():
- import streamlit as st
- import controller
- import colorsys
- import pickle
- import os
- from PIL import Image
-
- hide_streamlit_style = """
-
-
- """
- st.markdown(hide_streamlit_style, unsafe_allow_html=True)
-
- def hex_to_rgb(hex):
- r = int(hex[1:3],16)
- g = int(hex[3:5],16)
- b = int(hex[5:7],16)
- return r,g,b
-
- def rgb_to_hex(r,g,b):
- return "#" + hex(r)[2:].zfill(2) + hex(g)[2:].zfill(2) + hex(b)[2:].zfill(2)
-
- def load_config():
- try:
- path = os.path.expanduser("~/.config/lc/lc.dmp")
- with open(path, 'rb') as f:
- return pickle.load(f)
- except FileNotFoundError:
- return {'color': '000000', 'saved': []}
-
- def save(conf):
- path = os.path.expanduser("~/.config/lc/lc.dmp")
- with open(path, 'wb') as f:
- pickle.dump(conf, f)
-
- def add_to_conf():
- st.session_state.config['saved'].append(color)
-
- if 'config' not in st.session_state:
- init = 1
- st.session_state.config = load_config()
- color = st.session_state.config['color']
-
- st.title("LED Control")
-
- # r = st.slider("Red", 0, 255, 0)
- # g = st.slider("Green", 0, 255, 0)
- # b = st.slider("Blue", 0, 255, 0)
- h = st.slider("Hue", 0.0, 1.0, 0.0, 0.01)
- s = st.slider("Saturation", 0.0, 1.0, 0.0, 0.01)
- v = st.slider("Value", 0.0, 1.0, 0.0, 0.01)
-
-
- r,g,b = colorsys.hsv_to_rgb(h,s,v)
- color = rgb_to_hex(int(r * 255),int(g * 255),int(b * 255))
-
-
- st.session_state.config['color'] = color[1:]
- save(st.session_state.config)
- #controller.set_pixels(color[1:])
- img = Image.new(mode="RGB", size=(30,30), color=(int(r * 255),int(g * 255),int(b * 255)))
-
- st.subheader(color)
- st.image(img)
- st.button("Save Color", add_to_conf)
- st.session_state.config['saved']
-
-
-if __name__ == "__main__":
- main()
diff --git a/controller/requirements.txt b/controller/requirements.txt
index 0284c04..872969c 100644
--- a/controller/requirements.txt
+++ b/controller/requirements.txt
@@ -1,3 +1,54 @@
+altair==5.0.1
+altgraph==0.17.3
+attrs==23.1.0
+blinker==1.6.2
+bottle==0.12.25
+cachetools==5.3.1
+certifi==2023.5.7
+charset-normalizer==3.1.0
click==8.1.3
-pywebview==3.7.2
-streamlit==1.17.0
+decorator==5.1.1
+gitdb==4.0.10
+GitPython==3.1.31
+idna==3.4
+importlib-metadata==6.7.0
+Jinja2==3.1.2
+jsonschema==4.17.3
+markdown-it-py==3.0.0
+MarkupSafe==2.1.3
+mdurl==0.1.2
+numpy==1.25.0
+packaging==23.1
+pandas==2.0.3
+Pillow==9.5.0
+protobuf==4.23.3
+proxy-tools==0.1.0
+pyarrow==12.0.1
+pycairo==1.24.0
+pydeck==0.8.1b0
+Pygments==2.15.1
+PyGObject==3.44.1
+pyinstaller==5.13.0
+pyinstaller-hooks-contrib==2023.4
+Pympler==1.0.1
+pyrsistent==0.19.3
+python-dateutil==2.8.2
+pytz==2023.3
+pytz-deprecation-shim==0.1.0.post0
+pywebview==4.2.2
+requests==2.31.0
+rich==13.4.2
+six==1.16.0
+smmap==5.0.0
+streamlit==1.24.0
+tenacity==8.2.2
+toml==0.10.2
+toolz==0.12.0
+tornado==6.3.2
+typing_extensions==4.7.0
+tzdata==2023.3
+tzlocal==4.3.1
+urllib3==2.0.3
+validators==0.20.0
+watchdog==3.0.0
+zipp==3.15.0
diff --git a/web/app.py b/web/app.py
new file mode 100644
index 0000000..67fc9bd
--- /dev/null
+++ b/web/app.py
@@ -0,0 +1,36 @@
+import socket
+import asyncio
+from flask import Flask, render_template
+
+app = Flask(__name__)
+
+async def scan_for_pi() -> dict():
+ ip = socket.gethostbyname(socket.gethostname())
+ baseIP = '.'.join(ip.split(".")[:3])
+
+ def scan(ip: str) -> str:
+ try:
+ with socket.socket() as s:
+ s.settimeout(0.5)
+ s.connect((ip, 5000))
+ s.send("PING".encode())
+ data = s.recv(1024).decode()
+ if data == "PONG":
+ print("found:", ip)
+ return ip
+ except OSError:
+ pass
+
+ async def scan_async(ip: str) -> str:
+ return await asyncio.to_thread(scan, ip)
+
+ pi_list = await asyncio.gather(*[scan_async(baseIP + "." + str(i)) for i in range(255)])
+ return [pi for pi in pi_list if pi is not None]
+
+@app.route('/')
+def home():
+ return render_template('index.html')
+
+@app.route('/set/')
+def set(color):
+ return f'{color}'
diff --git a/web/controller.py b/web/controller.py
new file mode 100755
index 0000000..d9285c7
--- /dev/null
+++ b/web/controller.py
@@ -0,0 +1,392 @@
+#!/usr/bin/python3
+import socket
+import os
+import asyncio
+import click
+import pickle
+import re
+import webview
+
+# function generate fibonacci sequence
+def fibonacci(n):
+ if n == 0:
+ return 0
+ elif n == 1:
+ return 1
+ else:
+ return fibonacci(n - 1) + fibonacci(n - 2)
+
+async def scan_for_pi() -> dict():
+ ip = socket.gethostbyname(socket.gethostname())
+ baseIP = '.'.join(ip.split(".")[:3])
+
+ def scan(ip: str) -> str:
+ try:
+ with socket.socket() as s:
+ s.settimeout(0.5)
+ s.connect((ip, 5000))
+ s.send("PING".encode())
+ data = s.recv(1024).decode()
+ if data == "PONG":
+ print("found:", ip)
+ return ip
+ except OSError:
+ pass
+
+ async def scan_async(ip: str) -> str:
+ return await asyncio.to_thread(scan, ip)
+
+ pi_list = await asyncio.gather(*[scan_async(baseIP + "." + str(i)) for i in range(255)])
+ return [pi for pi in pi_list if pi is not None]
+
+class PI:
+ def __init__(self, ip, port=5000):
+ self.ip = ip
+ self.port = port
+ self.connected = False
+ self.socket = socket.socket()
+
+ def __str__(self):
+ return self.ip
+
+ def connect(self):
+ self.socket.connect((self.ip, self.port))
+
+ def send(self, data):
+ self.socket.send(data.encode())
+
+ def disconnect(self):
+ self.socket.close()
+
+def load_config(path=os.path.expanduser("~/.config/lc/lc.conf")):
+ try:
+ with open(path, 'rb') as f:
+ return [PI(ip) for ip in pickle.load(f)]
+ except FileNotFoundError:
+ print("Config does not exist")
+ exit()
+
+def save_config(obj, path=os.path.expanduser("~/.config/lc/lc.conf")):
+ os.makedirs(path[::-1].split('/',1)[-1][::-1], exist_ok=True)
+ with open(path, 'wb') as f:
+ pickle.dump(list([o.ip for o in obj]), f)
+
+def set_pixels(color):
+ if not re.match("[0-9a-f]{6}$", color):
+ print(f"{color} not a valid hex color code")
+ return
+ pi_list = load_config()
+ for pi in pi_list:
+ pi.connect()
+ pi.send(color)
+ pi.disconnect()
+
+@click.command()
+@click.argument("arg", nargs=-1)
+# @click.option("-s", help="Set HEX Color: -s ffffff")
+# @click.option("-v", help="Set HEX Color as base visualizer color: -v ffffff")
+def main(arg):
+ if arg == ():
+ # proc = subprocess.Popen(["python", "-m", "streamlit", "run", "/var/lib/lc/gui.py", "--server.headless", "True", "--theme.base", "dark", "--theme.primaryColor", "#9f0000", "--theme.backgroundColor", "#181a1b", "--theme.secondaryBackgroundColor", "#1f2123", "--theme.textColor", "#c4c0b8"], stdout=subprocess.PIPE)
+ # for line in proc.stdout:
+ # if line == b' You can now view your Streamlit app in your browser.\n':
+ # break
+
+ import streamlit.web.bootstrap as bootstrap
+ from streamlit import config
+ import gui as gui
+
+ di = {'global_disableWatchdogWarning': None, 'global_showWarningOnDirectExecution': None, 'global_developmentMode': None, 'global_logLevel': None, 'global_unitTest': None, 'global_suppressDeprecationWarnings': None, 'global_minCachedMessageSize': None, 'global_maxCachedMessageAge': None, 'global_dataFrameSerialization': None, 'logger_level': None, 'logger_messageFormat': None, 'logger_enableRich': None, 'client_caching': None, 'client_displayEnabled': None, 'client_showErrorDetails': None, 'runner_magicEnabled': None, 'runner_installTracer': None, 'runner_fixMatplotlib': None, 'runner_postScriptGC': None, 'runner_fastReruns': None, 'server_folderWatchBlacklist': None, 'server_fileWatcherType': None, 'server_cookieSecret': None, 'server_headless': True, 'server_runOnSave': None, 'server_allowRunOnSave': None, 'server_address': None, 'server_port': None, 'server_scriptHealthCheckEnabled': None, 'server_baseUrlPath': None, 'server_enableCORS': None, 'server_enableXsrfProtection': None, 'server_maxUploadSize': None, 'server_maxMessageSize': None, 'server_enableWebsocketCompression': None, 'browser_serverAddress': None, 'browser_gatherUsageStats': None, 'browser_serverPort': None, 'ui_hideTopBar': None, 'ui_hideSidebarNav': None, 'mapbox_token': None, 'deprecation_showfileUploaderEncoding': None, 'deprecation_showImageFormat': None, 'deprecation_showPyplotGlobalUse': None, 'theme_base': None, 'theme_primaryColor': None, 'theme_backgroundColor': None, 'theme_secondaryBackgroundColor': None, 'theme_textColor': None, 'theme_font': None}
+
+ import multiprocessing
+
+ def run():
+ config.set_option('server.headless', True)
+ bootstrap.run(gui.__file__, 'streamlit run gui.py --server.headless True', [], di)
+
+ p = multiprocessing.Process(target=run)
+ p.start()
+
+ # import requests
+
+ # while True:
+ # try:
+ # print(requests.get('http://localhost:8501'))
+ # except:
+ # continue
+ # print('asdf')
+ # break
+ # cli.main_run(str(gui.__file__))
+ # from streamlit.web.server import Server
+ # server = Server(gui.__file__, 'streamlit run gui.py --server.headless True')
+ # import tornado.web
+
+ # class MainHandler(tornado.web.RequestHandler):
+ # def get(self):
+ # self.write("Hello, world")
+
+ # application = tornado.web.Application([
+ # (r"/", ), ])
+ # application.listen(8888)
+
+ webview.create_window('LED Control', 'http://localhost:8501')
+ webview.start()
+ # proc.terminate()
+ exit()
+
+
+ match arg[0]:
+ case "help":
+ print("lc [help|set|search|list]")
+
+ case "set":
+ if len(arg) < 2:
+ print("color argument missing")
+ set_pixels(arg[1])
+
+ case "search":
+ ip_list = asyncio.run(scan_for_pi())
+ pi_list = [PI(ip) for ip in ip_list]
+ save_config(pi_list)
+
+ case "list":
+ pi_list = load_config()
+ if pi_list is not None:
+ for pi in pi_list:
+ print(pi)
+ case "music":
+ pass
+
+if __name__ == '__main__':
+ main()
+
+# def helpmenu():
+ # print("light controll\n")
+ # print("Options:")
+ # print("-h show help")
+ # print("-s set static color")
+ # print("-v visualizer")
+ # print("-i interactive interface")
+ # print("-a ambient light")
+ # print("-t test function (debug)")
+
+# def base_color(color):
+ # return [i // min(color) for i in color]
+
+# def visualizer(color, amp_strength=0.6):
+ # r,g,b = hex_to_rgb(color)
+
+ # cava = subprocess.Popen(["cava", "-p", "/etc/lc/cava.conf"], stdout=subprocess.PIPE)
+ # sed = subprocess.Popen(["sed", "-u", "s/;.*;$//"], stdin=cava.stdout, stdout=subprocess.PIPE)
+
+ # for line in sed.stdout:
+ # amp_factor = amp_strength * ((int(line) / 500) - 1) + 1
+ # send(rgb_to_hex(int(r * amp_factor), int(g * amp_factor), int(b * amp_factor)))
+ # cava.stdout.close()
+ # sed.stdout.close()
+
+# def visualizer_cava_thread():
+ # global volume_amp
+
+ # cava = subprocess.Popen(["cava", "-p", "/etc/lc/cava.conf"], stdout=subprocess.PIPE)
+ # sed = subprocess.Popen(["sed", "-u", "s/;.*;$//"], stdin=cava.stdout, stdout=subprocess.PIPE)
+
+ # for line in sed.stdout:
+ # volume_amp = int(line)
+ # print(volume_amp)
+ # cava.stdout.close()
+ # sed.stdout.close()
+
+# def amp_by_vol(color, amp_strength):
+ # global volume_amp
+ # # amp_strength in percentage
+ # amp_factor = amp_strength*((volume_amp/500)-1)+1
+ # #print(amp_factor, color,[c*amp_factor for c in color], volume_amp)
+ # return [c*amp_factor for c in color]
+
+# def vibrant(r,g,b):
+ # intensity = 50 # usabel range 1-100 max:1000
+
+ # intensity = 1+intensity/1000
+ # rgb = [r,g,b]
+ # #min_idx = rgb.index(min(rgb))
+ # d = (r+g+b)/3
+ # for c in range(3):
+ # if rgb[c] < d:
+ # rgb[c] = int(rgb[c]*(intensity**(rgb[c]-d)))
+ # elif rgb[c] > d:
+ # rgb[c] = int(rgb[c]*(-intensity**(-rgb[c]+d)+2))
+ # if rgb[c] > 255:
+ # rgb[c] = 255
+ # #rgb[min_idx] = int(rgb[min_idx]*(rgb[min_idx]/d)**2)
+ # return rgb
+
+# def ambient_light_thread():
+ # r,g,b = 0,0,0
+ # brighness = 1
+ # active_color = ''
+
+ # while True:
+ # # P-Regler
+ # r,g,b = [w+((y-w)*0.1) for y,w in zip((_r,_g,_b),(r,g,b))]
+ # if ((round(r),round(g),round(b)) == (_r,_g,_b)):
+ # active_color = '\033[0;32;40m'
+ # else:
+ # active_color = '\033[0;31;40m'
+ # r_out,g_out,b_out = amp_by_vol((r,g,b), 0.6)
+ # #r_out,g_out,b_out = r,g,b
+ # print(active_color, round(r),round(g),round(b), round(r_out),round(g_out),round(b_out), '\033[0;37;40m')
+ # send(rgb_to_hex(int(r_out*brighness),int(g_out*brighness),int(b_out*brighness)))
+ # time.sleep(0.01)
+
+# ups_counter = 0
+# start_time = time.time()
+
+# def ups():
+ # global ups_counter
+ # global start_time
+
+ # ups_counter += 1
+ # time_d = time.time()-start_time
+ # ups = ups_counter/time_d
+ # print(ups)
+
+# def color_correction(r,g,b):
+ # amp = [1,1,0.8]
+ # threshold = 10
+ # if r < threshold and g < threshold and b < threshold:
+ # return 0,0,0
+ # return int(r*amp[0]), int(g*amp[1]), int(b*amp[2])
+
+# def ambient_light():
+
+ # t1 = Thread(target=ambient_light_thread)
+ # t1.start()
+
+ # t2 = Thread(target=visualizer_cava_thread)
+ # t2.start()
+
+ # global _r,_g,_b
+
+ # counter = 0
+ # start_time = time.time()
+ # while True:
+ # # screenshot
+
+ # # Xorg
+ # img = pyscreenshot.grab(backend="mss", childprocess=False, bbox=(1920,0,4480,1440))
+
+ # #Wayland
+ # #time.sleep(0.1)
+ # #cap = cv2.VideoCapture('/tmp/a')
+ # #count = cap.get(cv2.CAP_PROP_FRAME_COUNT)
+ # #cap.set(cv2.CAP_PROP_POS_FRAMES, count-1)
+
+ # #ret, frame = cap.read()
+
+ # #frame = cv2.cvtColor(frame,cv2.COLOR_BGR2RGB)
+ # #img = Image.fromarray(frame)
+ # #cap.release()
+
+ # # find dominant color
+ # img.thumbnail((2,2))
+ # r,g,b = img.getpixel((0, 0))
+ # r,g,b = vibrant(r,g,b)
+ # _r,_g,_b = color_correction(r,g,b)
+
+ # time.sleep(0.05)
+
+
+# def rgb_to_hex(r,g,b):
+ # return "%02x%02x%02x" % (r,g,b)
+
+# def hex_to_rgb(hex):
+ # r = int(hex[0:2],16)
+ # g = int(hex[2:4],16)
+ # b = int(hex[4:6],16)
+ # return r,g,b
+
+# def test():
+ # for i in range(256):
+ # h = rgb_to_hex(0,i,0)
+ # send(h)
+ # print(h)
+ # time.sleep(0.0)
+
+# def tui_main(scr, *args):
+ # # -- Perform an action with Screen --
+ # scr.border(0)
+ # scr.addstr(5, 5, 'Hello from Curses!', curses.A_BOLD)
+ # scr.addstr(6, 5, 'Press q to close this screen', curses.A_NORMAL)
+ # scr.addstr(8, 5, '\u250C')
+
+ # rgb = [0,0,0]
+ # color_selector = 0
+
+ # while True:
+ # status = '{},{},{} {}'.format(rgb[0], rgb[1], rgb[2], color_selector)
+ # scr.addstr(1, 1, status)
+
+ # ch = scr.getch()
+ # if ch == ord('q'):
+ # break
+ # elif ch == ord('j'):
+ # if rgb[color_selector] > 0:
+ # rgb[color_selector] -= 1
+ # send(rgb_to_hex(rgb[0], rgb[1], rgb[2]))
+ # elif ch == ord('k'):
+ # if rgb[color_selector] < 255:
+ # rgb[color_selector] += 1
+ # send(rgb_to_hex(rgb[0], rgb[1], rgb[2]))
+ # elif ch == ord('l'):
+ # if color_selector < 3:
+ # color_selector += 1
+ # elif ch == ord('h'):
+ # if color_selector > 0:
+ # color_selector -= 1
+
+# def main(argv):
+ # if not sys.stdin.isatty():
+ # connect()
+ # for volume in sys.stdin:
+ # volume = int(volume)
+ # hex_color = rgb_to_hex(volume,0,0)
+ # send(hex_color)
+ # sys.exit()
+
+ # try:
+ # opts, args = getopt.getopt(argv, "s:v:ahti")
+ # except getopt.GetoptError:
+ # print(sys.argv[0], "invalid option")
+ # print("Try", sys.argv[0], "-h for help")
+ # sys.exit(1)
+
+ # for opt, arg in opts:
+ # if opt == "-h":
+ # helpmenu()
+ # elif opt == "-s":
+ # connect()
+ # send(arg)
+ # disconnect()
+ # elif opt == "-a":
+ # connect()
+ # ambient_light()
+ # disconnect()
+ # elif opt == "-v":
+ # connect()
+ # visualizer(arg)
+ # disconnect()
+ # elif opt == "-t":
+ # connect()
+ # test()
+ # disconnect()
+ # elif opt == '-i':
+ # connect()
+ # curses.wrapper(tui_main)
+ # disconnect()
+
+ # sys.exit()
+
+# if __name__ == "__main__":
+
+ # main(sys.argv[1:])
diff --git a/web/index.html b/web/index.html
new file mode 100644
index 0000000..9cfa395
--- /dev/null
+++ b/web/index.html
@@ -0,0 +1,16 @@
+
+
+
+Input Field and Button
+
+
+
+
+
+
+