987
|
1 from __future__ import division
|
|
2 import pigpio
|
|
3 import time
|
|
4 from rdflib import Namespace, RDF, URIRef, Literal
|
|
5
|
|
6 ROOM = Namespace('http://projects.bigasterisk.com/room/')
|
|
7
|
|
8 class DeviceType(object):
|
|
9 deviceType = None
|
|
10 @classmethod
|
|
11 def findInstances(cls, graph, board, pi):
|
|
12 """
|
|
13 return any number of instances of this class for all the separately
|
|
14 controlled devices on the board. Two LEDS makes two instances,
|
|
15 but two sensors on the same onewire bus makes only one device
|
|
16 (which yields more statements).
|
|
17 """
|
|
18 for row in graph.query("""SELECT ?dev ?pinNumber WHERE {
|
|
19 ?board :hasPin ?pin .
|
|
20 ?pin :pinNumber ?pinNumber;
|
|
21 :connectedTo ?dev .
|
|
22 ?dev a ?thisType .
|
|
23 } ORDER BY ?dev""",
|
|
24 initBindings=dict(board=board,
|
|
25 thisType=cls.deviceType),
|
|
26 initNs={'': ROOM}):
|
|
27 yield cls(graph, row.dev, pi, int(row.pinNumber))
|
|
28
|
|
29 def __init__(self, graph, uri, pi, pinNumber):
|
|
30 self.graph, self.uri, self.pi = graph, uri, pi
|
|
31 self.pinNumber = pinNumber
|
|
32
|
|
33 def description(self):
|
|
34 return {
|
|
35 'uri': self.uri,
|
|
36 'className': self.__class__.__name__,
|
|
37 'pinNumber': self.pinNumber,
|
|
38 'outputPatterns': self.outputPatterns(),
|
|
39 'watchPrefixes': self.watchPrefixes(),
|
|
40 'outputWidgets': self.outputWidgets(),
|
|
41 }
|
|
42
|
|
43 def watchPrefixes(self):
|
|
44 """
|
|
45 subj,pred pairs of the statements that might be returned from
|
|
46 readFromPoll, so the dashboard knows what it should
|
|
47 watch. This should be eliminated, as the dashboard should just
|
|
48 always watch the whole tree of statements starting self.uri
|
|
49 """
|
|
50 return []
|
|
51
|
|
52 def poll(self):
|
|
53 return [] # statements
|
|
54
|
|
55 def outputPatterns(self):
|
|
56 """
|
|
57 Triple patterns, using None as a wildcard, that should be routed
|
|
58 to sendOutput
|
|
59 """
|
|
60 return []
|
|
61
|
|
62 def outputWidgets(self):
|
|
63 """
|
|
64 structs to make output widgets on the dashboard. ~1 of these per
|
|
65 handler you have in sendOutput
|
|
66 """
|
|
67 return []
|
|
68
|
|
69 def sendOutput(self, statements):
|
|
70 """
|
|
71 If we got statements that match this class's outputPatterns, this
|
|
72 will be called with the statements that matched.
|
|
73
|
|
74 Todo: it would be fine to read back confirmations or
|
|
75 whatever. Just need a way to collect them into graph statements.
|
|
76 """
|
|
77 raise NotImplementedError
|
|
78
|
|
79 _knownTypes = set()
|
|
80 def register(deviceType):
|
|
81 _knownTypes.add(deviceType)
|
|
82 return deviceType
|
|
83
|
|
84 @register
|
|
85 class MotionSensorInput(DeviceType):
|
|
86 deviceType = ROOM['MotionSensor']
|
|
87
|
|
88 def setup(self):
|
|
89 self.pi.set_mode(17, pigpio.INPUT)
|
|
90 self.pi.set_pull_up_down(17, pigpio.PUD_DOWN)
|
|
91
|
|
92 def poll(self):
|
|
93 motion = self.pi.read(17)
|
|
94
|
|
95 return [
|
|
96 (self.uri, ROOM['sees'],
|
|
97 ROOM['motion'] if motion else ROOM['noMotion']),
|
|
98 self.recentMotionStatement(motion),
|
|
99 ]
|
|
100
|
|
101 def recentMotionStatement(self, motion):
|
|
102 if not hasattr(self, 'lastMotionTime'):
|
|
103 self.lastMotionTime = 0
|
|
104 now = time.time()
|
|
105 if motion:
|
|
106 self.lastMotionTime = now
|
|
107 recentMotion = now - self.lastMotionTime < 60 * 10
|
|
108 return (self.uri, ROOM['seesRecently'],
|
|
109 ROOM['motion'] if recentMotion else ROOM['noMotion'])
|
|
110
|
|
111 def watchPrefixes(self):
|
|
112 return [
|
|
113 (self.uri, ROOM['sees']),
|
|
114 (self.uri, ROOM['seesRecently']),
|
|
115 ]
|
|
116
|
|
117 def makeDevices(graph, board, pi):
|
|
118 out = []
|
|
119 for dt in sorted(_knownTypes, key=lambda cls: cls.__name__):
|
|
120 out.extend(dt.findInstances(graph, board, pi))
|
|
121 return out
|
|
122
|