Mercurial > code > home > repos > homeauto
changeset 392:79d041273e26
mqtt has two devices now. various older cleanups.
Ignore-this: 67ca3acc5dc6aa672d0c896c9f5ae48e
author | drewp@bigasterisk.com |
---|---|
date | Sat, 19 Jan 2019 12:08:59 -0800 |
parents | 7ab6f4c248c8 |
children | 5fc79536885a |
files | service/mqtt_graph_bridge/makefile service/mqtt_graph_bridge/mqtt_graph_bridge.py service/reasoning/actions.py service/reasoning/input/startup.n3 service/reasoning/inputgraph.py service/reasoning/makefile service/reasoning/oneShot service/reasoning/reasoning.py service/reasoning/requirements.txt service/reasoning/rules.n3 |
diffstat | 10 files changed, 126 insertions(+), 115 deletions(-) [+] |
line wrap: on
line diff
--- a/service/mqtt_graph_bridge/makefile Sat Jan 19 10:44:10 2019 -0800 +++ b/service/mqtt_graph_bridge/makefile Sat Jan 19 12:08:59 2019 -0800 @@ -12,8 +12,8 @@ rm -rf tmp_ctx -shell: +shell: build_image docker run --rm -it --cap-add SYS_PTRACE --net=host bang6:5000/mqtt_graph_bridge_x86:latest /bin/sh -local_run: +local_run: build_image docker run --rm -it --net=host bang6:5000/mqtt_graph_bridge_x86:latest
--- a/service/mqtt_graph_bridge/mqtt_graph_bridge.py Sat Jan 19 10:44:10 2019 -0800 +++ b/service/mqtt_graph_bridge/mqtt_graph_bridge.py Sat Jan 19 12:08:59 2019 -0800 @@ -4,13 +4,14 @@ from rdflib.parser import StringInputSource from twisted.internet import reactor import cyclone.web -import sys, logging +import sys, logging, json from mqtt_client import MqttClient ROOM = Namespace('http://projects.bigasterisk.com/room/') devs = { - ROOM['kitchenLight']: {'root': '004BD965', 'ctx': ROOM['kitchenH801']} + ROOM['kitchenLight']: {'root': 'h801_skylight', 'ctx': ROOM['kitchenH801']}, + ROOM['kitchenCounterLight']: {'root': 'h801_counter', 'ctx': ROOM['kitchenH801']}, } logging.basicConfig() @@ -40,16 +41,16 @@ self._onStatement(stmt) def _onStatement(self, stmt): + ignored = True for dev, attrs in devs.items(): if stmt[0:2] == (dev, ROOM['brightness']): - sw = 'OFF' if stmt[2].toPython() == 0 else 'ON' - self.settings.mqtt.publish("%s/w1/light/switch" % attrs['root'], sw) - self.settings.mqtt.publish("%s/rgb/rgb/set" % attrs['root'], - '200,255,200' if sw == 'ON' else '0,0,0') + self.settings.mqtt.publish("%s/light/kit_w1/command" % attrs['root'], + json.dumps({'state': 'ON', 'brightness': int(stmt[2].toPython() * 255)})) self.settings.masterGraph.patchObject(attrs['ctx'], stmt[0], stmt[1], stmt[2]) - return - log.warn("ignoring %s", stmt) + ignored = False + if ignored: + log.warn("ignoring %s", stmt) if __name__ == '__main__': arg = docopt("""
--- a/service/reasoning/actions.py Sat Jan 19 10:44:10 2019 -0800 +++ b/service/reasoning/actions.py Sat Jan 19 12:08:59 2019 -0800 @@ -26,45 +26,43 @@ If the graph doesn't contain any matches, we use (?d :zeroValue ?val) for the value and PUT that. """ - + self._putDevices(deviceGraph, inferred) self._oneShotPostActions(deviceGraph, inferred) for dev, pred in [ - # the config of each putUrl should actually be in the - # context of a dev and predicate pair, and then that would - # be the source of this list - #(DEV.theaterDoorLock, ROOM.state), #(URIRef('http://bigasterisk.com/host/bang/monitor'), ROOM.powerState), (URIRef('http://bigasterisk.com/host/dash/monitor'), ROOM.powerState), + (URIRef('http://bigasterisk.com/host/frontdoor/monitor'), ROOM.powerState), (ROOM['storageCeilingLedLong'], ROOM.brightness), (ROOM['storageCeilingLedCross'], ROOM.brightness), + (ROOM['garageOverhead'], ROOM.brightness), (ROOM['headboardWhite'], ROOM.brightness), (ROOM['changingWhite'], ROOM.brightness), (ROOM['starTrekLight'], ROOM.brightness), + (ROOM['kitchenLight'], ROOM.brightness), + (ROOM['kitchenCounterLight'], ROOM.brightness), (ROOM['livingRoomLamp1'], ROOM.brightness), (ROOM['livingRoomLamp2'], ROOM.brightness), (ROOM['bedLedStrip'], ROOM.color), ]: url = deviceGraph.value(dev, ROOM.putUrl) - if url and dev == DEV.theaterDoorLock: # ew - self._put(url+"/mode", payload="output") - + log.debug('inferredObjects of dev=%s pred=%s', + deviceGraph.qname(dev), + deviceGraph.qname(pred)) inferredObjects = list(inferred.objects(dev, pred)) if len(inferredObjects) == 0: self._putZero(deviceGraph, dev, pred, url) elif len(inferredObjects) == 1: - log.debug('inferredObject: %s %s %r', + log.debug(' inferredObject: %s %s %r', deviceGraph.qname(dev), deviceGraph.qname(pred), - inferredObjects[0]) + inferredObjects[0].toPython()) self._putInferred(deviceGraph, url, inferredObjects[0]) elif len(inferredObjects) > 1: - log.info("conflict, ignoring: %s has %s of %s" % + log.info(" conflict, ignoring: %s has %s of %s" % (dev, pred, inferredObjects)) # write about it to the inferred graph? - - #self._frontDoorPuts(deviceGraph, inferred) - + def _oneShotPostActions(self, deviceGraph, inferred): """ Inferred graph may contain some one-shot statements. We'll send @@ -88,7 +86,7 @@ p = deviceGraph.value(osp, ROOM['predicate']) if s is None or p is None: continue - log.info("checking for %s %s", s, p) + #log.info("checking for %s %s", s, p) for postTarget in inferred.objects(s, p): log.info("post target %r", postTarget) # this packet ought to have 'oneShot' in it somewhere @@ -96,74 +94,21 @@ log.info(" POST %s", postTarget) fetch(postTarget, method="POST", timeout=2).addErrback(err) - self._postMpdCommands(inferred) - - def _postMpdCommands(self, inferred): - """special case to be eliminated. mpd play urls are made of an - mpd service and a song/album/playlist uri to be played. - Ideally the graph rules would assemble these like - http://{mpd}/addAndPlay?uri={toPlay} or maybe toPlay as the payload - which would be fairly general but still allow toPlay uris to - be matched with any player.""" - rootSkippingAuth = "http://brace:9009/" - for mpd in [URIRef("http://bigasterisk.com/host/brace/mpd")]: - - for song in inferred.objects(mpd, ROOM['startMusic']): - log.info("mpd statement: %r" % song) - assert song.startswith('http://bigasterisk.com/music/') - self.post(rootSkippingAuth + "addAndPlay" + urllib.quote(song[len("http://bigasterisk.com/music"):])) - - for state in inferred.objects(mpd, ROOM['playState']): - log.info('hello playstate %s', state) - if state == ROOM['pause']: - log.info("mpd %s %s", mpd, state) - self.post(rootSkippingAuth + "mpd/pause") - for vol in inferred.objects(mpd, ROOM['audioState']): - if vol == ROOM['volumeStepUp']: - self.post(rootSkippingAuth + "volumeAdjust?amount=6&max=70") - if vol == ROOM['volumeStepDown']: - self.post(rootSkippingAuth + "volumeAdjust?amount=-6&min=10") - - def _frontDoorPuts(self, deviceGraph, inferred): - # todo: shouldn't have to be a special case - brt = inferred.value(DEV.frontDoorLcd, ROOM.brightness) - if brt is None: - return - url = deviceGraph.value(DEV.frontDoorLcdBrightness, ROOM.putUrl) - log.info("put lcd %s brightness %s", url, brt) - self._put(str(url) + "?brightness=%s" % str(brt), payload='') - - msg = "open %s motion %s" % ( - inferred.value(DEV['frontDoorOpenIndicator'], ROOM.text), - inferred.value(DEV['frontDoorMotionIndicator'], ROOM.text)) - # this was meant to be 2 chars in the bottom row, but the - # easier test was to replace the whole top msg - #restkit.Resource("http://slash:9080/").put("lcd", message=msg) - - - - - def _put(self, url, payload): - def err(e): - log.warn(" put %s failed (%r)", url, e) - log.info(" PUT %s payload=%r", url, payload) - fetch(url, method="PUT", postdata=payload, timeout=2).addErrback(err) - - def post(self, postTarget): - log.info("special mpd POST %s", postTarget) - def err(e): - log.warn("post %s failed", postTarget) - fetch(postTarget, method="POST", timeout=2).addErrback(err) - - def _putZero(self, deviceGraph, dev, pred, putUrl): - # zerovalue should be a function of pred as well. - value = deviceGraph.value(dev, ROOM.zeroValue) - if value is not None: - log.info("put zero (%r) to %s", value, putUrl) - self._put(putUrl, payload=str(value)) - # this should be written back into the inferred graph - # for feedback + def _putDevices(self, deviceGraph, inferred): + agentFor = {} + for stmt in inferred: + if stmt[1] == ROOM['putAgent']: + agentFor[stmt[0]] = stmt[2] + for stmt in inferred: + putUrl = deviceGraph.value(stmt[0], ROOM['putUrl']) + putPred = deviceGraph.value(stmt[0], ROOM['putPredicate']) + if putUrl and putPred == stmt[1]: + self._put(putUrl + '?' + urllib.urlencode([ + ('s', str(stmt[0])), + ('p', str(stmt[1]))]), + str(stmt[2].toPython()), + agent=agentFor.get(stmt[0], None)) def _putInferred(self, deviceGraph, putUrl, obj): """ @@ -176,5 +121,25 @@ elif isinstance(obj, Literal): self._put(putUrl, payload=str(obj)) else: - log.warn("don't know what payload to put for %s. obj=%r", + log.warn(" don't know what payload to put for %s. obj=%r", putUrl, obj) + + def _putZero(self, deviceGraph, dev, pred, putUrl): + # zerovalue should be a function of pred as well. + value = deviceGraph.value(dev, ROOM.zeroValue) + if value is not None: + log.info(" put zero (%r) to %s", value.toPython(), putUrl) + self._put(putUrl, payload=str(value)) + # this should be written back into the inferred graph + # for feedback + + def _put(self, url, payload, agent=None): + assert isinstance(payload, bytes) + def err(e): + log.warn(" put %s failed (%r)", url, e) + log.info(" PUT %s payload=%s agent=%s", url, payload, agent) + headers = {} + if agent is not None: + headers['x-foaf-agent'] = [str(agent)] + fetch(url, method="PUT", postdata=payload, timeout=2, + headers=headers).addErrback(err)
--- a/service/reasoning/input/startup.n3 Sat Jan 19 10:44:10 2019 -0800 +++ b/service/reasoning/input/startup.n3 Sat Jan 19 12:08:59 2019 -0800 @@ -42,3 +42,11 @@ [ a :OneShotPost; :subject <http://bigasterisk.com/host/plus/sound>; :predicate :postAction ] . [ a :OneShotPost; :subject <http://bigasterisk.com/host/slash/mpd>; :predicate :postAction ] . + +<http://bigasterisk.com/homeauto/thermostatSetTemp> :temperatureF 70 . +# run rdfdb, put it in there + +<http://bigasterisk.com/rfidCard/9327861a28> :cardText "UVOLLTJJEDBZTYBRPCRSFHJMEJGOQAIG"; :owner <http://bigasterisk.com/foaf.rdf#drewp> . +<http://bigasterisk.com/rfidCard/93a7591a77> :cardText "XXGDUPSVAVJJLMZD" ; :owner <http://bigasterisk.com/foaf.rdf#drewp> . +<http://bigasterisk.com/rfidCard/93cd401a04> :cardText "JMQHDQZXENVZRYXUGQPYZCPJFJKIZWAS"; :owner <http://bigasterisk.com/foaf.rdf#drewp> . +<http://bigasterisk.com/rfidCard/a3aa6b1a78> :cardText "UOHQPTMTGSHLETGLYOMPMNJXDYINHHIF"; :owner <http://bigasterisk.com/foaf.rdf#drewp> .
--- a/service/reasoning/inputgraph.py Sat Jan 19 10:44:10 2019 -0800 +++ b/service/reasoning/inputgraph.py Sat Jan 19 12:08:59 2019 -0800 @@ -9,6 +9,7 @@ from rdflibtrig import addTrig from graphop import graphEqual +from greplin import scales from patchsource import ReconnectingPatchSource @@ -21,6 +22,9 @@ DEV = Namespace("http://projects.bigasterisk.com/device/") +STATS = scales.collection('/web', + scales.PmfStat('combineGraph'), +) def parseRdf(text, contentType): g = Graph() g.parse(StringInputSource(text), format={ @@ -33,7 +37,10 @@ def __init__(self, onChange): self.onChange = onChange self.graph = ConjunctiveGraph() - self.patchSource = ReconnectingPatchSource(URIRef('http://bang:9072/graph/home'), self.onPatch) + self.patchSource = ReconnectingPatchSource( + URIRef('http://bang:9072/graph/home'), + #URIRef('http://frontdoor:10012/graph/events'), + self.onPatch, reconnectSecs=10) def onPatch(self, p, fullGraph): if fullGraph: @@ -57,7 +64,6 @@ ROOM['graphLoadMs'], ROOM['localTimeToSecond'], ROOM['history'], - ROOM['temperatureF'], ROOM['connectedAgo'], RDFS['comment'], ] @@ -142,7 +148,8 @@ t1 = time.time() self.addOneShot(g) return time.time() - t1 - + + @STATS.combineGraph.time() def getGraph(self): """rdflib Graph with the file+remote contents of the input graph""" # this could be much faster with the combined readonly graph
--- a/service/reasoning/makefile Sat Jan 19 10:44:10 2019 -0800 +++ b/service/reasoning/makefile Sat Jan 19 12:08:59 2019 -0800 @@ -15,7 +15,7 @@ shell: docker run --rm -it --cap-add SYS_PTRACE --net=host ${TAG} /bin/bash -local_run: +local_run: build_image docker run --rm -it -p ${PORT}:${PORT} \ -v `pwd`:/mnt \ --net=host \
--- a/service/reasoning/oneShot Sat Jan 19 10:44:10 2019 -0800 +++ b/service/reasoning/oneShot Sat Jan 19 12:08:59 2019 -0800 @@ -3,7 +3,7 @@ send a statement to the reasoning server for one update cycle. Args are s/p/o in n3 notation, with many prefixes predefined here. """ -import sys, restkit, time +import sys, requests, time s, p, o = sys.argv[1:] prefixes = { @@ -25,9 +25,9 @@ print "Sending: %s" % stmt t1 = time.time() -reasoning = restkit.Resource("http://bang:9071/") -ret = reasoning.post("oneShot", - headers={"content-type": "text/n3"}, - payload=stmt) +ret = requests.post( + 'http://bang:9071/oneShot', + headers={"content-type": "text/n3"}, + data=stmt.encode('ascii')) g = float(ret.headers['x-graph-ms']) print "%.1f ms for graph update; %.1f ms other overhead" % (g, 1000 * (time.time() - t1) - g)
--- a/service/reasoning/reasoning.py Sat Jan 19 10:44:10 2019 -0800 +++ b/service/reasoning/reasoning.py Sat Jan 19 12:08:59 2019 -0800 @@ -45,7 +45,9 @@ NS = {'': ROOM, 'dev': DEV} STATS = scales.collection('/web', - scales.PmfStat('graphChanged')) + scales.PmfStat('graphChanged'), + scales.PmfStat('updateRules'), +) class Reasoning(object): def __init__(self): @@ -59,6 +61,7 @@ self.inputGraph = InputGraph([], self.graphChanged) self.inputGraph.updateFileData() + @STATS.updateRules.time() def updateRules(self): rulesPath = 'rules.n3' try: @@ -90,7 +93,11 @@ statements are already in inputGraph.getGraph(). """ log.info("----------------------") - log.info("graphChanged (oneShot=%s):", oneShot) + log.info("graphChanged (oneShot=%s %s stmts):", + oneShot, len(oneShotGraph) if oneShotGraph is not None else 0) + if oneShotGraph: + for stmt in oneShotGraph: + log.info(" OS-> %r", stmt) t1 = time.time() oldInferred = self.inferred try: @@ -171,6 +178,7 @@ everything appears to be a 'change'. """ try: + log.info('POST to oneShot, headers=%s', self.request.headers) dt = self.settings.reasoning.inputGraph.addOneShotFromString( self.request.body, self.request.headers['content-type']) self.set_header('x-graph-ms', str(1000 * dt)) @@ -294,7 +302,6 @@ -i Verbose log on the input phase -r Verbose log on the reasoning phase and web stuff -o Verbose log on the actions/output phase - --source=<substr> Limit sources to those with this string. """) r = Reasoning()
--- a/service/reasoning/requirements.txt Sat Jan 19 10:44:10 2019 -0800 +++ b/service/reasoning/requirements.txt Sat Jan 19 12:08:59 2019 -0800 @@ -10,4 +10,4 @@ rdflib_jsonld==0.4.0 git+http://github.com/drewp/FuXi.git@003fb48984e9813808a23ba152798c125718f0e7#egg=FuXi git+http://github.com/drewp/scales.git@448d59fb491b7631877528e7695a93553bfaaa93#egg=scales -https://projects.bigasterisk.com/rdfdb/rdfdb-0.3.0.tar.gz +https://projects.bigasterisk.com/rdfdb/rdfdb-0.7.0.tar.gz
--- a/service/reasoning/rules.n3 Sat Jan 19 10:44:10 2019 -0800 +++ b/service/reasoning/rules.n3 Sat Jan 19 12:08:59 2019 -0800 @@ -137,18 +137,16 @@ # :storageCeilingLedLong :brightness 1 . #} . -@prefix bed: <http://bigasterisk.com/homeauto/sensor/bed/> . - -{ bed:greenButton :buttonState :press } => { +{ sensor:bedGreenButton :buttonState :press } => { :headboardWhite :brightness 0.0 . :anim1 :position :end . } . -{ bed:redButton :buttonState :press . :headboardWhite :brightness 0.0 . } => { +{ sensor:bedRedButton :buttonState :press . :headboardWhite :brightness 0.0 . } => { :headboardWhite :brightness 0.2 . } . -{ bed:redButton :buttonState :press . :headboardWhite :brightness 0.2 . } => { +{ sensor:bedRedButton :buttonState :press . :headboardWhite :brightness 0.2 . } => { :headboardWhite :brightness 1 . } . @@ -178,18 +176,41 @@ :livingRoomLamp2 :brightness 1.0 . } . { :bookSwitch :buttonState :press . :livingRoomLamp1 :brightness 1.0 . } => { - :livingRoomLamp1 :brightness 0 . - :livingRoomLamp2 :brightness 0 . + :livingRoomLamp1 :brightness 0.0 . + :livingRoomLamp2 :brightness 0.0 . +} . + +{ :frontBedPostSwitch1 :buttonState :press . :starTrekLight :brightness 0.0 . } => { + :starTrekLight :brightness 1.0 . +} . +{ :frontBedPostSwitch1 :buttonState :press . :starTrekLight :brightness 1.0 . } => { + :starTrekLight :brightness 0.0 . +} . + + +#{ :change :down . } => { star:slideshow :postAction <http://brace:9049/effects/beep1> . } . +#{ sensor:kitchenCounterButton1 :buttonState :press . } => { +# bang: :postAction <http://10.2.0.62/rpc/motor> . +#} . + +{ sensor:kitchenCounterButton1 :buttonState :press . :kitchenLight :brightness 0.0 . } => { + :kitchenLight :brightness 1.0 . + :kitchenCounterLight :brightness 0.5 . +# and skylight rgb/rgb/set '200,255,200' +} . +{ sensor:kitchenCounterButton1 :buttonState :press . :kitchenLight :brightness 1.0 . } => { + :kitchenLight :brightness 0.0 . + :kitchenCounterLight :brightness 0.0 . } . { <http://bigasterisk.com/homeauto/sensor/motionGarageDoorInside> :seesRecently :noMotion . } => { - :garageOverhead :brightness 0 . + :garageOverhead :brightness 0.0. } . { <http://bigasterisk.com/homeauto/sensor/motionGarageDoorInside> :seesRecently :motion . } => { - :garageOverhead :brightness 1 . + :garageOverhead :brightness 1.0 . } . { sensor:motionBed :sees :motion . } => { :anim1 :playback :start . } . @@ -199,6 +220,8 @@ <http://bigasterisk.com/host/frontdoor/monitor> :powerState :on . } . + + { <http://projects.bigasterisk.com/room/remoteButton/KEY_POWER> :state :press } => { #star:slideshow :postAction <http://dash:9049/effects/question> . bang: :postAction <http://10.2.0.62/rpc/motor> .