Mercurial > code > home > repos > homeauto
diff service/reasoning/actions.py @ 240:0c306e76d8c5
ipv6 fetch support. refactor Actions to new class and file
Ignore-this: 200d7093919cf001706ad9c02347fabb
author | drewp@bigasterisk.com |
---|---|
date | Mon, 01 Feb 2016 03:28:17 -0800 |
parents | |
children | 3f936fb61e4e |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/service/reasoning/actions.py Mon Feb 01 03:28:17 2016 -0800 @@ -0,0 +1,171 @@ +from rdflib import URIRef, Namespace, RDF, Literal +import logging +import urllib + +from cyclone.httpclient import fetch +log = logging.getLogger('output') +log.setLevel(logging.WARN) + +ROOM = Namespace("http://projects.bigasterisk.com/room/") +DEV = Namespace("http://projects.bigasterisk.com/device/") + +class Actions(object): + def __init__(self, sendToLiveClients): + self.sendToLiveClients = sendToLiveClients + + def putResults(self, deviceGraph, inferred): + """ + some conclusions in the inferred graph lead to PUT requests + getting made + + if the graph contains (?d ?p ?o) and ?d and ?p are a device + and predicate we support PUTs for, then we look up + (?d :putUrl ?url) and (?o :putValue ?val) and call + PUT ?url <- ?val + + If the graph doesn't contain any matches, we use (?d + :zeroValue ?val) for the value and PUT that. + """ + + 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://projects.bigasterisk.com/room/storageCeilingLedLong'), ROOM.brightness), + (URIRef('http://projects.bigasterisk.com/room/storageCeilingLedCross'), ROOM.brightness), + ]: + url = deviceGraph.value(dev, ROOM.putUrl) + + if url and dev == DEV.theaterDoorLock: # ew + self._put(url+"/mode", payload="output") + + inferredObjects = list(inferred.objects(dev, pred)) + if len(inferredObjects) == 0: + self._putZero(deviceGraph, dev, pred, url) + elif len(inferredObjects) == 1: + self._putInferred(deviceGraph, url, inferredObjects[0]) + elif len(inferredObjects) > 1: + log.info("conflict, ignoring: %s has %s of %s" % + (dev, pred, inferredObjects)) + # write about it to the inferred graph? + + #self._frontDoorPuts(deviceGraph, inferred) + + + 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 _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 + log.info("_oneShotPostActions") + def err(e): + log.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 + self.sendToLiveClients({"s":s, "p":p, "o":postTarget}) + + 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.""" + def post(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) + + 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/') + 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) + post(rootSkippingAuth + "mpd/pause") + for vol in inferred.objects(mpd, 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) + 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 _putInferred(self, deviceGraph, putUrl, obj): + """ + HTTP PUT to putUrl, with a payload that's either obj's :putValue + or obj itself. + """ + value = deviceGraph.value(obj, ROOM.putValue) + if value is not None: + log.info("put %s to %s", value, putUrl) + self._put(putUrl, payload=str(value)) + elif isinstance(obj, Literal): + log.info("put %s to %s", obj, putUrl) + self._put(putUrl, payload=str(obj)) + else: + log.warn("don't know what payload to put for %s. obj=%r", + putUrl, obj) + + 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) + +