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> .