Mercurial > code > home > repos > homeauto
comparison service/tinyScreen/tiny_screen.py @ 1193:08a6eb5edf3d
tinyscreen can flip images and render news
Ignore-this: 72c89530e71d4c6f2550bb4deeccd807
darcs-hash:1e6cd5b4615297bd91d63e92951e90594ab98f25
author | drewp <drewp@bigasterisk.com> |
---|---|
date | Wed, 02 Jan 2019 18:40:16 -0800 |
parents | c1cf544711da |
children | 5fc79536885a |
comparison
equal
deleted
inserted
replaced
1192:532942f6d405 | 1193:08a6eb5edf3d |
---|---|
1 from docopt import docopt | |
2 from patchablegraph import PatchableGraph, CycloneGraphHandler, CycloneGraphEventsHandler | |
3 from rdflib import Namespace, URIRef, Literal, Graph | |
4 from rdflib.parser import StringInputSource | |
5 from twisted.internet import reactor | |
6 import cyclone.web | |
7 import sys, logging, time, textwrap | |
8 | |
1 from luma.core.interface.serial import spi | 9 from luma.core.interface.serial import spi |
2 from luma.core.render import canvas | |
3 from luma.oled.device import ssd1331 | 10 from luma.oled.device import ssd1331 |
11 from PIL import Image, ImageFont, ImageDraw | |
12 ROOM = Namespace('http://projects.bigasterisk.com/room/') | |
4 | 13 |
14 logging.basicConfig() | |
15 log = logging.getLogger() | |
5 | 16 |
6 class Screen(object): | 17 class Screen(object): |
7 def __init__(self, spiDevice=1, rotation=0): | 18 def __init__(self, spiDevice=1, rotation=0): |
8 self._dev = ssd1331(spi(device=spiDevice, port=0), rotation=rotation) | 19 self._initOutput(spiDevice, rotation) |
9 with canvas(self._dev) as draw: | 20 self.news = "" |
10 draw.rectangle(d.bounding_box, outline="white", fill="red") | 21 self.animateTo(ROOM['boot']) |
11 | 22 |
23 def _stateImage(self, state): | |
24 return Image.open('anim/%s.png' % state.rsplit('/')[-1]) | |
25 | |
26 def _initOutput(self, spiDevice, rotation): | |
27 self._dev = ssd1331(spi(device=spiDevice, port=0), rotation=rotation) | |
28 | |
29 def setContrast(self, contrast): | |
30 """0..255""" | |
31 self._dev.contrast(contrast) | |
32 | |
33 def hide(self): | |
34 """Switches the display mode OFF, putting the device in low-power sleep mode.""" | |
35 self._dev.hide() | |
36 | |
37 def show(self): | |
38 self._dev.show() | |
39 | |
40 def display(self, img): | |
41 self._dev.display(img) | |
42 | |
43 def animateTo(self, state): | |
44 """ | |
45 boot | |
46 sleep | |
47 locked | |
48 lockedUnknownKey | |
49 unlockNews | |
50 """ | |
51 self.goalState = state | |
52 self.display(self._stateImage(state)) | |
53 if state == ROOM['unlockNews']: | |
54 self.renderNews() | |
55 | |
56 def setNews(self, text): | |
57 self.news = text | |
58 if self.goalState == ROOM['unlockNews']: | |
59 # wrong during animation | |
60 self.renderNews() | |
61 | |
62 def renderNews(self): | |
63 bg = self._stateImage(ROOM['unlockNews']) | |
64 draw = ImageDraw.Draw(bg) | |
65 | |
66 font = ImageFont.truetype("font/Oswald-SemiBold.ttf", 12) | |
67 #w, h = font.getsize('txt') | |
68 for i, line in enumerate( | |
69 textwrap.fill(self.news, width=12).splitlines()): | |
70 draw.text((24, 0 + 10 * i), line, font=font) | |
71 self.display(bg) | |
72 | |
73 class ScreenSim(Screen): | |
74 def _initOutput(self, spiDevice, rotation): | |
75 self.windowScale = 2 | |
76 import pygame | |
77 self.pygame = pygame | |
78 pygame.init() | |
79 self.surf = pygame.display.set_mode( | |
80 (96 * self.windowScale, 64 * self.windowScale)) | |
81 time.sleep(.05) # something was causing the 1st update not to show | |
82 | |
83 def display(self, img): | |
84 pgi = self.pygame.image.fromstring( | |
85 img.tobytes(), img.size, img.mode) | |
86 self.pygame.transform.scale(pgi, self.surf.get_size(), self.surf) | |
87 self.pygame.display.flip() | |
88 | |
89 def rdfGraphBody(body, headers): | |
90 g = Graph() | |
91 g.parse(StringInputSource(body), format='nt') | |
92 return g | |
93 | |
94 class OutputPage(cyclone.web.RequestHandler): | |
95 def put(self): | |
96 arg = self.request.arguments | |
97 if arg.get('s') and arg.get('p'): | |
98 subj = URIRef(arg['s'][-1]) | |
99 pred = URIRef(arg['p'][-1]) | |
100 turtleLiteral = self.request.body | |
101 try: | |
102 obj = Literal(float(turtleLiteral)) | |
103 except ValueError: | |
104 obj = Literal(turtleLiteral) | |
105 stmts = [(subj, pred, obj)] | |
106 else: | |
107 nt = self.request.body.replace("\\n", "\n") # wrong, but i can't quote right in curl | |
108 g = rdfGraphBody(nt, self.request.headers) | |
109 assert len(g) | |
110 stmts = list(g.triples((None, None, None))) | |
111 self._onStatement(stmts) | |
112 | |
113 def _onStatement(self, stmts): | |
114 """ | |
115 (disp :brightness 1.0 . ) | |
116 (disp :state :locked . ) | |
117 (disp :state :sleep . ) | |
118 (disp :state :readKeyUnlock . disp :news "some news text" . ) | |
119 """ | |
120 disp = ROOM['frontDoorOled'] | |
121 for stmt in stmts: | |
122 if stmt[:2] == (disp, ROOM['news']): | |
123 self.settings.screen.setNews(stmt[2].toPython()) | |
124 elif stmt[:2] == (disp, ROOM['state']): | |
125 self.settings.screen.animateTo(stmt[2]) | |
126 else: | |
127 log.warn("ignoring %s", stmt) | |
128 | |
129 if __name__ == '__main__': | |
130 arg = docopt(""" | |
131 Usage: tiny_screen.py [options] | |
132 | |
133 -v Verbose | |
134 -x Draw to X11 window, not output hardware | |
135 """) | |
136 log.setLevel(logging.WARN) | |
137 if arg['-v']: | |
138 from twisted.python import log as twlog | |
139 twlog.startLogging(sys.stdout) | |
140 log.setLevel(logging.DEBUG) | |
141 | |
142 masterGraph = PatchableGraph() | |
143 | |
144 if arg['-x']: | |
145 screen = ScreenSim() | |
146 else: | |
147 screen = Screen(spiDevice=1) | |
148 | |
149 port = 10013 | |
150 reactor.listenTCP(port, cyclone.web.Application([ | |
151 (r"/()", cyclone.web.StaticFileHandler, | |
152 {"path": ".", "default_filename": "index.html"}), | |
153 (r"/graph", CycloneGraphHandler, {'masterGraph': masterGraph}), | |
154 (r"/graph/events", CycloneGraphEventsHandler, | |
155 {'masterGraph': masterGraph}), | |
156 (r'/output', OutputPage), | |
157 ], screen=screen, masterGraph=masterGraph, debug=arg['-v']), | |
158 interface='::') | |
159 log.warn('serving on %s', port) | |
160 | |
161 reactor.run() |