comparison service/frontDoorLock/front_door_lock.py @ 1366:5836e88a0287

py3 and new build Ignore-this: 69cf69e5d43adfa65b3f62c5c0af2014 darcs-hash:a82563e9bfce399590ffd2229d997b490c0b038d
author drewp <drewp@bigasterisk.com>
date Wed, 01 May 2019 00:30:54 -0700
parents e6c6574f3d24
children 2d8a5532f1b5
comparison
equal deleted inserted replaced
1365:e6c6574f3d24 1366:5836e88a0287
4 4
5 put :frontDoorLock :state ?s to this /output to request a change. 5 put :frontDoorLock :state ?s to this /output to request a change.
6 6
7 reasoning can infer :frontDoorLock :putState ?s to do that put request. 7 reasoning can infer :frontDoorLock :putState ?s to do that put request.
8 """ 8 """
9 import time, json
10
9 from docopt import docopt 11 from docopt import docopt
10 from patchablegraph import PatchableGraph, CycloneGraphHandler, CycloneGraphEventsHandler
11 from rdflib import Namespace, URIRef, Literal, Graph 12 from rdflib import Namespace, URIRef, Literal, Graph
12 from rdflib.parser import StringInputSource 13 from rdflib.parser import StringInputSource
13 from twisted.internet import reactor, task 14 from twisted.internet import reactor, task
14 import cyclone.web 15 import cyclone.web
15 import logging, time, json 16
16 from mqtt_client import MqttClient 17 from mqtt_client import MqttClient
17 from logsetup import log, enableTwistedLog 18 from patchablegraph import PatchableGraph, CycloneGraphHandler, CycloneGraphEventsHandler
19 from standardservice.logsetup import log, verboseLogging
18 20
19 ROOM = Namespace('http://projects.bigasterisk.com/room/') 21 ROOM = Namespace('http://projects.bigasterisk.com/room/')
20 22
21 ctx = ROOM['frontDoorControl'] 23 ctx = ROOM['frontDoorControl']
22 24
23 def rdfGraphBody(body, headers): 25 def rdfGraphBody(body, headers):
24 g = Graph() 26 g = Graph()
25 g.parse(StringInputSource(body), format='nt') 27 g.parse(StringInputSource(body), format='nt')
26 return g 28 return g
27 29
28 def mqttMessageFromState(state): 30 def mqttMessageFromState(state: URIRef):
29 return { 31 return {
30 ROOM['locked']: b'OFF', 32 ROOM['locked']: b'OFF',
31 ROOM['unlocked']: b'ON', 33 ROOM['unlocked']: b'ON',
32 }[state] 34 }[state]
33 35
34 def stateFromMqtt(msg): 36 def stateFromMqtt(msg: bytes):
35 return { 37 return {
36 'OFF': ROOM['locked'], 38 b'OFF': ROOM['locked'],
37 'ON': ROOM['unlocked'], 39 b'ON': ROOM['unlocked'],
38 }[msg.decode('ascii')] 40 }[bytes(msg)]
39 41
40 class OutputPage(cyclone.web.RequestHandler): 42 class OutputPage(cyclone.web.RequestHandler):
41 def put(self): 43 def put(self):
42 try: 44 try:
43 user = URIRef(self.request.headers['x-foaf-agent']) 45 # what happened to the case-insens dict?
46 h = dict((k.lower(), v) for k,v in self.request.headers.items())
47 user = URIRef(h['x-foaf-agent'])
44 except KeyError: 48 except KeyError:
45 log.warn('request without x-foaf-agent: %s', self.request.headers) 49 log.warn('request without x-foaf-agent: %s', h)
46 self.set_status(403, 'need x-foaf-agent') 50 self.set_status(403, 'need x-foaf-agent')
47 return 51 return
48 arg = self.request.arguments 52 arg = self.request.arguments
49 if arg.get('s') and arg.get('p'): 53 if arg.get('s') and arg.get('p'):
50 subj = URIRef(arg['s'][-1]) 54 subj = URIRef(arg['s'][-1])
51 pred = URIRef(arg['p'][-1]) 55 pred = URIRef(arg['p'][-1])
52 obj = URIRef(self.request.body) 56 obj = URIRef(self.request.body.strip().decode('ascii'))
53 stmt = (subj, pred, obj) 57 stmt = (subj, pred, obj)
54 else: 58 else:
55 g = rdfGraphBody(self.request.body, self.request.headers) 59 g = rdfGraphBody(self.request.body, self.request.headers)
56 assert len(g) == 1, len(g) 60 assert len(g) == 1, len(g)
57 stmt = g.triples((None, None, None)).next() 61 stmt = next(g.triples((None, None, None)))
58 self._onStatement(user, stmt) 62 self._onStatement(user, stmt)
59 post = put 63 post = put
60 64
61 def _onStatement(self, user, stmt): 65 def _onStatement(self, user, stmt):
62 log.info('put statement %r', stmt) 66 log.info('put statement %r', stmt)
64 if stmt[2] == ROOM['unlocked']: 68 if stmt[2] == ROOM['unlocked']:
65 log.info('unlock for %r', user) 69 log.info('unlock for %r', user)
66 self.settings.autoLock.onUnlockedStmt() 70 self.settings.autoLock.onUnlockedStmt()
67 if stmt[2] == ROOM['locked']: 71 if stmt[2] == ROOM['locked']:
68 self.settings.autoLock.onLockedStmt() 72 self.settings.autoLock.onLockedStmt()
69 self.settings.mqtt.publish("frontdoor/switch/strike/command", 73 self.settings.mqtt.publish(b"frontdoor/switch/strike/command",
70 mqttMessageFromState(stmt[2])) 74 mqttMessageFromState(stmt[2]))
71 return 75 return
72 log.warn("ignoring %s", stmt) 76 log.warn("ignoring %s", stmt)
73 77
74 78
81 self.subj = ROOM['frontDoorLock'] 85 self.subj = ROOM['frontDoorLock']
82 task.LoopingCall(self.pollCheck).start(1) 86 task.LoopingCall(self.pollCheck).start(1)
83 87
84 def relock(self): 88 def relock(self):
85 log.info('autolock is up: requesting lock') 89 log.info('autolock is up: requesting lock')
86 self.mqtt.publish("frontdoor/switch/strike/command", 90 self.mqtt.publish(b"frontdoor/switch/strike/command",
87 mqttMessageFromState(ROOM['locked'])) 91 mqttMessageFromState(ROOM['locked']))
88 92
89 def reportTimes(self, unlockedFor): 93 def reportTimes(self, unlockedFor):
90 g = self.masterGraph 94 g = self.masterGraph
91 lockIn = self.autoLockSec - int(unlockedFor) 95 lockIn = self.autoLockSec - int(unlockedFor)
142 def post(self): 146 def post(self):
143 body = json.loads(self.request.body) 147 body = json.loads(self.request.body)
144 log.info('POST bluetoothButton %r', body) 148 log.info('POST bluetoothButton %r', body)
145 if body['addr'] == 'zz:zz:zz:zz:zz:zz' and body['key'] == 'top': 149 if body['addr'] == 'zz:zz:zz:zz:zz:zz' and body['key'] == 'top':
146 log.info('unlock for %r', body['addr']) 150 log.info('unlock for %r', body['addr'])
147 self.settings.mqtt.publish("frontdoor/switch/strike/command", 'ON') 151 self.settings.mqtt.publish(
152 b"frontdoor/switch/strike/command", b'ON')
148 153
149 154
150 if __name__ == '__main__': 155 if __name__ == '__main__':
151 arg = docopt(""" 156 arg = docopt("""
152 Usage: front_door_lock.py [options] 157 Usage: front_door_lock.py [options]
153 158
154 -v Verbose 159 -v Verbose
155 """) 160 """)
156 log.setLevel(logging.INFO) 161 verboseLogging(arg['-v'])
157 if arg['-v']:
158 enableTwistedLog()
159 log.setLevel(logging.DEBUG)
160 162
161 masterGraph = PatchableGraph() 163 masterGraph = PatchableGraph()
162 mqtt = MqttClient(brokerPort=10010) 164 mqtt = MqttClient(brokerPort=10010)
163 autoclose = AutoLock(masterGraph, mqtt) 165 autoclose = AutoLock(masterGraph, mqtt)
164 166
165 def toGraph(payload): 167 def toGraph(payload):
166 log.info('mqtt->graph %r', payload) 168 log.info('mqtt->graph %r', payload)
167 masterGraph.patchObject(ctx, ROOM['frontDoorLock'], ROOM['state'], 169 masterGraph.patchObject(ctx, ROOM['frontDoorLock'], ROOM['state'],
168 stateFromMqtt(payload)) 170 stateFromMqtt(payload))
169 171
170 mqtt.subscribe("frontdoor/switch/strike/state").subscribe(on_next=toGraph) 172 mqtt.subscribe(b"frontdoor/switch/strike/state").subscribe(on_next=toGraph)
171 port = 10011 173 port = 10011
172 reactor.listenTCP(port, cyclone.web.Application( 174 reactor.listenTCP(port, cyclone.web.Application(
173 [ 175 [
174 (r"/()", cyclone.web.StaticFileHandler, 176 (r"/()", cyclone.web.StaticFileHandler,
175 {"path": ".", "default_filename": "index.html"}), 177 {"path": ".", "default_filename": "index.html"}),