annotate service/colplay/colplay.py @ 1058:2dfd367f7113

move code from nightlight.py into new colplay.py Ignore-this: ca60cb8905438b9c9b876422618f526f darcs-hash:e6920524032f6e3edb6ba93ddf9bf5f82ebec65a
author drewp <drewp@bigasterisk.com>
date Mon, 21 Mar 2016 04:17:57 -0700
parents
children 33883457d1c2
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
1058
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
1 """
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
2 Color player: emits many color values that change over time, by
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
3 scanning across images and creating new images by blending other
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
4 patterns.
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
5
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
6 Rewrite of pixel/nightlight.py
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
7 """
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
8 from __future__ import division
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
9 import time, os, logging, json, traceback
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
10 from PIL import Image
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
11 from datetime import datetime, timedelta
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
12 from twisted.internet import reactor, task
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
13 import cyclone.web
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
14 from dateutil.tz import tzlocal
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
15 from cyclone.httpclient import fetch
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
16 from webcolors import rgb_to_hex
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
17
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
18 logging.basicConfig(level=logging.DEBUG)
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
19 log = logging.getLogger()
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
20 logging.getLogger('restkit.client').setLevel(logging.WARN)
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
21
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
22 class Img(object):
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
23 def __init__(self, filename):
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
24 self.filename = filename
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
25 self.reread()
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
26
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
27 def reread(self):
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
28 try:
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
29 self.img = Image.open(self.filename)
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
30 except IOError: # probably mid-write
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
31 time.sleep(.5)
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
32 self.img = Image.open(self.filename)
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
33 self.mtime = os.path.getmtime(self.filename)
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
34
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
35 def getColor(self, x, y):
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
36 """10-bit rgb"""
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
37 if os.path.getmtime(self.filename) > self.mtime:
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
38 self.reread()
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
39 return [v * 4 for v in self.img.getpixel((x, y))[:3]]
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
40
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
41 lightResource = {
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
42 'theater0': 'http://bang:9059/output?s=http://bigasterisk.com/homeauto/board0/rgb_right_top_2&p=http://projects.bigasterisk.com/room/color',
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
43 }
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
44
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
45 lightYPos = {
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
46 'theater0' : 135,
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
47 }
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
48
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
49 def hexFromRgb(rgb):
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
50 return rgb_to_hex(tuple([x // 4 for x in rgb])).encode('ascii')
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
51
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
52 def setColor(lightName, rgb, _req):
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
53 """takes 10-bit r,g,b
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
54
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
55 returns even if the server is down
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
56 """
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
57 log.debug("setColor(%r,%r)", lightName, rgb)
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
58
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
59 serv = lightResource[lightName]
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
60 try:
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
61 h = hexFromRgb(rgb)
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
62 log.debug("put %r to %r", h, serv)
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
63 r = _req(method='PUT', url=serv, body=h,
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
64 headers={"content-type":"text/plain"})
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
65 return r
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
66 except Exception, e:
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
67 log.warn("Talking to: %r" % serv)
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
68 log.warn(e)
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
69 return None
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
70
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
71 def setColorAsync(lightName, rgb):
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
72 """
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
73 uses twisted http, return deferred or sometimes None when there
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
74 was a warning
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
75 """
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
76 def _req(method, url, body, headers):
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
77 d = fetch(url=url, method=method, postdata=body,
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
78 headers=dict((k,[v]) for k,v in headers.items()))
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
79 @d.addErrback
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
80 def err(e):
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
81 log.warn("http client error on %s: %s" % (url, e))
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
82 raise e
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
83 return d
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
84 setColor(lightName, rgb, _req=_req)
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
85
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
86
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
87 class LightState(object):
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
88 def __init__(self):
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
89 self.lastUpdateTime = 0
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
90 self.lastErrorTime = 0
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
91 self.lastError = ""
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
92 self.img = Img("pattern.png")
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
93 self.autosetAfter = dict.fromkeys(lightYPos.keys(),
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
94 datetime.fromtimestamp(0, tzlocal()))
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
95
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
96 def mute(self, name, secs):
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
97 """don't autoset this light for a few seconds"""
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
98 self.autosetAfter[name] = datetime.now(tzlocal()) + timedelta(seconds=secs)
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
99
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
100 def step(self):
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
101 try:
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
102 now = datetime.now(tzlocal())
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
103 hr = now.hour + now.minute / 60 + now.second / 3600
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
104 x = int(((hr - 12) % 24) * 50)
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
105 log.debug("x = %s", x)
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
106
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
107 for name, ypos in lightYPos.items():
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
108 if now > self.autosetAfter[name]:
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
109 c = self.img.getColor(x, ypos)
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
110 setColorAsync(name, c)
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
111 self.lastUpdateTime = time.time()
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
112 except Exception:
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
113 self.lastError = traceback.format_exc()
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
114 self.lastErrorTime = time.time()
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
115
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
116
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
117 class IndexHandler(cyclone.web.RequestHandler):
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
118 def get(self):
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
119 ls = self.settings.lightState
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
120 now = time.time()
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
121 self.set_header("content-type", "application/json")
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
122 self.set_status(200 if ls.lastUpdateTime > ls.lastErrorTime else 500)
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
123 self.write(json.dumps(dict(
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
124 secsSinceLastUpdate=now - ls.lastUpdateTime,
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
125 secsSinceLastError=now - ls.lastErrorTime,
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
126 lastError=ls.lastError,
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
127 ), indent=4))
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
128
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
129 lightState = LightState()
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
130 task.LoopingCall(lightState.step).start(1)
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
131 log.info("listening http on 9051")
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
132 reactor.listenTCP(9051, cyclone.web.Application([
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
133 (r'/', IndexHandler),
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
134 ], lightState=lightState))
2dfd367f7113 move code from nightlight.py into new colplay.py
drewp <drewp@bigasterisk.com>
parents:
diff changeset
135 reactor.run()