Mercurial > code > home > repos > homeauto
changeset 934:3bb18b7d21df
reasoning actions: generalize them a bit but then add a bunch of special cases for mpd for now
Ignore-this: 3ab80fee5836817dcd54ce6678a6089d
darcs-hash:20131009044224-312f9-b47b461e8872e4ef5c4951703ab8f13d5b848ea3
author | drewp <drewp@bigasterisk.com> |
---|---|
date | Tue, 08 Oct 2013 21:42:24 -0700 |
parents | cd3ec05e4f57 |
children | 9bb3eac740f0 |
files | service/reasoning/input/startup.n3 service/reasoning/oneShot service/reasoning/rdflibtrig.py service/reasoning/reasoning.py service/reasoning/rules.n3 |
diffstat | 5 files changed, 90 insertions(+), 24 deletions(-) [+] |
line wrap: on
line diff
--- a/service/reasoning/input/startup.n3 Tue Oct 08 21:39:06 2013 -0700 +++ b/service/reasoning/input/startup.n3 Tue Oct 08 21:42:24 2013 -0700 @@ -18,3 +18,9 @@ # also, http://bang:9072/bang/processStatus + +[ a :OneShotPost; :subject <http://bigasterisk.com/host/star/slideshow>; :predicate :postAction ] . +[ a :OneShotPost; :subject <http://bigasterisk.com/host/star/sound>; :predicate :postAction ] . +[ a :OneShotPost; :subject <http://bigasterisk.com/host/slash/sound>; :predicate :postAction ] . + +[ a :OneShotPost; :subject <http://bigasterisk.com/host/slash/mpd>; :predicate :postAction ] .
--- a/service/reasoning/oneShot Tue Oct 08 21:39:06 2013 -0700 +++ b/service/reasoning/oneShot Tue Oct 08 21:42:24 2013 -0700 @@ -8,10 +8,11 @@ prefixes = { 'room' : 'http://projects.bigasterisk.com/room/', +'shuttle': 'http://bigasterisk.com/room/livingRoom/shuttlepro/', } def expand(term): - if ':' not in term: + if ':' not in term or term.startswith(('<', '"', "'")): return term left, right = term.split(':', 1) if left in prefixes:
--- a/service/reasoning/rdflibtrig.py Tue Oct 08 21:39:06 2013 -0700 +++ b/service/reasoning/rdflibtrig.py Tue Oct 08 21:42:24 2013 -0700 @@ -24,9 +24,9 @@ yield stmt + (ctx,) -def addTrig(graph, url): +def addTrig(graph, url, timeout=2): t1 = time.time() - response = restkit.request(url) + response = restkit.request(url, timeout=timeout) if response.status_int != 200: raise ValueError("status %s from %s" % (response.status, url)) trig = response.body_string()
--- a/service/reasoning/reasoning.py Tue Oct 08 21:39:06 2013 -0700 +++ b/service/reasoning/reasoning.py Tue Oct 08 21:42:24 2013 -0700 @@ -19,7 +19,7 @@ from twisted.internet import reactor, task from twisted.web.client import getPage from twisted.python.filepath import FilePath -import time, traceback, sys, json, logging +import time, traceback, sys, json, logging, urllib from rdflib.Graph import Graph, ConjunctiveGraph from rdflib import Namespace, URIRef, Literal, RDF, StringInputSource from FuXi.Rete.RuleStore import N3RuleStore @@ -244,8 +244,8 @@ def _put(self, url, payload): def err(e): - outlog.warn("put %s failed", url) - outlog.info("PUT %s payload=%r", url, payload) + outlog.warn(" put %s failed", url) + outlog.info(" PUT %s payload=%r", url, payload) fetch(url, method="PUT", postdata=payload, timeout=2).addErrback(err) def putResults(self, inferred): @@ -290,29 +290,66 @@ def oneShotPostActions(self, deviceGraph, inferred): + """ + Inferred graph may contain some one-shot statements. We'll send + statement objects to anyone on web sockets, and also generate + POST requests as described in the graph. + + one-shot statement ?s ?p ?o + with this in the graph: + ?osp a :OneShotPost + ?osp :subject ?s + ?osp :predicate ?p + this will cause a post to ?o + """ # nothing in this actually makes them one-shot yet. they'll # just fire as often as we get in here, which is not desirable - for s, p in [ - (URIRef('http://bigasterisk.com/host/star/slideshow'), ROOM.postAction), - (URIRef('http://bigasterisk.com/host/star/sound'), ROOM.postAction), - (URIRef('http://bigasterisk.com/host/slash/sound'), ROOM.postAction), - ]: - log.info("find inferred objs %r %r" % (s, p)) + log.info("oneShotPostActions") + def err(e): + outlog.warn("post %s failed", postTarget) + for osp in deviceGraph.subjects(RDF.type, ROOM['OneShotPost']): + s = deviceGraph.value(osp, ROOM['subject']) + p = deviceGraph.value(osp, ROOM['predicate']) + if s is None or p is None: + continue for postTarget in inferred.objects(s, p): log.info("post target %r", postTarget) + # this packet ought to have 'oneShot' in it somewhere sendToLiveClients({"s":s, "p":p, "o":postTarget}) - if s in [URIRef('http://bigasterisk.com/host/star/sound'), - URIRef('http://bigasterisk.com/host/slash/sound'), - URIRef('http://bigasterisk.com/host/star/slideshow'), - ]: - try: - response = restkit.request(url=postTarget, method="POST", body="") - except Exception, e: - log.warn("post to %s failed: %s" % (postTarget, e)) - else: - log.info("post to %s got status %s" % - (postTarget, response.status)) + outlog.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.""" + def post(postTarget): + outlog.info("special mpd POST %s", postTarget) + def err(e): + outlog.warn("post %s failed", postTarget) + fetch(postTarget, method="POST", timeout=2).addErrback(err) + root = "http://bigasterisk.com/music/slash/mpd/" + rootSkippingAuth = "http://slash:9009/" + slashMpd = URIRef("http://bigasterisk.com/host/slash/mpd") + for song in inferred.objects(slashMpd, ROOM['startMusic']): + outlog.info("mpd statement: %r" % song) + assert song.startswith('http://bigasterisk.com/music/') + post(rootSkippingAuth + "addAndPlay" + urllib.quote(song[len("http://bigasterisk.com/music"):])) + + for state in inferred.objects(slashMpd, ROOM['playState']): + if state == ROOM['pause']: + post(rootSkippingAuth + "mpd/pause") + for vol in inferred.objects(slashMpd, ROOM['audioState']): + if vol == ROOM['volumeStepUp']: + post(rootSkippingAuth + "volumeAdjust?amount=6&max=70") + if vol == ROOM['volumeStepDown']: + post(rootSkippingAuth + "volumeAdjust?amount=-6&min=10") + def putZero(self, deviceGraph, dev, pred, putUrl): # zerovalue should be a function of pred as well. value = deviceGraph.value(dev, ROOM.zeroValue) @@ -399,6 +436,9 @@ everything appears to be a 'change'. """ g = parseRdf(self.request.body, self.request.headers['content-type']) + if not len(g): + log.warn("incoming oneshot graph had no statements: %r", self.request.body) + return self.settings.reasoning.inputGraph.addOneShot(g) # for reuse
--- a/service/reasoning/rules.n3 Tue Oct 08 21:39:06 2013 -0700 +++ b/service/reasoning/rules.n3 Tue Oct 08 21:42:24 2013 -0700 @@ -1,3 +1,5 @@ +# rules only! statements in this file will not be considered in the graph + @prefix : <http://projects.bigasterisk.com/room/>. @prefix bigast: <http://bigasterisk.com/>. @prefix dev: <http://projects.bigasterisk.com/device/>. @@ -58,9 +60,17 @@ { <http://projects.bigasterisk.com/room/star/button/yel> :change :down . } => { - <http://bigasterisk.com/host/star/sound> :postAction <http://star:9049/sound?filename=/my/music/ubuntuone/Daft+Punk/Discovery/Harder+Better+Faster+Stronger.mp3> . + <http://bigasterisk.com/host/star/slideshow> :postAction <http://bigasterisk.com/host/star/slideshow/toggleFeeder> . + <http://bigasterisk.com/host/star/slideshow> :postAction <http://star:9049/effects/beep1> . } . + +# sound wasn't responding; waiting for other button fixes +# { <http://projects.bigasterisk.com/room/star/button/yel> :change :down . } => +# { +# <http://bigasterisk.com/host/star/sound> :postAction <http://star:9049/sound?filename=/my/music/ubuntuone/Daft+Punk/Discovery/Harder+Better+Faster+Stronger.mp3> . +# } . + { <http://projects.bigasterisk.com/room/livingRoom/shuttlepro/dial> :change :up . } => { <http://bigasterisk.com/host/slash/sound> :postAction <http://slash:9049/volume?offset=.05&max=.6> . } . @@ -83,3 +93,12 @@ # the plan here is that as soon as we can show that I'm not at my # desk (cell phone wifi, asleep, etc), power the screen off } . + +@prefix shuttle: <http://bigasterisk.com/room/livingRoom/shuttlepro/> . +@prefix mpd: <http://bigasterisk.com/host/slash/mpd> . + +{ ?button :state :press . ?button :playsMusic ?song } => { mpd: :startMusic ?song } . +# the rest of this modeling is still a mess. Handled completely by special case in reasoning.py +{ shuttle:button12 :state :press } => { mpd: :playState :pause } . +{ shuttle:dial :change :clockwise } => { mpd: :audioState :volumeStepUp } . +{ shuttle:dial :change :counterclockwise } => { mpd: :audioState :volumeStepDown } .