changeset 251:254df9f881a6

start sending oneshot events from some devices Ignore-this: 2c98200e9bab1acca872f4cdcaf88e4d
author drewp@bigasterisk.com
date Fri, 12 Feb 2016 02:41:29 -0800
parents c1287ab87add
children 57f16890ab3a
files service/arduinoNode/arduinoNode.py service/arduinoNode/devices.py service/piNode/devices.py service/piNode/piNode.py service/reasoning/reasoning.py service/reasoning/rules.n3
diffstat 6 files changed, 72 insertions(+), 11 deletions(-) [+]
line wrap: on
line diff
--- a/service/arduinoNode/arduinoNode.py	Tue Feb 09 22:10:38 2016 -0800
+++ b/service/arduinoNode/arduinoNode.py	Fri Feb 12 02:41:29 2016 -0800
@@ -8,6 +8,7 @@
 import shutil, json
 import serial
 import cyclone.web
+from cyclone.httpclient import fetch
 from rdflib import Graph, Namespace, URIRef, Literal, RDF, ConjunctiveGraph
 from rdflib.parser import StringInputSource
 from twisted.internet import reactor, task
@@ -127,6 +128,11 @@
         for i in self._polledDevs:
             now = time.time()
             new = i.readFromPoll(self.ser.read)
+            if isinstance(new, dict): # new style
+                oneshot = new['oneshot']
+                new = new['latest']
+            else:
+                oneshot = None
             prev = self._statementsFromInputs.get(i.uri, [])
             if new or prev:
                 self._statementsFromInputs[i.uri] = new
@@ -139,6 +145,8 @@
                 #     fail since the '2' statement is gone)
                 self.masterGraph.patch(Patch.fromDiff(inContext(prev, i.uri),
                                                       inContext(new, i.uri)))
+            if oneshot:
+                self._sendOneshot(oneshot)
             self._lastPollTime[i.uri] = now
             
         #plus statements about succeeding or erroring on the last poll
@@ -150,6 +158,15 @@
             log.warn('poll took %.1f seconds' % elapsed)
         self._exportToGraphite()
 
+    def _sendOneshot(self, oneshot):
+        body = (' '.join('%s %s %s .' % (s.n3(), p.n3(), o.n3())
+                         for s,p,o in oneshot)).encode('utf8')
+        bang6 = 'fcb8:4119:fb46:96f8:8b07:1260:0f50:fcfa'
+        fetch(method='POST',
+              url='http://[%s]:9071/oneShot' % bang6,
+              headers={'Content-Type': ['text/n3']}, postdata=body,
+              timeout=5)
+
     def _exportToGraphite(self):
         # note this is writing way too often- graphite is storing at a lower res
         now = time.time()
--- a/service/arduinoNode/devices.py	Tue Feb 09 22:10:38 2016 -0800
+++ b/service/arduinoNode/devices.py	Fri Feb 12 02:41:29 2016 -0800
@@ -177,6 +177,10 @@
 @register
 class MotionSensorInput(DeviceType):
     deviceType = ROOM['MotionSensor']
+    def __init__(self, graph, uri, pinNumber):
+        DeviceType.__init__(self, graph, uri, pinNumber)
+        self.lastRead = None
+        
     def generateSetupCode(self):
         return 'pinMode(%(pin)d, INPUT); digitalWrite(%(pin)d, LOW);' % {
             'pin': self.pinNumber,
@@ -192,12 +196,17 @@
         if b not in 'yn':
             raise ValueError('unexpected response %r' % b)
         motion = b == 'y'
-                
-        return [
+
+        oneshot = []
+        if self.lastRead is not None and motion != self.lastRead:
+            oneshot = [(self.uri, ROOM['sees'], ROOM['motionStart'])]
+        self.lastRead = motion
+        
+        return {'latest': [
             (self.uri, ROOM['sees'],
              ROOM['motion'] if motion else ROOM['noMotion']),
             self.recentMotionStatement(motion),
-        ]
+        ], 'oneshot': oneshot}
 
     def recentMotionStatement(self, motion):
         if not hasattr(self, 'lastMotionTime'):
--- a/service/piNode/devices.py	Tue Feb 09 22:10:38 2016 -0800
+++ b/service/piNode/devices.py	Fri Feb 12 02:41:29 2016 -0800
@@ -122,11 +122,11 @@
     def poll(self):
         motion = self.pi.read(17)
         
-        return [
+        return {'latest': [
             (self.uri, ROOM['sees'],
              ROOM['motion'] if motion else ROOM['noMotion']),
             self.recentMotionStatement(motion),
-        ]
+        ], 'oneshot': []}
 
     def recentMotionStatement(self, motion):
         if not hasattr(self, 'lastMotionTime'):
@@ -257,14 +257,25 @@
         log.debug("setup switch on %r", self.pinNumber)
         self.pi.set_mode(self.pinNumber, pigpio.INPUT)
         self.pi.set_pull_up_down(self.pinNumber, pigpio.PUD_UP)
+        self.lastClosed = None
 
     def poll(self):
         closed = not self.pi.read(self.pinNumber)
-        
-        return [
+
+        if self.lastClosed is not None and closed != self.lastClosed:
+            oneshot = [
+                (self.uri, ROOM['buttonState'],
+                 ROOM['press'] if closed else ROOM['release']),
+            ]
+        else:
+            oneshot = []
+        self.lastClosed = closed
+            
+        return {'latest': [
             (self.uri, ROOM['buttonState'],
              ROOM['pressed'] if closed else ROOM['notPressed']),
-        ]
+        ],
+                'oneshot':oneshot}
         
     def watchPrefixes(self):
         return [
--- a/service/piNode/piNode.py	Tue Feb 09 22:10:38 2016 -0800
+++ b/service/piNode/piNode.py	Fri Feb 12 02:41:29 2016 -0800
@@ -1,6 +1,7 @@
 from __future__ import division
 import sys, logging, socket, json, time, os
 import cyclone.web
+from cyclone.httpclient import fetch
 from rdflib import Namespace, URIRef, Literal, Graph, RDF, ConjunctiveGraph
 from rdflib.parser import StringInputSource
 from twisted.internet import reactor, task
@@ -72,7 +73,13 @@
                 self._lastPollTime.get(i.uri, 0) + i.pollPeriod > now):
                 continue
             new = i.poll()
+            if isinstance(new, dict): # new style
+                oneshot = new['oneshot']
+                new = new['latest']
+            else:
+                oneshot = None
             prev = self._statementsFromInputs.get(i.uri, [])
+
             if new or prev:
                 self._statementsFromInputs[i.uri] = new
                 # it's important that quads from different devices
@@ -84,9 +91,21 @@
                 #     fail since the '2' statement is gone)
                 self.masterGraph.patch(Patch.fromDiff(inContext(prev, i.uri),
                                                       inContext(new, i.uri)))
+
+            if oneshot:
+                self._sendOneshot(oneshot)
             self._lastPollTime[i.uri] = now
         self._exportToGraphite()
 
+    def _sendOneshot(self, oneshot):
+        body = (' '.join('%s %s %s .' % (s.n3(), p.n3(), o.n3())
+                         for s,p,o in oneshot)).encode('utf8')
+        bang6 = 'fcb8:4119:fb46:96f8:8b07:1260:0f50:fcfa'
+        fetch(method='POST',
+              url='http://[%s]:9071/oneShot' % bang6,
+              headers={'Content-Type': ['text/n3']}, postdata=body,
+              timeout=5)
+
     def _exportToGraphite(self):
         # note this is writing way too often- graphite is storing at a lower res
         now = time.time()
--- a/service/reasoning/reasoning.py	Tue Feb 09 22:10:38 2016 -0800
+++ b/service/reasoning/reasoning.py	Fri Feb 12 02:41:29 2016 -0800
@@ -310,7 +310,7 @@
         try:
             g = parseRdf(self.request.body, self.request.headers['content-type'])
             for s in g:
-                print "stmt", s
+                log.debug("oneshot stmt %r", s)
             if not len(g):
                 log.warn("incoming oneshot graph had no statements: %r", self.request.body)
                 return
--- a/service/reasoning/rules.n3	Tue Feb 09 22:10:38 2016 -0800
+++ b/service/reasoning/rules.n3	Fri Feb 12 02:41:29 2016 -0800
@@ -129,12 +129,17 @@
   :storageCeilingLedLong :brightness "1" .
 } .
 
+{ <http://bigasterisk.com/homeauto/sensor/motion0> :sees <http://projects.bigasterisk.com/room/motionStart> . } => {
+  :storageCeilingLedCross :brightness "1" .
+  :storageCeilingLedLong :brightness "1" .
+} .
+
 @prefix bed: <http://bigasterisk.com/homeauto/sensor/bed/> .
 
-{ bed:greenButton :buttonState :pressed } => {
+{ bed:greenButton :buttonState :press } => {
   :headboardWhite :brightness 0 .
 } .
 
-{ bed:redButton :buttonState :pressed } => {
+{ bed:redButton :buttonState :press } => {
   :headboardWhite :brightness 0.04 .
 } .
\ No newline at end of file