git.haldean.org bitpic / 4c239e8
move to a static file model instead of a webserver Haldean Brown 11 months ago
4 changed file(s) with 64 addition(s) and 73 deletion(s). Raw diff Collapse all Expand all
00 import anims
11 import argparse
22 import img
3 import os
34 import requests
45 import screen
56
1516 "--anim", default=None, help="set screen to an animation")
1617 p.add_argument(
1718 "--brightness", type=int, default=255, help="set screen brightness")
19 p.add_argument(
20 "--redir", default=None, help="set screen to redirect to this screen")
21 p.add_argument(
22 "--webroot", required=True, help="set webroot of bitpic server on localhost")
23
1824 args = p.parse_args()
19
20 url = "http://{}/set/{}".format(screen.HOST, args.screen)
25 fname = os.path.join(args.webroot, args.screen)
2126
2227 if args.image:
2328 print("setting to image {}".format(args.image))
2429 s = img.image_to_screen(args.image, args.brightness)
25 requests.post(url, data={"data": s.encode()})
30 with open(fname, 'w') as f:
31 f.write(s.encode())
2632 elif args.solid:
2733 print("setting solid color rgb({})".format(args.solid))
2834 c = [int(x) for x in args.solid.split(',')]
2935 s = screen.Screen([[c for _ in range(8)] for _ in range(8)], args.brightness)
30 requests.post(url, data={"data": s.encode()})
36 with open(fname, 'w') as f:
37 f.write(s.encode())
3138 elif args.anim:
3239 print("setting animation {}".format(args.anim))
33 requests.post(url, data={"data": anims.encode(args.anim, args.brightness)})
40 a = anims.encode(args.anim, args.brightness)
41 with open(fname, 'w') as f:
42 f.write(a)
43 elif args.redir:
44 print("redirecting {} to {}".format(args.screen, args.redir))
45 with open(fname, 'w') as f:
46 f.write("redir/{}".format(args.redir))
3447 else:
35 print("setting brightness to {}".format(args.brightness))
36 requests.post("http://{}/set_brightness/{}".format(screen.HOST, args.screen), data={"brightness": args.brightness})
48 print("no operation given")
1313 import numpy as np
1414 import requests
1515 import screen
16 from screen import Screen
1617 import time
17
18 try:
19 with open("sid.txt") as sid_f:
20 SID = sid_f.read().strip()
21 except IOError:
22 SID = screen.DEFAULT_SID
23 URL = "http://{}/get/{}".format(screen.HOST, SID)
2418
2519
2620 class SerialError(Exception):
3832
3933 class Bitpic:
4034 def __init__(self):
35 self._sid = None
36 print("starting bitpic with screen ID {}".format(self.sid))
4137 self.current_screen = None
4238 self.current_anim = None
4339 self.last_anim_check = None
121117 return animtype(anims.KCS)
122118 return None
123119
124 def loop(self):
125 now = datetime.now()
126 time_check = (self.last_anim_check is None or
127 now - self.last_anim_check > timedelta(seconds=5))
128 if time_check or self.current_anim is None:
120 @property
121 def sid(self):
122 if self._sid is not None:
123 return self._sid
124 try:
125 with open("sid.txt") as sid_f:
126 self._sid = sid_f.read().strip()
127 return self._sid
128 except IOError:
129 return screen.DEFAULT_SID
130
131 def load(self, sid=None):
132 if sid is None:
133 sid = self.sid
134 while True:
135 url = "http://{}/{}".format(screen.HOST, sid)
129136 try:
130 res = requests.get(URL).text
131 self.last_anim_check = now
132 except requests.ConnectionError:
133 print("failed to connect, sleeping 10s before retry")
137 res = requests.get(url).text
138 self.last_anim_check = datetime.now()
139 except requests.ConnectionError as e:
140 print("failed to fetch, sleeping 10s before retry: {}".format(e))
134141 time.sleep(10)
135 return
142 continue
136143 if res.startswith("anim/"):
137144 self.current_anim = anims.get(self.current_anim, res)
145 return None
146 elif res.startswith("redir/"):
147 sid = res.split('/')[1]
148 continue
138149 else:
150 try:
151 return Screen.decode(res)
152 except ValueError as e:
153 print("failed to load screen: {}".format(e))
154 return None
155
156 def loop(self):
157 time_check = (self.last_anim_check is None or
158 datetime.now() - self.last_anim_check > timedelta(seconds=5))
159 if time_check or self.current_anim is None:
160 new_screen = self.load()
161 if new_screen is not None:
139162 self.current_anim = None
140 new_screen = screen.Screen.decode(res)
141163 if new_screen != self.current_screen:
142164 self.fade_between(self.current_screen, new_screen)
143165 self.current_screen = new_screen
150172 # if we get here, we either didn't check for new data and are in an
151173 # animation, or we did check for new data and found out we're in
152174 # an animation.
153 new_screen = self.current_anim.get_screen()
154 self.write_screen(new_screen)
175 anim_screen = self.current_anim.get_screen()
176 self.write_screen(anim_screen)
155177 time.sleep(self.current_anim.desired_frame_time)
156178
157179 def main(self):
166188 self.led.push_to_driver()
167189
168190 if __name__ == "__main__":
169 print("starting bitpic with screen ID {}".format(SID))
170191 bp = Bitpic()
171192 bp.main()
00 DEFAULT_SID = "__default"
1 HOST = "localhost:5000"
1 HOST = "localhost:80"
22
33 class Screen:
44 DATA_VERSION = 0
3434
3535 @classmethod
3636 def decode(cls, screenstr):
37 screenstr = screenstr.strip()
3738 if len(screenstr) != cls.DATA_LENGTH_BYTES:
38 raise ValueError("bad screenstr length")
39 raise ValueError("bad screenstr length {}: {}".format(len(screenstr), screenstr))
3940 data_bits = screenstr.split('/')
4041 if len(data_bits) != 3:
4142 raise ValueError("bad screenstr format: wrong number of /'s")
+0
-43
server.py less more
0 import flask
1 import img
2 import io
3 import pprint
4 import screen
5 from screen import Screen
6
7 app = flask.Flask(__name__)
8
9 screens = {
10 screen.DEFAULT_SID: Screen(
11 [[(0, 0, 0) for _ in range(Screen.W)] for _ in range(Screen.H)], 255).encode()
12 }
13
14 def get_real_sid(sid):
15 if sid in screens:
16 return sid
17 return screen.DEFAULT_SID
18
19 @app.route("/get/<sid>")
20 def get(sid):
21 return screens[get_real_sid(sid)]
22
23 @app.route("/view/<sid>")
24 def view(sid):
25 s = Screen.decode(screens[get_real_sid(sid)])
26 imgfile = io.BytesIO()
27 image = img.screen_to_image(s, imgfile)
28 imgfile.seek(0)
29 return flask.send_file(imgfile, mimetype="image/png")
30
31 @app.route("/set/<sid>", methods=["POST"])
32 def set(sid):
33 encoded = flask.request.form["data"]
34 print("received new image for {}: {}".format(sid, encoded))
35 screens[sid] = encoded
36 return flask.url_for("get", sid=sid)
37
38 @app.route("/set_brightness/<sid>", methods=["POST"])
39 def set_brightness(sid):
40 if sid in screens:
41 screens[sid].brightness = int(flask.request.form["brightness"])
42 return flask.url_for("get", sid=sid)