Mercurial > code > home > repos > homeauto
comparison service/piNode/devices.py @ 1427:6bd36e5e109f
whitespace
Ignore-this: 8abcbd0552c4356db0073b464fc3816a
darcs-hash:2917f14b52fb149530a2369486ca9876cb0d334a
author | drewp <drewp@bigasterisk.com> |
---|---|
date | Thu, 08 Aug 2019 16:48:40 -0700 |
parents | 83043957c809 |
children | 69a84b3d1dfa |
comparison
equal
deleted
inserted
replaced
1426:c3c2418d138c | 1427:6bd36e5e109f |
---|---|
64 def hostStateInit(self): | 64 def hostStateInit(self): |
65 """ | 65 """ |
66 If you don't want to use __init__, you can use this to set up | 66 If you don't want to use __init__, you can use this to set up |
67 whatever storage you might need for hostStatements | 67 whatever storage you might need for hostStatements |
68 """ | 68 """ |
69 | 69 |
70 def description(self): | 70 def description(self): |
71 return { | 71 return { |
72 'uri': self.uri, | 72 'uri': self.uri, |
73 'className': self.__class__.__name__, | 73 'className': self.__class__.__name__, |
74 'pinNumber': getattr(self, 'pinNumber', None), | 74 'pinNumber': getattr(self, 'pinNumber', None), |
84 (e.g. light brightness) if its master version is in this | 84 (e.g. light brightness) if its master version is in this |
85 object. This method is called on /graph requests so it should | 85 object. This method is called on /graph requests so it should |
86 be fast. | 86 be fast. |
87 """ | 87 """ |
88 return [] | 88 return [] |
89 | 89 |
90 def watchPrefixes(self): | 90 def watchPrefixes(self): |
91 """ | 91 """ |
92 subj,pred pairs of the statements that might be returned from | 92 subj,pred pairs of the statements that might be returned from |
93 readFromPoll, so the dashboard knows what it should | 93 readFromPoll, so the dashboard knows what it should |
94 watch. This should be eliminated, as the dashboard should just | 94 watch. This should be eliminated, as the dashboard should just |
96 """ | 96 """ |
97 return [] | 97 return [] |
98 | 98 |
99 def poll(self): | 99 def poll(self): |
100 return [] # statements | 100 return [] # statements |
101 | 101 |
102 def outputPatterns(self): | 102 def outputPatterns(self): |
103 """ | 103 """ |
104 Triple patterns, using None as a wildcard, that should be routed | 104 Triple patterns, using None as a wildcard, that should be routed |
105 to sendOutput | 105 to sendOutput |
106 """ | 106 """ |
110 """ | 110 """ |
111 structs to make output widgets on the dashboard. ~1 of these per | 111 structs to make output widgets on the dashboard. ~1 of these per |
112 handler you have in sendOutput | 112 handler you have in sendOutput |
113 """ | 113 """ |
114 return [] | 114 return [] |
115 | 115 |
116 def sendOutput(self, statements): | 116 def sendOutput(self, statements): |
117 """ | 117 """ |
118 If we got statements that match this class's outputPatterns, this | 118 If we got statements that match this class's outputPatterns, this |
119 will be called with the statements that matched. | 119 will be called with the statements that matched. |
120 | 120 |
121 Todo: it would be fine to read back confirmations or | 121 Todo: it would be fine to read back confirmations or |
122 whatever. Just need a way to collect them into graph statements. | 122 whatever. Just need a way to collect them into graph statements. |
123 """ | 123 """ |
124 raise NotImplementedError | 124 raise NotImplementedError |
125 | 125 |
126 _knownTypes = set() | 126 _knownTypes = set() |
127 def register(deviceType): | 127 def register(deviceType): |
128 _knownTypes.add(deviceType) | 128 _knownTypes.add(deviceType) |
129 return deviceType | 129 return deviceType |
130 | 130 |
131 @register | 131 @register |
132 class MotionSensorInput(DeviceType): | 132 class MotionSensorInput(DeviceType): |
133 """ | 133 """ |
134 Triggering all the time? Try 5V VCC, per https://electronics.stackexchange.com/a/416295 | 134 Triggering all the time? Try 5V VCC, per https://electronics.stackexchange.com/a/416295 |
135 | 135 |
136 0 30s 60s 90s 10min | 136 0 30s 60s 90s 10min |
137 | | | | ... | | 137 | | | | ... | |
138 Sensor input ******** ** ******* **** | 138 Sensor input ******** ** ******* **** |
139 :sees output ........ .. ....... .... | 139 :sees output ........ .. ....... .... |
140 :seesRecently ............................................................. | 140 :seesRecently ............................................................. |
141 :seesRecently30 .................................... | 141 :seesRecently30 .................................... |
142 :motionStart x x x x | 142 :motionStart x x x x |
158 self.lastMotionStart90 = 0 | 158 self.lastMotionStart90 = 0 |
159 | 159 |
160 def poll(self): | 160 def poll(self): |
161 motion = self.pi.read(self.pinNumber) | 161 motion = self.pi.read(self.pinNumber) |
162 now = time.time() | 162 now = time.time() |
163 | 163 |
164 oneshot = [] | 164 oneshot = [] |
165 if self.lastRead is not None and motion != self.lastRead: | 165 if self.lastRead is not None and motion != self.lastRead: |
166 oneshot = [(self.uri, ROOM['sees'], ROOM['motionStart'])] | 166 oneshot = [(self.uri, ROOM['sees'], ROOM['motionStart'])] |
167 for v, t in [('lastMotionStart30', 30), ('lastMotionStart90', 90)]: | 167 for v, t in [('lastMotionStart30', 30), ('lastMotionStart90', 90)]: |
168 if now - getattr(self, v) > t: | 168 if now - getattr(self, v) > t: |
169 oneshot.append((self.uri, ROOM['sees'], ROOM['motionStart%s' % t])) | 169 oneshot.append((self.uri, ROOM['sees'], ROOM['motionStart%s' % t])) |
170 setattr(self, v, now) | 170 setattr(self, v, now) |
171 self.lastRead = motion | 171 self.lastRead = motion |
172 | 172 |
173 return {'latest': [ | 173 return {'latest': [ |
174 (self.uri, ROOM['sees'], | 174 (self.uri, ROOM['sees'], |
175 ROOM['motion'] if motion else ROOM['noMotion']), | 175 ROOM['motion'] if motion else ROOM['noMotion']), |
176 ] + self.recentMotionStatements(now, motion), | 176 ] + self.recentMotionStatements(now, motion), |
177 'oneshot': oneshot} | 177 'oneshot': oneshot} |
184 dt = now - self.lastMotionTime | 184 dt = now - self.lastMotionTime |
185 return [(self.uri, ROOM['seesRecently'], | 185 return [(self.uri, ROOM['seesRecently'], |
186 ROOM['motion'] if (dt < 60 * 10) else ROOM['noMotion']), | 186 ROOM['motion'] if (dt < 60 * 10) else ROOM['noMotion']), |
187 (self.uri, ROOM['seesRecently30'], | 187 (self.uri, ROOM['seesRecently30'], |
188 ROOM['motion'] if (dt < 30) else ROOM['noMotion'])] | 188 ROOM['motion'] if (dt < 30) else ROOM['noMotion'])] |
189 | 189 |
190 def watchPrefixes(self): | 190 def watchPrefixes(self): |
191 return [ | 191 return [ |
192 (self.uri, ROOM['sees']), | 192 (self.uri, ROOM['sees']), |
193 (self.uri, ROOM['seesRecently']), | 193 (self.uri, ROOM['seesRecently']), |
194 (self.uri, ROOM['seesRecently30']), | 194 (self.uri, ROOM['seesRecently30']), |
200 """3 PWMs for r/g/b on a strip""" | 200 """3 PWMs for r/g/b on a strip""" |
201 # pigpio daemon is working fine, but | 201 # pigpio daemon is working fine, but |
202 # https://github.com/RPi-Distro/python-gpiozero/blob/59ba7154c5918745ac894ea03503667d6473c760/gpiozero/output_devices.py#L213 | 202 # https://github.com/RPi-Distro/python-gpiozero/blob/59ba7154c5918745ac894ea03503667d6473c760/gpiozero/output_devices.py#L213 |
203 # can also apparently do PWM | 203 # can also apparently do PWM |
204 deviceType = ROOM['RgbStrip'] | 204 deviceType = ROOM['RgbStrip'] |
205 | 205 |
206 @classmethod | 206 @classmethod |
207 def findInstances(cls, graph, board, pi): | 207 def findInstances(cls, graph, board, pi): |
208 for row in graph.query("""SELECT DISTINCT ?dev ?r ?g ?b WHERE { | 208 for row in graph.query("""SELECT DISTINCT ?dev ?r ?g ?b WHERE { |
209 ?board | 209 ?board |
210 :hasPin ?rpin; | 210 :hasPin ?rpin; |
225 | 225 |
226 def __init__(self, graph, uri, pi, r, g, b): | 226 def __init__(self, graph, uri, pi, r, g, b): |
227 self.graph, self.uri, self.pi = graph, uri, pi | 227 self.graph, self.uri, self.pi = graph, uri, pi |
228 self.rgb = map(int, [r, g, b]) | 228 self.rgb = map(int, [r, g, b]) |
229 self.value = '#000000' | 229 self.value = '#000000' |
230 | 230 |
231 def setup(self): | 231 def setup(self): |
232 for i in self.rgb: | 232 for i in self.rgb: |
233 setupPwm(self.pi, i) | 233 setupPwm(self.pi, i) |
234 | 234 |
235 def hostStatements(self): | 235 def hostStatements(self): |
236 return [(self.uri, ROOM['color'], Literal(self.value))] | 236 return [(self.uri, ROOM['color'], Literal(self.value))] |
237 | 237 |
238 def outputPatterns(self): | 238 def outputPatterns(self): |
239 return [(self.uri, ROOM['color'], None)] | 239 return [(self.uri, ROOM['color'], None)] |
240 | 240 |
241 def _rgbFromHex(self, h): | 241 def _rgbFromHex(self, h): |
242 rrggbb = h.lstrip('#') | 242 rrggbb = h.lstrip('#') |
243 return [int(x, 16) for x in [rrggbb[0:2], rrggbb[2:4], rrggbb[4:6]]] | 243 return [int(x, 16) for x in [rrggbb[0:2], rrggbb[2:4], rrggbb[4:6]]] |
244 | 244 |
245 def sendOutput(self, statements): | 245 def sendOutput(self, statements): |
246 assert len(statements) == 1 | 246 assert len(statements) == 1 |
247 assert statements[0][:2] == (self.uri, ROOM['color']) | 247 assert statements[0][:2] == (self.uri, ROOM['color']) |
248 | 248 |
249 rgb = self._rgbFromHex(statements[0][2]) | 249 rgb = self._rgbFromHex(statements[0][2]) |
250 self.value = statements[0][2] | 250 self.value = statements[0][2] |
251 | 251 |
252 for (i, v) in zip(self.rgb, rgb): | 252 for (i, v) in zip(self.rgb, rgb): |
253 self.pi.set_PWM_dutycycle(i, v) | 253 self.pi.set_PWM_dutycycle(i, v) |
254 | 254 |
255 def outputWidgets(self): | 255 def outputWidgets(self): |
256 return [{ | 256 return [{ |
257 'element': 'output-rgb', | 257 'element': 'output-rgb', |
258 'subj': self.uri, | 258 'subj': self.uri, |
259 'pred': ROOM['color'], | 259 'pred': ROOM['color'], |
272 DeviceType.__init__(self, *a, **kw) | 272 DeviceType.__init__(self, *a, **kw) |
273 import DHT22 | 273 import DHT22 |
274 self.sens = DHT22.sensor(self.pi, self.pinNumber) | 274 self.sens = DHT22.sensor(self.pi, self.pinNumber) |
275 self.recentLowTemp = (0, None) # time, temp | 275 self.recentLowTemp = (0, None) # time, temp |
276 self.recentPeriodSec = 30 | 276 self.recentPeriodSec = 30 |
277 | 277 |
278 def poll(self): | 278 def poll(self): |
279 stmts = set() | 279 stmts = set() |
280 | 280 |
281 now = time.time() | 281 now = time.time() |
282 if self.recentLowTemp[0] < now - self.recentPeriodSec: | 282 if self.recentLowTemp[0] < now - self.recentPeriodSec: |
300 stmts.add((self.uri, RDFS['comment'], | 300 stmts.add((self.uri, RDFS['comment'], |
301 Literal('No recent DHT response (%.02f sec old)' % self.sens.staleness()))) | 301 Literal('No recent DHT response (%.02f sec old)' % self.sens.staleness()))) |
302 | 302 |
303 if self.recentLowTemp[1] is not None: | 303 if self.recentLowTemp[1] is not None: |
304 stmts.add((self.uri, ROOM['recentLowTemperatureF'], Literal(self.recentLowTemp[1]))) | 304 stmts.add((self.uri, ROOM['recentLowTemperatureF'], Literal(self.recentLowTemp[1]))) |
305 | 305 |
306 self.sens.trigger() | 306 self.sens.trigger() |
307 | 307 |
308 return stmts | 308 return stmts |
309 | 309 |
310 def watchPrefixes(self): | 310 def watchPrefixes(self): |
311 return [ | 311 return [ |
312 (self.uri, ROOM['temperatureF']), | 312 (self.uri, ROOM['temperatureF']), |
313 (self.uri, ROOM['humidity']), | 313 (self.uri, ROOM['humidity']), |
314 ] | 314 ] |
339 ROOM['press'] if closed else ROOM['release']), | 339 ROOM['press'] if closed else ROOM['release']), |
340 ] | 340 ] |
341 else: | 341 else: |
342 oneshot = [] | 342 oneshot = [] |
343 self.lastClosed = closed | 343 self.lastClosed = closed |
344 | 344 |
345 return {'latest': [ | 345 return {'latest': [ |
346 (self.uri, ROOM['buttonState'], | 346 (self.uri, ROOM['buttonState'], |
347 ROOM['pressed'] if closed else ROOM['notPressed']), | 347 ROOM['pressed'] if closed else ROOM['notPressed']), |
348 ], | 348 ], |
349 'oneshot':oneshot} | 349 'oneshot':oneshot} |
350 | 350 |
351 def watchPrefixes(self): | 351 def watchPrefixes(self): |
352 return [ | 352 return [ |
353 (self.uri, ROOM['buttonState']), | 353 (self.uri, ROOM['buttonState']), |
354 ] | 354 ] |
355 | 355 |
356 @register | 356 @register |
357 class OneWire(DeviceType): | 357 class OneWire(DeviceType): |
358 """ | 358 """ |
359 Also see /my/proj/ansible/roles/raspi_io_node/tasks/main.yml for | 359 Also see /my/proj/ansible/roles/raspi_io_node/tasks/main.yml for |
360 some system config that contains the pin number that you want to | 360 some system config that contains the pin number that you want to |
394 | 394 |
395 returnValue(stmts) | 395 returnValue(stmts) |
396 except Exception as e: | 396 except Exception as e: |
397 log.error(e) | 397 log.error(e) |
398 os.abort() | 398 os.abort() |
399 | 399 |
400 def watchPrefixes(self): | 400 def watchPrefixes(self): |
401 return [(s.uri, ROOM['temperatureF']) for s in self._sensors] | 401 return [(s.uri, ROOM['temperatureF']) for s in self._sensors] |
402 | 402 |
403 class FilteredValue(object): | 403 class FilteredValue(object): |
404 def __init__(self, setter, | 404 def __init__(self, setter, |
405 slew=2.0, # step/sec max slew rate | 405 slew=2.0, # step/sec max slew rate |
406 accel=5, # step/sec^2 acceleration | 406 accel=5, # step/sec^2 acceleration |
407 ): | 407 ): |
408 self.setter = setter | 408 self.setter = setter |
409 self.slew, self.accel = slew, accel | 409 self.slew, self.accel = slew, accel |
410 | 410 |
411 self.x = None | 411 self.x = None |
412 self.dx = 0 | 412 self.dx = 0 |
413 self.goal = self.x | 413 self.goal = self.x |
414 self.lastStep = 0 | 414 self.lastStep = 0 |
415 | 415 |
438 self.x = nextX | 438 self.x = nextX |
439 reactor.callLater(.05, self.step) | 439 reactor.callLater(.05, self.step) |
440 | 440 |
441 #print "x= %(x)s dx= %(dx)s goal= %(goal)s" % self.__dict__ | 441 #print "x= %(x)s dx= %(dx)s goal= %(goal)s" % self.__dict__ |
442 self.setter(self.x) | 442 self.setter(self.x) |
443 | 443 |
444 @register | 444 @register |
445 class LedOutput(DeviceType): | 445 class LedOutput(DeviceType): |
446 deviceType = ROOM['LedOutput'] | 446 deviceType = ROOM['LedOutput'] |
447 | 447 |
448 def hostStateInit(self): | 448 def hostStateInit(self): |
455 class Instant(object): | 455 class Instant(object): |
456 def set(self, goal): | 456 def set(self, goal): |
457 _setPwm(goal) | 457 _setPwm(goal) |
458 self.fv = Instant() | 458 self.fv = Instant() |
459 self.gamma = float(self.graph.value(self.uri, ROOM['gamma'], default=1)) | 459 self.gamma = float(self.graph.value(self.uri, ROOM['gamma'], default=1)) |
460 | 460 |
461 def setup(self): | 461 def setup(self): |
462 setupPwm(self.pi, self.pinNumber) | 462 setupPwm(self.pi, self.pinNumber) |
463 | 463 |
464 def outputPatterns(self): | 464 def outputPatterns(self): |
465 return [(self.uri, ROOM['brightness'], None)] | 465 return [(self.uri, ROOM['brightness'], None)] |
466 | 466 |
467 def sendOutput(self, statements): | 467 def sendOutput(self, statements): |
468 assert len(statements) == 1 | 468 assert len(statements) == 1 |
469 assert statements[0][:2] == (self.uri, ROOM['brightness']) | 469 assert statements[0][:2] == (self.uri, ROOM['brightness']) |
470 self.value = float(statements[0][2]) | 470 self.value = float(statements[0][2]) |
471 self.fv.set(self.value) | 471 self.fv.set(self.value) |
473 def _setPwm(self, x): | 473 def _setPwm(self, x): |
474 v = max(0, min(255, int((x ** self.gamma)* 255))) | 474 v = max(0, min(255, int((x ** self.gamma)* 255))) |
475 self.pi.set_PWM_dutycycle(self.pinNumber, v) | 475 self.pi.set_PWM_dutycycle(self.pinNumber, v) |
476 | 476 |
477 def hostStatements(self): | 477 def hostStatements(self): |
478 return [(self.uri, ROOM['brightness'], Literal(self.value))] | 478 return [(self.uri, ROOM['brightness'], Literal(self.value))] |
479 | 479 |
480 def outputWidgets(self): | 480 def outputWidgets(self): |
481 return [{ | 481 return [{ |
482 'element': 'output-slider', | 482 'element': 'output-slider', |
483 'min': 0, | 483 'min': 0, |
484 'max': 1, | 484 'max': 1, |
485 'step': 1 / 255, | 485 'step': 1 / 255, |
486 'subj': self.uri, | 486 'subj': self.uri, |
487 'pred': ROOM['brightness'], | 487 'pred': ROOM['brightness'], |
488 }] | 488 }] |
489 | 489 |
490 | 490 |
491 @register | 491 @register |
492 class OnboardTemperature(DeviceType): | 492 class OnboardTemperature(DeviceType): |
493 deviceType = ROOM['OnboardTemperature'] | 493 deviceType = ROOM['OnboardTemperature'] |
494 pollPeriod = 10 | 494 pollPeriod = 10 |
495 @classmethod | 495 @classmethod |
496 def findInstances(cls, graph, board, pi): | 496 def findInstances(cls, graph, board, pi): |
497 log.debug('graph has any connected devices of type OnboardTemperature on %r?', board) | 497 log.debug('graph has any connected devices of type OnboardTemperature on %r?', board) |
498 for row in graph.query('''SELECT DISTINCT ?uri WHERE { | 498 for row in graph.query('''SELECT DISTINCT ?uri WHERE { |
499 ?board :onboardDevice ?uri . | 499 ?board :onboardDevice ?uri . |
500 ?uri a :OnboardTemperature . | 500 ?uri a :OnboardTemperature . |
501 }''', initBindings=dict(board=board)): | 501 }''', initBindings=dict(board=board)): |
502 log.debug(' found %s', row.uri) | 502 log.debug(' found %s', row.uri) |
503 yield cls(graph, row.uri, pi, pinNumber=None) | 503 yield cls(graph, row.uri, pi, pinNumber=None) |
504 | 504 |
505 def poll(self): | 505 def poll(self): |
506 milliC = open('/sys/class/thermal/thermal_zone0/temp').read().strip() | 506 milliC = open('/sys/class/thermal/thermal_zone0/temp').read().strip() |
507 c = float(milliC) / 1000. | 507 c = float(milliC) / 1000. |
508 f = c * 1.8 + 32 | 508 f = c * 1.8 + 32 |
509 return [ | 509 return [ |
519 pixelStats = scales.collection('/rgbPixels', | 519 pixelStats = scales.collection('/rgbPixels', |
520 scales.PmfStat('updateOutput'), | 520 scales.PmfStat('updateOutput'), |
521 scales.PmfStat('currentColors'), | 521 scales.PmfStat('currentColors'), |
522 scales.PmfStat('poll'), | 522 scales.PmfStat('poll'), |
523 ) | 523 ) |
524 | 524 |
525 @register | 525 @register |
526 class RgbPixels(DeviceType): | 526 class RgbPixels(DeviceType): |
527 """chain of ws2812 rgb pixels on pin GPIO18""" | 527 """chain of ws2812 rgb pixels on pin GPIO18""" |
528 deviceType = ROOM['RgbPixels'] | 528 deviceType = ROOM['RgbPixels'] |
529 | 529 |
530 def hostStateInit(self): | 530 def hostStateInit(self): |
531 self.anim = RgbPixelsAnimation(self.graph, self.uri, self.updateOutput) | 531 self.anim = RgbPixelsAnimation(self.graph, self.uri, self.updateOutput) |
532 log.debug('%s maxIndex = %s', self.uri, self.anim.maxIndex()) | 532 log.debug('%s maxIndex = %s', self.uri, self.anim.maxIndex()) |
533 self.neo = rpi_ws281x.Adafruit_NeoPixel(self.anim.maxIndex() + 1, pin=18) | 533 self.neo = rpi_ws281x.Adafruit_NeoPixel(self.anim.maxIndex() + 1, pin=18) |
534 self.neo.begin() | 534 self.neo.begin() |
535 | 535 |
536 colorOrder, stripType = self.anim.getColorOrder(self.graph, self.uri) | 536 colorOrder, stripType = self.anim.getColorOrder(self.graph, self.uri) |
537 | 537 |
538 def sendOutput(self, statements): | 538 def sendOutput(self, statements): |
539 self.anim.onStatements(statements) | 539 self.anim.onStatements(statements) |
540 | 540 |
541 @pixelStats.updateOutput.time() | 541 @pixelStats.updateOutput.time() |
542 def updateOutput(self): | 542 def updateOutput(self): |
546 print list(self.anim.currentColors()) | 546 print list(self.anim.currentColors()) |
547 return | 547 return |
548 | 548 |
549 with pixelStats.currentColors.time(): | 549 with pixelStats.currentColors.time(): |
550 colors = self.anim.currentColors() | 550 colors = self.anim.currentColors() |
551 | 551 |
552 for idx, (r, g, b) in colors: | 552 for idx, (r, g, b) in colors: |
553 if idx < 4: | 553 if idx < 4: |
554 log.debug('out color %s (%s,%s,%s)', idx, r, g, b) | 554 log.debug('out color %s (%s,%s,%s)', idx, r, g, b) |
555 self.neo.setPixelColorRGB(idx, r, g, b) | 555 self.neo.setPixelColorRGB(idx, r, g, b) |
556 self.neo.show() | 556 self.neo.show() |
560 self.anim.step() | 560 self.anim.step() |
561 return [] | 561 return [] |
562 | 562 |
563 def hostStatements(self): | 563 def hostStatements(self): |
564 return self.anim.hostStatements() | 564 return self.anim.hostStatements() |
565 | 565 |
566 def outputPatterns(self): | 566 def outputPatterns(self): |
567 return self.anim.outputPatterns() | 567 return self.anim.outputPatterns() |
568 | 568 |
569 def outputWidgets(self): | 569 def outputWidgets(self): |
570 return self.anim.outputWidgets() | 570 return self.anim.outputWidgets() |
571 | 571 |
572 @register | 572 @register |
573 class Lcd8544(DeviceType): | 573 class Lcd8544(DeviceType): |
574 """PCD8544 lcd (nokia 5110)""" | 574 """PCD8544 lcd (nokia 5110)""" |
575 deviceType = ROOM['RgbStrip'] | 575 deviceType = ROOM['RgbStrip'] |
576 | 576 |
577 @classmethod | 577 @classmethod |
578 def findInstances(cls, graph, board, pi): | 578 def findInstances(cls, graph, board, pi): |
579 for row in graph.query(""" | 579 for row in graph.query(""" |
580 SELECT DISTINCT ?dev ?din ?clk ?dc ?rst WHERE { | 580 SELECT DISTINCT ?dev ?din ?clk ?dc ?rst WHERE { |
581 ?dev a :Lcd8544 . | 581 ?dev a :Lcd8544 . |
603 spi=Adafruit_GPIO.SPI.BitBang( | 603 spi=Adafruit_GPIO.SPI.BitBang( |
604 Adafruit_Nokia_LCD.GPIO.RPiGPIOAdapter(RPi.GPIO), | 604 Adafruit_Nokia_LCD.GPIO.RPiGPIOAdapter(RPi.GPIO), |
605 sclk=clk, | 605 sclk=clk, |
606 mosi=din)) | 606 mosi=din)) |
607 self.lcd.begin(contrast=60) | 607 self.lcd.begin(contrast=60) |
608 | 608 |
609 def hostStatements(self): | 609 def hostStatements(self): |
610 return [] | 610 return [] |
611 return [(self.uri, ROOM['color'], Literal(self.value))] | 611 return [(self.uri, ROOM['color'], Literal(self.value))] |
612 | 612 |
613 def outputPatterns(self): | 613 def outputPatterns(self): |
614 return [] | 614 return [] |
615 return [(self.uri, ROOM['color'], None)] | 615 return [(self.uri, ROOM['color'], None)] |
616 | 616 |
617 def sendOutput(self, statements): | 617 def sendOutput(self, statements): |
622 rgb = self._rgbFromHex(statements[0][2]) | 622 rgb = self._rgbFromHex(statements[0][2]) |
623 self.value = statements[0][2] | 623 self.value = statements[0][2] |
624 | 624 |
625 for (i, v) in zip(self.rgb, rgb): | 625 for (i, v) in zip(self.rgb, rgb): |
626 self.pi.set_PWM_dutycycle(i, v) | 626 self.pi.set_PWM_dutycycle(i, v) |
627 | 627 |
628 def outputWidgets(self): | 628 def outputWidgets(self): |
629 return [] | 629 return [] |
630 return [{ | 630 return [{ |
631 'element': 'output-rgb', | 631 'element': 'output-rgb', |
632 'subj': self.uri, | 632 'subj': self.uri, |
658 for out in graph.query("""SELECT DISTINCT ?area ?chan WHERE { | 658 for out in graph.query("""SELECT DISTINCT ?area ?chan WHERE { |
659 ?dev :output [:area ?area; :channel ?chan] . | 659 ?dev :output [:area ?area; :channel ?chan] . |
660 }""", initBindings=dict(dev=row.dev), initNs={'': ROOM}): | 660 }""", initBindings=dict(dev=row.dev), initNs={'': ROOM}): |
661 outs[out.area] = out.chan.toPython() | 661 outs[out.area] = out.chan.toPython() |
662 yield cls(graph, row.dev, pi, outs) | 662 yield cls(graph, row.dev, pi, outs) |
663 | 663 |
664 def __init__(self, graph, dev, pi, outs): | 664 def __init__(self, graph, dev, pi, outs): |
665 super(PwmBoard, self).__init__(graph, dev, pi, pinNumber=None) | 665 super(PwmBoard, self).__init__(graph, dev, pi, pinNumber=None) |
666 import PCA9685 | 666 import PCA9685 |
667 self.pwm = PCA9685.PWM(pi, bus=1, address=0x40) | 667 self.pwm = PCA9685.PWM(pi, bus=1, address=0x40) |
668 self.pwm.set_frequency(1200) | 668 self.pwm.set_frequency(1200) |
681 assert statements[0][1] == ROOM['brightness']; | 681 assert statements[0][1] == ROOM['brightness']; |
682 chan = self.outs[statements[0][0]] | 682 chan = self.outs[statements[0][0]] |
683 value = float(statements[0][2]) | 683 value = float(statements[0][2]) |
684 self.values[statements[0][0]] = value | 684 self.values[statements[0][0]] = value |
685 self.pwm.set_duty_cycle(chan, value * 100) | 685 self.pwm.set_duty_cycle(chan, value * 100) |
686 | 686 |
687 def outputWidgets(self): | 687 def outputWidgets(self): |
688 return [{ | 688 return [{ |
689 'element': 'output-slider', | 689 'element': 'output-slider', |
690 'min': 0, | 690 'min': 0, |
691 'max': 1, | 691 'max': 1, |
692 'step': 1 / 255, | 692 'step': 1 / 255, |
693 'subj': area, | 693 'subj': area, |
694 'pred': ROOM['brightness'], | 694 'pred': ROOM['brightness'], |
695 } for area in self.outs] | 695 } for area in self.outs] |
696 | 696 |
697 | 697 |
698 def makeDevices(graph, board, pi): | 698 def makeDevices(graph, board, pi): |
699 out = [] | 699 out = [] |
700 for dt in sorted(_knownTypes, key=lambda cls: cls.__name__): | 700 for dt in sorted(_knownTypes, key=lambda cls: cls.__name__): |
701 out.extend(dt.findInstances(graph, board, pi)) | 701 out.extend(dt.findInstances(graph, board, pi)) |
702 return out | 702 return out |
703 |