Mercurial > code > home > repos > homeauto
comparison service/arduinoNode/devices.py @ 218:f8ffb9d8d982
multi-boards on one service, new devices, devices return their current
Ignore-this: e214852bca67519e79f9ddb3644576e1
values in the graph, jsonld support, multiple temp sensors on OW bus
author | drewp@bigasterisk.com |
---|---|
date | Sun, 03 Jan 2016 02:29:14 -0800 |
parents | 6c6897a139da |
children | 0aa54404df19 |
comparison
equal
deleted
inserted
replaced
217:163cfa384372 | 218:f8ffb9d8d982 |
---|---|
1 from __future__ import division | 1 from __future__ import division |
2 import itertools | 2 import itertools, logging, struct, os |
3 from rdflib import Namespace, RDF, URIRef, Literal | 3 from rdflib import Namespace, RDF, URIRef, Literal |
4 import time | 4 import time |
5 | 5 |
6 ROOM = Namespace('http://projects.bigasterisk.com/room/') | 6 ROOM = Namespace('http://projects.bigasterisk.com/room/') |
7 XSD = Namespace('http://www.w3.org/2001/XMLSchema#') | 7 XSD = Namespace('http://www.w3.org/2001/XMLSchema#') |
8 log = logging.getLogger() | |
8 | 9 |
9 def readLine(read): | 10 def readLine(read): |
10 buf = '' | 11 buf = '' |
11 for c in iter(lambda: read(1), '\n'): | 12 for c in iter(lambda: read(1), '\n'): |
12 buf += c | 13 buf += c |
29 ?dev a ?thisType . | 30 ?dev a ?thisType . |
30 } ORDER BY ?dev""", | 31 } ORDER BY ?dev""", |
31 initBindings=dict(board=board, | 32 initBindings=dict(board=board, |
32 thisType=cls.deviceType), | 33 thisType=cls.deviceType), |
33 initNs={'': ROOM}): | 34 initNs={'': ROOM}): |
35 log.info('found %s, a %s', row.dev, cls.deviceType) | |
34 yield cls(graph, row.dev, int(row.pinNumber)) | 36 yield cls(graph, row.dev, int(row.pinNumber)) |
35 | 37 |
36 # subclasses may add args to this | 38 # subclasses may add args to this |
37 def __init__(self, graph, uri, pinNumber): | 39 def __init__(self, graph, uri, pinNumber): |
38 self.graph, self.uri = graph, uri | 40 self.graph, self.uri = graph, uri |
39 self.pinNumber = pinNumber | 41 self.pinNumber = pinNumber |
42 self.hostStateInit() | |
43 | |
44 def hostStateInit(self): | |
45 """ | |
46 If you don't want to use __init__, you can use this to set up | |
47 whatever storage you might need for hostStatements | |
48 """ | |
40 | 49 |
41 def description(self): | 50 def description(self): |
42 return { | 51 return { |
43 'uri': self.uri, | 52 'uri': self.uri, |
44 'className': self.__class__.__name__, | 53 'className': self.__class__.__name__, |
55 input (e.g. IR receiver). | 64 input (e.g. IR receiver). |
56 Returns rdf statements. | 65 Returns rdf statements. |
57 """ | 66 """ |
58 raise NotImplementedError('readFromPoll in %s' % self.__class__) | 67 raise NotImplementedError('readFromPoll in %s' % self.__class__) |
59 | 68 |
69 def hostStatements(self): | |
70 """ | |
71 Like readFromPoll but these statements come from the host-side | |
72 python code, not the connected device. Include output state | |
73 (e.g. light brightness) if its master version is in this | |
74 object. This method is called on /graph requests so it should | |
75 be fast. | |
76 """ | |
77 return [] | |
78 | |
60 def watchPrefixes(self): | 79 def watchPrefixes(self): |
61 """ | 80 """ |
62 subj,pred pairs of the statements that might be returned from | 81 subj,pred pairs of the statements that might be returned from |
63 readFromPoll, so the dashboard knows what it should | 82 readFromPoll, so the dashboard knows what it should |
64 watch. This should be eliminated, as the dashboard should just | 83 watch. This should be eliminated, as the dashboard should just |
145 | 164 |
146 def generatePollCode(self): | 165 def generatePollCode(self): |
147 return "Serial.write('k');" | 166 return "Serial.write('k');" |
148 | 167 |
149 def readFromPoll(self, read): | 168 def readFromPoll(self, read): |
150 if read(1) != 'k': | 169 byte = read(1) |
151 raise ValueError('invalid ping response') | 170 if byte != 'k': |
171 raise ValueError('invalid ping response: chr(%s)' % ord(byte)) | |
152 return [(self.uri, ROOM['ping'], ROOM['ok'])] | 172 return [(self.uri, ROOM['ping'], ROOM['ok'])] |
153 | 173 |
154 def watchPrefixes(self): | 174 def watchPrefixes(self): |
155 return [(self.uri, ROOM['ping'])] | 175 return [(self.uri, ROOM['ping'])] |
156 | 176 |
194 (self.uri, ROOM['sees']), | 214 (self.uri, ROOM['sees']), |
195 (self.uri, ROOM['seesRecently']), | 215 (self.uri, ROOM['seesRecently']), |
196 ] | 216 ] |
197 | 217 |
198 @register | 218 @register |
219 class PushbuttonInput(DeviceType): | |
220 """add a switch to ground; we'll turn on pullup""" | |
221 deviceType = ROOM['Pushbutton'] | |
222 def generateSetupCode(self): | |
223 return 'pinMode(%(pin)d, INPUT); digitalWrite(%(pin)d, HIGH);' % { | |
224 'pin': self.pinNumber, | |
225 } | |
226 | |
227 def generatePollCode(self): | |
228 # note: pulldown means unpressed reads as a 1 | |
229 return "Serial.write(digitalRead(%(pin)d) ? '0' : '1');" % { | |
230 'pin': self.pinNumber | |
231 } | |
232 | |
233 def readFromPoll(self, read): | |
234 b = read(1) | |
235 if b not in '01': | |
236 raise ValueError('unexpected response %r' % b) | |
237 motion = b == '1' | |
238 | |
239 #and exactly once for the transition | |
240 return [ | |
241 (self.uri, ROOM['buttonState'], | |
242 ROOM['pressed'] if motion else ROOM['notPressed']), | |
243 ] | |
244 | |
245 def watchPrefixes(self): | |
246 return [ | |
247 (self.uri, ROOM['buttonState']), | |
248 ] | |
249 | |
250 @register | |
199 class OneWire(DeviceType): | 251 class OneWire(DeviceType): |
200 """ | 252 """ |
201 A OW bus with temperature sensors (and maybe other devices, which | 253 A OW bus with temperature sensors (and maybe other devices, which |
202 are also to be handled under this object) | 254 are also to be handled under this object). We return graph |
255 statements for all devices we find, even if we don't scan them, so | |
256 you can more easily add them to your config. Onewire search | |
257 happens only at device startup (not even program startup, yet). | |
258 | |
259 self.uri is a resource representing the bus. | |
260 | |
261 DS18S20 pin 1: ground, pin 2: data and pull-up with 4.7k. | |
203 """ | 262 """ |
204 deviceType = ROOM['OneWire'] | 263 deviceType = ROOM['OneWire'] |
205 | 264 def hostStateInit(self): |
265 # eliminate this as part of removing watchPrefixes | |
266 self._knownTempSubjects = set() | |
206 def generateIncludes(self): | 267 def generateIncludes(self): |
207 return ['OneWire.h', 'DallasTemperature.h'] | 268 return ['OneWire.h', 'DallasTemperature.h'] |
208 | 269 |
209 def generateArduinoLibs(self): | 270 def generateArduinoLibs(self): |
210 return ['OneWire', 'DallasTemperature'] | 271 return ['OneWire', 'DallasTemperature'] |
212 def generateGlobalCode(self): | 273 def generateGlobalCode(self): |
213 # not yet isolated to support multiple OW buses | 274 # not yet isolated to support multiple OW buses |
214 return ''' | 275 return ''' |
215 OneWire oneWire(%(pinNumber)s); | 276 OneWire oneWire(%(pinNumber)s); |
216 DallasTemperature sensors(&oneWire); | 277 DallasTemperature sensors(&oneWire); |
217 DeviceAddress tempSensorAddress; | 278 #define MAX_DEVICES 8 |
218 #define NUM_TEMPERATURE_RETRIES 2 | 279 DeviceAddress tempSensorAddress[MAX_DEVICES]; |
219 | 280 |
220 void initSensors() { | 281 void initSensors() { |
221 sensors.begin(); | 282 sensors.begin(); |
283 sensors.setResolution(12); | |
222 sensors.setWaitForConversion(false); | 284 sensors.setWaitForConversion(false); |
223 sensors.getAddress(tempSensorAddress, 0); | 285 for (uint8_t i=0; i < sensors.getDeviceCount(); ++i) { |
224 sensors.setResolution(tempSensorAddress, 9); // down from 12 to avoid flicker | 286 sensors.getAddress(tempSensorAddress[i], i); |
287 } | |
225 } | 288 } |
226 ''' % dict(pinNumber=self.pinNumber) | 289 ''' % dict(pinNumber=self.pinNumber) |
227 | 290 |
228 def generateSetupCode(self): | 291 def generateSetupCode(self): |
229 return 'initSensors();' | 292 return 'initSensors();' |
230 | 293 |
231 def generatePollCode(self): | 294 def generatePollCode(self): |
232 return r''' | 295 return r''' |
233 for (int i=0; i<NUM_TEMPERATURE_RETRIES; i++) { | |
234 sensors.requestTemperatures(); | 296 sensors.requestTemperatures(); |
235 // not waiting for conversion at all is fine- the temps will update soon | 297 |
236 //unsigned long until = millis() + 750; while(millis() < until) {idle();} | 298 // If we need frequent idle calls or fast polling again, this needs |
237 float newTemp = sensors.getTempF(tempSensorAddress); | 299 // to be changed, but it makes temp sensing work. I had a note that I |
238 idle(); | 300 // could just wait until the next cycle to get my reading, but that's |
239 if (i < NUM_TEMPERATURE_RETRIES-1 && | 301 // not working today, maybe because of a changed poll rate. |
240 (newTemp < -100 || newTemp > 180)) { | 302 sensors.setWaitForConversion(true); // ~100ms |
241 // too many errors that were fixed by restarting arduino. | 303 |
242 // trying repeating this much init | 304 Serial.write((uint8_t)sensors.getDeviceCount()); |
243 initSensors(); | 305 for (uint8_t i=0; i < sensors.getDeviceCount(); ++i) { |
244 continue; | 306 float newTemp = sensors.getTempF(tempSensorAddress[i]); |
307 | |
308 Serial.write(tempSensorAddress[i], 8); | |
309 Serial.write((uint8_t*)(&newTemp), 4); | |
245 } | 310 } |
246 Serial.print(newTemp); | |
247 idle(); | |
248 Serial.print('\n'); | |
249 idle(); | |
250 Serial.print((char)i); | |
251 idle(); | |
252 break; | |
253 } | |
254 ''' | 311 ''' |
255 | 312 |
256 def readFromPoll(self, read): | 313 def readFromPoll(self, read): |
257 newTemp = readLine(read) | 314 t1 = time.time() |
258 retries = ord(read(1)) | 315 count = ord(read(1)) |
259 # uri will change; there could (likely) be multiple connected sensors | 316 stmts = [] |
260 return [ | 317 for i in range(count): |
261 (self.uri, ROOM['temperatureF'], | 318 addr = struct.unpack('>Q', read(8))[0] |
262 Literal(newTemp, datatype=XSD['decimal'])), | 319 tempF = struct.unpack('<f', read(4))[0] |
263 (self.uri, ROOM['temperatureRetries'], Literal(retries)), | 320 sensorUri = URIRef(os.path.join(self.uri, 'dev-%s' % hex(addr)[2:])) |
264 ] | 321 stmts.extend([ |
322 (self.uri, ROOM['connectedTo'], sensorUri), | |
323 (sensorUri, ROOM['temperatureF'], Literal(tempF))]) | |
324 self._knownTempSubjects.add(sensorUri) | |
325 | |
326 log.debug("read temp in %.1fms" % ((time.time() - t1) * 1000)) | |
327 return stmts | |
265 | 328 |
266 def watchPrefixes(self): | 329 def watchPrefixes(self): |
267 # these uris will become dynamic! see note on watchPrefixes | 330 # these uris will become dynamic! see note on watchPrefixes |
268 # about eliminating it. | 331 # about eliminating it. |
269 return [(self.uri, ROOM['temperatureF']), | 332 return [(uri, ROOM['temperatureF']) for uri in self._knownTempSubjects] |
270 (self.uri, ROOM['temperatureRetries']), | |
271 ] | |
272 | 333 |
273 def byteFromFloat(f): | 334 def byteFromFloat(f): |
274 return chr(int(min(255, max(0, f * 255)))) | 335 return chr(int(min(255, max(0, f * 255)))) |
275 | 336 |
276 @register | 337 @register |
277 class LedOutput(DeviceType): | 338 class LedOutput(DeviceType): |
278 deviceType = ROOM['LedOutput'] | 339 deviceType = ROOM['LedOutput'] |
340 def hostStateInit(self): | |
341 self.value = 0 | |
342 | |
279 def generateSetupCode(self): | 343 def generateSetupCode(self): |
280 return 'pinMode(%(pin)d, OUTPUT); digitalWrite(%(pin)d, LOW);' % { | 344 return 'pinMode(%(pin)d, OUTPUT); digitalWrite(%(pin)d, LOW);' % { |
281 'pin': self.pinNumber, | 345 'pin': self.pinNumber, |
282 } | 346 } |
283 | 347 |
285 return [(self.uri, ROOM['brightness'], None)] | 349 return [(self.uri, ROOM['brightness'], None)] |
286 | 350 |
287 def sendOutput(self, statements, write, read): | 351 def sendOutput(self, statements, write, read): |
288 assert len(statements) == 1 | 352 assert len(statements) == 1 |
289 assert statements[0][:2] == (self.uri, ROOM['brightness']) | 353 assert statements[0][:2] == (self.uri, ROOM['brightness']) |
290 value = float(statements[0][2]) | 354 self.value = float(statements[0][2]) |
291 if (self.uri, RDF.type, ROOM['ActiveLowOutput']) in self.graph: | 355 if (self.uri, RDF.type, ROOM['ActiveLowOutput']) in self.graph: |
292 value = 1 - value | 356 self.value = 1 - self.value |
293 write(byteFromFloat(value)) | 357 write(byteFromFloat(self.value)) |
358 | |
359 def hostStatements(self): | |
360 return [(self.uri, ROOM['brightness'], Literal(self.value))] | |
294 | 361 |
295 def generateActionCode(self): | 362 def generateActionCode(self): |
296 return r''' | 363 return r''' |
297 while(Serial.available() < 1) NULL; | 364 while(Serial.available() < 1) NULL; |
298 analogWrite(%(pin)d, Serial.read()); | 365 analogWrite(%(pin)d, Serial.read()); |
309 }] | 376 }] |
310 | 377 |
311 @register | 378 @register |
312 class DigitalOutput(DeviceType): | 379 class DigitalOutput(DeviceType): |
313 deviceType = ROOM['DigitalOutput'] | 380 deviceType = ROOM['DigitalOutput'] |
381 def hostStateInit(self): | |
382 self.value = 0 | |
383 | |
314 def generateSetupCode(self): | 384 def generateSetupCode(self): |
315 return 'pinMode(%(pin)d, OUTPUT); digitalWrite(%(pin)d, LOW);' % { | 385 return 'pinMode(%(pin)d, OUTPUT); digitalWrite(%(pin)d, LOW);' % { |
316 'pin': self.pinNumber, | 386 'pin': self.pinNumber, |
317 } | 387 } |
318 | 388 |
320 return [(self.uri, ROOM['level'], None)] | 390 return [(self.uri, ROOM['level'], None)] |
321 | 391 |
322 def sendOutput(self, statements, write, read): | 392 def sendOutput(self, statements, write, read): |
323 assert len(statements) == 1 | 393 assert len(statements) == 1 |
324 assert statements[0][:2] == (self.uri, ROOM['level']) | 394 assert statements[0][:2] == (self.uri, ROOM['level']) |
325 value = {"high": 1, "low": 0}[str(statements[0][2])] | 395 self.value = {"high": 1, "low": 0}[str(statements[0][2])] |
326 write(chr(value)) | 396 write(chr(self.value)) |
397 | |
398 def hostStatements(self): | |
399 return [(self.uri, ROOM['level'], | |
400 Literal('high' if self.value else 'low'))] | |
327 | 401 |
328 def generateActionCode(self): | 402 def generateActionCode(self): |
329 return r''' | 403 return r''' |
330 while(Serial.available() < 1) NULL; | 404 while(Serial.available() < 1) NULL; |
331 digitalWrite(%(pin)d, Serial.read()); | 405 digitalWrite(%(pin)d, Serial.read()); |
336 'element': 'output-switch', | 410 'element': 'output-switch', |
337 'subj': self.uri, | 411 'subj': self.uri, |
338 'pred': ROOM['level'], | 412 'pred': ROOM['level'], |
339 }] | 413 }] |
340 | 414 |
415 | |
341 @register | 416 @register |
342 class PwmBoard(DeviceType): | 417 class PwmBoard(DeviceType): |
343 deviceType = ROOM['PwmBoard'] | 418 deviceType = ROOM['PwmBoard'] |
344 @classmethod | 419 @classmethod |
345 def findInstances(cls, graph, board): | 420 def findInstances(cls, graph, board): |
360 }""", initBindings=dict(dev=row.dev), initNs={'': ROOM}): | 435 }""", initBindings=dict(dev=row.dev), initNs={'': ROOM}): |
361 outs[out.area] = out.chan.toPython() | 436 outs[out.area] = out.chan.toPython() |
362 yield cls(graph, row.dev, outs=outs) | 437 yield cls(graph, row.dev, outs=outs) |
363 | 438 |
364 def __init__(self, graph, dev, outs): | 439 def __init__(self, graph, dev, outs): |
365 super(PwmBoard, self).__init__(graph, dev, pinNumber=None) | |
366 self.codeVals = {'pwm': 'pwm%s' % (hash(str(dev)) % 99999)} | 440 self.codeVals = {'pwm': 'pwm%s' % (hash(str(dev)) % 99999)} |
367 self.outs = outs | 441 self.outs = outs |
368 | 442 super(PwmBoard, self).__init__(graph, dev, pinNumber=None) |
443 | |
444 def hostStateInit(self): | |
445 self.values = {uri: 0 for uri in self.outs.keys()} # uri: brightness | |
446 | |
447 def hostStatements(self): | |
448 return [(uri, ROOM['brightness'], Literal(b)) | |
449 for uri, b in self.values.items()] | |
450 | |
369 def generateIncludes(self): | 451 def generateIncludes(self): |
370 return ['Wire.h', 'Adafruit_PWMServoDriver.h'] | 452 return ['Wire.h', 'Adafruit_PWMServoDriver.h'] |
371 | 453 |
372 def generateArduinoLibs(self): | 454 def generateArduinoLibs(self): |
373 return ['Wire', 'Adafruit-PWM-Servo-Driver-Library'] | 455 return ['Wire', 'Adafruit-PWM-Servo-Driver-Library'] |
398 def sendOutput(self, statements, write, read): | 480 def sendOutput(self, statements, write, read): |
399 assert len(statements) == 1 | 481 assert len(statements) == 1 |
400 assert statements[0][1] == ROOM['brightness']; | 482 assert statements[0][1] == ROOM['brightness']; |
401 chan = self.outs[statements[0][0]] | 483 chan = self.outs[statements[0][0]] |
402 value = float(statements[0][2]) | 484 value = float(statements[0][2]) |
485 self.values[statements[0][0]] = value | |
403 v12 = int(min(4095, max(0, value * 4095))) | 486 v12 = int(min(4095, max(0, value * 4095))) |
404 write(chr(chan) + chr(v12 >> 8) + chr(v12 & 0xff)) | 487 write(chr(chan) + chr(v12 >> 8) + chr(v12 & 0xff)) |
405 | 488 |
406 def outputWidgets(self): | 489 def outputWidgets(self): |
407 return [{ | 490 return [{ |
436 yield cls(graph, dev, connections=connections) | 519 yield cls(graph, dev, connections=connections) |
437 | 520 |
438 def __init__(self, graph, dev, connections): | 521 def __init__(self, graph, dev, connections): |
439 super(ST7576Lcd, self).__init__(graph, dev, pinNumber=None) | 522 super(ST7576Lcd, self).__init__(graph, dev, pinNumber=None) |
440 self.connections = connections | 523 self.connections = connections |
524 self.text = '' | |
441 | 525 |
442 def generateIncludes(self): | 526 def generateIncludes(self): |
443 return ['ST7565.h'] | 527 return ['ST7565.h'] |
444 | 528 |
445 def generateArduinoLibs(self): | 529 def generateArduinoLibs(self): |
470 return [(self.uri, ROOM['text'], None)] | 554 return [(self.uri, ROOM['text'], None)] |
471 | 555 |
472 def sendOutput(self, statements, write, read): | 556 def sendOutput(self, statements, write, read): |
473 assert len(statements) == 1 | 557 assert len(statements) == 1 |
474 assert statements[0][:2] == (self.uri, ROOM['text']) | 558 assert statements[0][:2] == (self.uri, ROOM['text']) |
475 value = str(statements[0][2]) | 559 self.text = str(statements[0][2]) |
476 assert len(value) < 254, repr(value) | 560 assert len(self.text) < 254, repr(self.text) |
477 write(chr(len(value)) + value) | 561 write(chr(len(self.text)) + self.text) |
478 | 562 |
563 def hostStatements(self): | |
564 return [(self.uri, ROOM['text'], Literal(self.text))] | |
565 | |
479 def outputWidgets(self): | 566 def outputWidgets(self): |
480 return [{ | 567 return [{ |
481 'element': 'output-fixed-text', | 568 'element': 'output-fixed-text', |
482 'cols': 21, | 569 'cols': 21, |
483 'rows': 8, | 570 'rows': 8, |
499 glcd.clear(); | 586 glcd.clear(); |
500 glcd.drawstring(0,0, newtxt); | 587 glcd.drawstring(0,0, newtxt); |
501 glcd.display(); | 588 glcd.display(); |
502 ''' | 589 ''' |
503 | 590 |
591 @register | |
592 class RgbPixels(DeviceType): | |
593 """chain of FastLED-controllable rgb pixels""" | |
594 deviceType = ROOM['RgbPixels'] | |
595 | |
596 def __init__(self, graph, uri, pinNumber): | |
597 super(RgbPixels, self).__init__(graph, uri, pinNumber) | |
598 px = graph.value(self.uri, ROOM['pixels']) | |
599 self.pixelUris = list(graph.items(px)) | |
600 self.values = dict((uri, Literal('#000000')) for uri in self.pixelUris) | |
601 self.replace = {'ledArray': 'leds_%s' % self.pinNumber, | |
602 'ledCount': len(self.pixelUris), | |
603 'pin': self.pinNumber, | |
604 'ledType': 'WS2812', | |
605 } | |
606 | |
607 def generateIncludes(self): | |
608 """filenames of .h files to #include""" | |
609 return ['FastLED.h'] | |
610 | |
611 def generateArduinoLibs(self): | |
612 """names of libraries for the ARDUINO_LIBS line in the makefile""" | |
613 return ['FastLED-3.1.0'] | |
614 | |
615 def myId(self): | |
616 return 'rgb_%s' % self.pinNumber | |
617 | |
618 def generateGlobalCode(self): | |
619 return 'CRGB {ledArray}[{ledCount}];'.format(**self.replace) | |
620 | |
621 def generateSetupCode(self): | |
622 return 'FastLED.addLeds<{ledType}, {pin}>({ledArray}, {ledCount});'.format(**self.replace) | |
623 | |
624 def _rgbFromHex(self, h): | |
625 rrggbb = h.lstrip('#') | |
626 return [int(x, 16) for x in [rrggbb[0:2], rrggbb[2:4], rrggbb[4:6]]] | |
627 | |
628 def sendOutput(self, statements, write, read): | |
629 px, pred, color = statements[0] | |
630 if pred != ROOM['color']: | |
631 raise ValueError(pred) | |
632 rgb = self._rgbFromHex(color) | |
633 if px not in self.values: | |
634 raise ValueError(px) | |
635 self.values[px] = Literal(color) | |
636 write(chr(self.pixelUris.index(px)) + | |
637 chr(rgb[1]) + # my WS2812 need these flipped | |
638 chr(rgb[0]) + | |
639 chr(rgb[2])) | |
640 | |
641 def hostStatements(self): | |
642 return [(uri, ROOM['color'], hexCol) | |
643 for uri, hexCol in self.values.items()] | |
644 | |
645 def outputPatterns(self): | |
646 return [(px, ROOM['color'], None) for px in self.pixelUris] | |
647 | |
648 def generateActionCode(self): | |
649 | |
650 return ''' | |
651 | |
652 while(Serial.available() < 1) NULL; | |
653 byte id = Serial.read(); | |
654 | |
655 while(Serial.available() < 1) NULL; | |
656 byte r = Serial.read(); | |
657 | |
658 while(Serial.available() < 1) NULL; | |
659 byte g = Serial.read(); | |
660 | |
661 while(Serial.available() < 1) NULL; | |
662 byte b = Serial.read(); | |
663 | |
664 {ledArray}[id] = CRGB(r, g, b); FastLED.show(); | |
665 | |
666 '''.format(**self.replace) | |
667 | |
668 def outputWidgets(self): | |
669 return [{ | |
670 'element': 'output-rgb', | |
671 'subj': px, | |
672 'pred': ROOM['color'], | |
673 } for px in self.pixelUris] | |
674 | |
504 def makeDevices(graph, board): | 675 def makeDevices(graph, board): |
505 out = [] | 676 out = [] |
506 for dt in sorted(_knownTypes, key=lambda cls: cls.__name__): | 677 for dt in sorted(_knownTypes, key=lambda cls: cls.__name__): |
507 out.extend(dt.findInstances(graph, board)) | 678 out.extend(dt.findInstances(graph, board)) |
508 return out | 679 return out |