Mercurial > code > home > repos > homeauto
changeset 1229:02e4b84821d5
talk to store graph, second button for holding unlocked, etc
Ignore-this: c2ae7d756e743c26e5e01d99772899bd
darcs-hash:a0750d0bbc4dc7c0f65f63f3e7342b35a175141b
author | drewp <drewp@bigasterisk.com> |
---|---|
date | Thu, 04 Apr 2019 02:16:22 -0700 |
parents | a72e9245cc72 |
children | ec0db53aa833 |
files | service/frontDoorLock/front_door_lock.py service/frontDoorLock/index.html service/frontDoorLock/makefile service/frontDoorLock/requirements.txt |
diffstat | 4 files changed, 126 insertions(+), 27 deletions(-) [+] |
line wrap: on
line diff
--- a/service/frontDoorLock/front_door_lock.py Thu Apr 04 02:14:48 2019 -0700 +++ b/service/frontDoorLock/front_door_lock.py Thu Apr 04 02:16:22 2019 -0700 @@ -12,7 +12,7 @@ from rdflib.parser import StringInputSource from twisted.internet import reactor, task import cyclone.web -import sys, logging, time +import logging, time, json from mqtt_client import MqttClient from logsetup import log, enableTwistedLog @@ -39,6 +39,12 @@ class OutputPage(cyclone.web.RequestHandler): def put(self): + try: + user = URIRef(self.request.headers['x-foaf-agent']) + except KeyError: + log.warn('request without x-foaf-agent: %s', self.request.headers) + self.set_status(403, 'need x-foaf-agent') + return arg = self.request.arguments if arg.get('s') and arg.get('p'): subj = URIRef(arg['s'][-1]) @@ -49,11 +55,17 @@ g = rdfGraphBody(self.request.body, self.request.headers) assert len(g) == 1, len(g) stmt = g.triples((None, None, None)).next() - self._onStatement(stmt) + self._onStatement(user, stmt) post = put - def _onStatement(self, stmt): + def _onStatement(self, user, stmt): + log.info('put statement %r', stmt) if stmt[0:2] == (ROOM['frontDoorLock'], ROOM['state']): + if stmt[2] == ROOM['unlocked']: + log.info('unlock for %r', user) + self.settings.autoLock.onUnlockedStmt() + if stmt[2] == ROOM['locked']: + self.settings.autoLock.onLockedStmt() self.settings.mqtt.publish("frontdoor/switch/strike/command", mqttMessageFromState(stmt[2])) return @@ -65,24 +77,44 @@ self.masterGraph = masterGraph self.mqtt = mqtt self.timeUnlocked = None - self.autoLockSec = 5 + self.autoLockSec = 6 self.subj = ROOM['frontDoorLock'] task.LoopingCall(self.check).start(1) + def relock(self): + log.info('autolock is up: requesting lock') + self.mqtt.publish("frontdoor/switch/strike/command", + mqttMessageFromState(ROOM['locked'])) + + def reportTimes(self, unlockedFor): + g = self.masterGraph + lockIn = self.autoLockSec - int(unlockedFor) + if lockIn < 0: + tu = self.timeUnlocked + log.warn("timeUnlocked %(tu)r, state %(state)s, " + "unlockedFor %(unlockedFor)r, lockIn %(lockIn)r", vars()) + lockIn = 0 + g.patchObject(ctx, self.subj, ROOM['unlockedForSec'], + Literal(int(unlockedFor))) + g.patchObject(ctx, self.subj, ROOM['autoLockInSec'], + Literal(lockIn)) + + def clearReport(self): + g = self.masterGraph + g.patchObject(ctx, self.subj, ROOM['unlockedForSec'], None) + g.patchObject(ctx, self.subj, ROOM['autoLockInSec'], None) + def check(self): + g = self.masterGraph now = time.time() - state = self.masterGraph._graph.value(self.subj, ROOM['state']) + state = g._graph.value(self.subj, ROOM['state']) if state == ROOM['unlocked']: if self.timeUnlocked is None: self.timeUnlocked = now + # *newly* unlocked- this resets on every input stmt unlockedFor = now - self.timeUnlocked - self.masterGraph.patchObject(ctx, self.subj, ROOM['unlockedForSec'], - Literal(int(unlockedFor))) - self.masterGraph.patchObject(ctx, self.subj, ROOM['autoLockInSec'], - Literal(self.autoLockSec - int(unlockedFor))) if unlockedFor > self.autoLockSec: - self.mqtt.publish("frontdoor/switch/strike/command", - mqttMessageFromState(ROOM['locked'])) + self.relock() else: self.timeUnlocked = None self.masterGraph.patchObject(ctx, self.subj, ROOM['unlockedForSec'], None) @@ -111,14 +143,21 @@ mqtt.subscribe("frontdoor/switch/strike/state").subscribe(on_next=toGraph) port = 10011 - reactor.listenTCP(port, cyclone.web.Application([ - (r"/()", cyclone.web.StaticFileHandler, - {"path": ".", "default_filename": "index.html"}), - (r"/graph", CycloneGraphHandler, {'masterGraph': masterGraph}), - (r"/graph/events", CycloneGraphEventsHandler, - {'masterGraph': masterGraph}), - (r'/output', OutputPage), - ], mqtt=mqtt, masterGraph=masterGraph, debug=arg['-v']), interface='::') + reactor.listenTCP(port, cyclone.web.Application( + [ + (r"/()", cyclone.web.StaticFileHandler, + {"path": ".", "default_filename": "index.html"}), + (r"/graph", CycloneGraphHandler, {'masterGraph': masterGraph}), + (r"/graph/events", CycloneGraphEventsHandler, + {'masterGraph': masterGraph}), + (r'/output', OutputPage), + (r'/bluetoothButton', BluetoothButton), + ], + mqtt=mqtt, + masterGraph=masterGraph, + autoLock=autoclose, + debug=arg['-v']), + interface='::') log.warn('serving on %s', port) reactor.run()
--- a/service/frontDoorLock/index.html Thu Apr 04 02:14:48 2019 -0700 +++ b/service/frontDoorLock/index.html Thu Apr 04 02:16:22 2019 -0700 @@ -58,6 +58,7 @@ <template> <div> <streamed-graph url="graph/events" graph="{{graph}}"></streamed-graph> + <streamed-graph url="/store/graph/events" graph="{{storeGraph}}"></streamed-graph> </div> <div id="form"> @@ -73,13 +74,42 @@ predicate="room:state" object="room:unlocked" ></rdf-oneshot> - <button on-click="unlock">Unlock</button> + <button on-click="unlock" + disabled$="[[!isLocked]]">Unlock 10 seconds</button> - <template is="dom-if" if="{{autoLockIsComing}}"> - <div> + <div class$="invis-[[!autoLockIsComing]]"> Locking in {{autoLockInSec}} </div> - </template> + + <div> + <rdf-oneshot + id="hold" + post="/store/values" + subject="room:frontDoorLockRequest" + predicate="room:state" + object="room:unlocked" + ></rdf-oneshot> + <template is="dom-if" if="{{!isHeld}}"> + <button on-click="hold">Hold unlocked</button> + </template> + <rdf-oneshot + id="releaseHold" + post="/store/values" + subject="room:frontDoorLockRequest" + predicate="room:state" + object="room:unset" + ></rdf-oneshot> + <rdf-oneshot + id="lockNow" + post="output" + subject="room:frontDoorLock" + predicate="room:state" + object="room:locked" + ></rdf-oneshot> + <template is="dom-if" if="{{isHeld}}"> + <button on-click="releaseHold">Release hold; lock door</button> + </template> + </div> </div> </template> <script> @@ -88,20 +118,26 @@ is: 'door-control', properties: { graph: { type: Object, notify: true, observer: "_onGraph" }, + storeGraph: { type: Object, notify: true, observer: "_onStoreGraph" }, lockState: { type: String }, autoLockIsComing: { type: Boolean }, autoLockInSec: { type: String}, + isHeld: { type: Boolean }, }, behaviors: [BigastUri], _onGraph: function(graph) { if (!graph.graph) return; const env = graph.graph.store.rdf; + const unlocked = env.createNamedNode('room:unlocked'); + const locked = env.createNamedNode('room:locked'); + this.isLocked = null; graph.graph.quadStore.quads( {subject: env.createNamedNode('room:frontDoorLock'), predicate: env.createNamedNode('room:state'), }, (q) => { this.lockState = q.object.toString().replace(/.*\//, ''); + this.isLocked = q.object.equals(locked) ? true : (q.object.equals(unlocked) ? false : null); }); this.autoLockIsComing = false; @@ -112,11 +148,32 @@ (q) => { this.autoLockIsComing = true; this.autoLockInSec = parseFloat(q.object.valueOf()); + }); + }, + _onStoreGraph: function(graph) { + if (!graph.graph) return; + const env = graph.graph.store.rdf; + const unlocked = env.createNamedNode('room:unlocked'); + const locked = env.createNamedNode('room:locked'); + this.isHeld = false; + graph.graph.quadStore.quads( + {subject: env.createNamedNode('room:frontDoorLockRequest'), + predicate: env.createNamedNode('room:state'), + }, + (q) => { + this.isHeld = q.object.equals(unlocked); }); }, unlock: function() { this.$.unlockOneshot.go(); - } + }, + hold: function () { + this.$.hold.go(); + }, + releaseHold: function() { + this.$.releaseHold.go(); + this.$.lockNow.go(); // may race with releaseHold? + }, }); }); </script>
--- a/service/frontDoorLock/makefile Thu Apr 04 02:14:48 2019 -0700 +++ b/service/frontDoorLock/makefile Thu Apr 04 02:16:22 2019 -0700 @@ -15,5 +15,8 @@ shell: docker run --rm -it --cap-add SYS_PTRACE --net=host bang6:5000/$(JOB)_x86:latest /bin/sh -local_run: - docker run --rm -it --net=host -v `pwd`/index.html:/opt/index.html bang6:5000/$(JOB)_x86:latest python ./front_door_lock.py +local_run: build_image + docker run --rm -it --net=host -v `pwd`/index.html:/opt/index.html bang6:5000/$(JOB)_x86:latest python ./front_door_lock.py -v + +redeploy: build_image + supervisorctl restart $(JOB)_$(PORT)
--- a/service/frontDoorLock/requirements.txt Thu Apr 04 02:14:48 2019 -0700 +++ b/service/frontDoorLock/requirements.txt Thu Apr 04 02:16:22 2019 -0700 @@ -2,5 +2,5 @@ rdflib-jsonld==0.4.0 rdflib==4.2.2 twisted-mqtt==0.3.6 -https://projects.bigasterisk.com/rdfdb/rdfdb-0.6.0.tar.gz +https://projects.bigasterisk.com/rdfdb/rdfdb-0.7.0.tar.gz rx==1.6.1