# HG changeset patch
# User drewp@bigasterisk.com
# Date 1555914405 25200
# Node ID c9cadfcb4fdceda0ab047fe38e97bc3da50a1a2b
# Parent 43bb3e69821d013970bad368458075f8cde6b9a5
rm old (2010) code for talking to arduino/firmata with a web ui and some activitystreams stuff
Ignore-this: 3227955154a42eb5b67b49dd1f15890c
diff -r 43bb3e69821d -r c9cadfcb4fdc service/theaterArduino/index.html
--- a/service/theaterArduino/index.html Sun Apr 21 03:30:59 2019 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,160 +0,0 @@
-
-
-
-
-
-
-
-
- pyduino web interface
-
- Use GET or PUT on the resources below. The value is a "0" or "1"
- string. PUT "output" to /pin/d2/mode (etc) to make it writable.
-
-
- pin/d2 :
-
-
-
-
-
- pin/d3 :
-
-
-
-
-
- pin/d4 :
-
-
-
-
-
- pin/d5 :
-
-
-
-
-
- pin/d6 :
-
-
-
-
-
- pin/d7 :
-
-
-
-
-
- pin/d8 :
-
-
-
-
-
- pin/d9 :
-
-
-
-
-
- pin/d10 :
-
-
-
-
-
- pin/d11 :
-
-
-
-
-
- pin/d12 :
-
-
-
-
-
- pin/d13 :
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff -r 43bb3e69821d -r c9cadfcb4fdc service/theaterArduino/theaterArduino.py
--- a/service/theaterArduino/theaterArduino.py Sun Apr 21 03:30:59 2019 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,145 +0,0 @@
-"""
-arduino example sketches, 'StandardFirmata'.
-
-####easy_install http://github.com/lupeke/python-firmata/tarball/master
-
-Now using http://code.google.com/p/pyduino, modified to run at 57600
-baud like my arduino's code does. pyduino is better than the lupeke
-one in that you can read your settings off the output pins
-
-Note that there are some startup delays and you may not hear about
-input changes for a few seconds.
-"""
-from __future__ import division
-import sys, cyclone.web, time, simplejson, os
-from twisted.web.client import getPage
-from twisted.internet import reactor, task
-
-sys.path.append("/my/proj/homeauto/lib")
-from cycloneerr import PrettyErrorHandler
-from logsetup import log
-
-sys.path.append("pyduino-read-only")
-import pyduino
-
-def _num(name):
- if name.startswith('d'):
- return int(name[1:])
- raise ValueError(name)
-
-class pin(PrettyErrorHandler, cyclone.web.RequestHandler):
- def get(self, name):
- self.set_header("Content-Type", "text/plain")
- arduino = self.settings.arduino
- arduino.iterate()
- self.write(str(int(arduino.digital[_num(name)].read())))
-
- def put(self, name):
- t1 = time.time()
- self.settings.arduino.digital[_num(name)].write(int(self.request.body))
- log.debug("arduino write in %.1f ms" % (1000 * (time.time() - t1)))
-
-
-class pinMode(PrettyErrorHandler, cyclone.web.RequestHandler):
- def get(self, name):
- self.set_header("Content-Type", "text/plain")
- mode = self.settings.arduino.digital[_num(name)].get_mode()
- self.write({pyduino.DIGITAL_INPUT : "input",
- pyduino.DIGITAL_OUTPUT : "output"}[mode])
-
- def put(self, name):
- mode = {
- "input" : pyduino.DIGITAL_INPUT,
- "output" : pyduino.DIGITAL_OUTPUT}[self.request.body.strip()]
- self.settings.arduino.digital[_num(name)].set_mode(mode)
-
-class Pid(PrettyErrorHandler, cyclone.web.RequestHandler):
- def get(self):
- self.set_header("Content-Type", "text/plain")
- self.write(str(os.getpid()))
-
-class index(PrettyErrorHandler, cyclone.web.RequestHandler):
- def get(self):
- """
- this is a suitable status check; it does a round-trip to arduino
- """
- # this would be a good ping() call for pyduino
- self.settings.arduino.sp.write(chr(pyduino.REPORT_VERSION))
- self.settings.arduino.iterate()
-
- self.set_header("Content-Type", "application/xhtml+xml")
- self.write(open('index.html').read())
-
-class Application(cyclone.web.Application):
- def __init__(self, arduino):
- handlers = [
- (r"/", index),
- (r'/pin/(.*)/mode', pinMode),
- (r'/pin/(.*)', pin),
- (r'/pid', Pid),
- # web refresh could benefit a lot from a json resource that
- # gives all the state
- ]
- settings = {"arduino" : arduino,}
- cyclone.web.Application.__init__(self, handlers, **settings)
-
-class WatchPins(object):
- def __init__(self, arduino, conf):
- self.arduino, self.conf = arduino, conf
- self.lastState = {}
- self.pins = conf['watchPins']
- if self.pins == 'allInput':
- self.watchAllInputs()
- for pin in self.pins:
- arduino.digital_ports[pin >> 3].set_active(1)
- arduino.digital[pin].set_mode(pyduino.DIGITAL_INPUT)
-
- def watchAllInputs(self):
- raise NotImplementedError("this needs to be updated whenever the modes change")
- self.pins = [p for p in range(2, 13+1) if
- self.arduino.digital[p].get_mode() ==
- pyduino.DIGITAL_INPUT]
-
- def reportPostError(self, fail, pin, value, url):
- log.error("failed to send pin %s update (now %s) to %r: %r" % (pin, value, url, fail))
-
- def poll(self):
- try:
- self._poll()
- except Exception, e:
- log.error("during poll:", exc_info=1)
-
- def _poll(self):
- # this can IndexError for a port number being out of
- # range. I'm not sure how- maybe error data coming in the
- # port?
- arduino.iterate()
- for pin in self.pins:
- current = arduino.digital[pin].read()
- if current != self.lastState.get(pin, None):
- d = getPage(
- self.conf['post'],
- method="POST",
- postdata=simplejson.dumps(dict(board=self.conf['boardName'], pin=pin, level=int(current))),
- headers={'Content-Type' : 'application/json'})
- d.addErrback(self.reportPostError, pin, current, self.conf['post'])
-
- self.lastState[pin] = current
-
-if __name__ == '__main__':
-
- config = { # to be read from a file
- 'arduinoPort': '/dev/serial/by-id/usb-FTDI_FT232R_USB_UART_A900cepU-if00-port0',
- 'servePort' : 9056,
- 'pollFrequency' : 20,
- 'post' : 'http://bang:9069/pinChange',
- 'boardName' : 'theater', # gets sent with updates
- 'watchPins' : [9, 10], # or 'allInput' (not yet working)
- # todo: need options to preset inputs/outputs at startup
- }
-
- arduino = pyduino.Arduino(config['arduinoPort'])
- wp = WatchPins(arduino, config)
- task.LoopingCall(wp.poll).start(1/config['pollFrequency'])
- reactor.listenTCP(config['servePort'], Application(arduino))
- reactor.run()
diff -r 43bb3e69821d -r c9cadfcb4fdc service/theaterArduino/watchpins.html
--- a/service/theaterArduino/watchpins.html Sun Apr 21 03:30:59 2019 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,98 +0,0 @@
-
-
-
-
- theater motion sensor history:
-
-max age:
-
-
-
-
-
-
diff -r 43bb3e69821d -r c9cadfcb4fdc service/theaterArduino/watchpins.py
--- a/service/theaterArduino/watchpins.py Sun Apr 21 03:30:59 2019 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,246 +0,0 @@
-"""
-listener to the POST messages sent by theaterArduino.py when a pin changes.
-records interesting events to mongodb, sends further messages.
-
-Will also serve activity stream.
-"""
-import sys, os, datetime, cyclone.web, simplejson, time
-from twisted.internet import reactor
-from twisted.internet.error import ConnectError
-from twisted.internet.defer import inlineCallbacks, returnValue
-from twisted.web.client import getPage
-from dateutil.tz import tzutc
-from pymongo import Connection
-from rdflib import Namespace, Literal, Graph
-from rdflib.parser import StringInputSource
-sys.path.append("/my/site/magma")
-from activitystream import ActivityStream
-from stategraph import StateGraph
-
-sys.path.append("/my/proj/homeauto/lib")
-from cycloneerr import PrettyErrorHandler
-from logsetup import log
-
-DEV = Namespace("http://projects.bigasterisk.com/device/")
-ROOM = Namespace("http://projects.bigasterisk.com/room/")
-zeroTime = datetime.datetime.fromtimestamp(0, tzutc())
-
-class PinChange(PrettyErrorHandler, cyclone.web.RequestHandler):
- def post(self):
- # there should be per-pin debounce settings so we don't log
- # all the noise of a transition change
-
- msg = simplejson.loads(self.request.body)
- msg['t'] = datetime.datetime.now(tzutc())
- msg['name'] = {9: 'downstairsDoorOpen',
- 10: 'downstairsDoorMotion',
- }[msg['pin']]
- log.info("pinchange post %r", msg)
- self.settings.mongo.insert(msg)
-
- history = self.settings.history
- if msg['pin'] == 10:
- history['motionHistory'] = (history.get('motionHistory', []) + [(msg['t'], msg['level'])])[-50:]
- if msg['level'] == 1:
- if history.get('prevMotion', 0) == 0:
- history['motionStart'] = msg['t']
-
- history['prevMotion'] = msg['level']
-
-
-class InputChange(PrettyErrorHandler, cyclone.web.RequestHandler):
- """
- several other inputs post to here to get their events recorded,
- too. This file shouldn't be in theaterArduino. See bedroomArduino,
- frontDoorArduino, garageArduino.
- """
- def post(self):
- msg = simplejson.loads(self.request.body)
- msg['t'] = datetime.datetime.now(tzutc())
- log.info(msg)
- self.settings.mongo.insert(msg)
-
- # trigger to entrancemusic? rdf graph change PSHB?
-
-class GraphHandler(PrettyErrorHandler, cyclone.web.RequestHandler):
- """
- fetch the pins from drv right now (so we don't have stale data),
- and return an rdf graph describing what we know about the world
- """
- @inlineCallbacks
- def get(self):
- g = StateGraph(ctx=DEV['houseSensors'])
-
- frontDoorDefer = getPage("http://slash:9080/door", timeout=2) # head start?
-
- doorOpen = int((yield getPage("http://bang:9056/pin/d9", timeout=1)))
- g.add((DEV['theaterDoorOpen'], ROOM['state'],
- ROOM['open'] if doorOpen else ROOM['closed']))
-
- for s in self.motionStatements(
- currentMotion=int((yield getPage("http://bang:9056/pin/d10",
- timeout=1)))):
- g.add(s)
-
- try:
- for s in (yield self.getBedroomStatements()):
- g.add(s)
- except ConnectError, e:
- g.add((ROOM['bedroomStatementFetch'], ROOM['error'],
- Literal("getBedroomStatements: %s" % e)))
-
- try:
- frontDoor = yield frontDoorDefer
- g.add((DEV['frontDoorOpen'], ROOM['state'],
- ROOM[frontDoor] if frontDoor in ['open', 'closed'] else
- ROOM['error']))
- except Exception, e:
- g.add((DEV['frontDoorOpen'], ROOM['error'], Literal(str(e))))
-
- self.set_header('Content-type', 'application/x-trig')
- self.write(g.asTrig())
-
- def motionStatements(self, currentMotion):
- uri = DEV['theaterDoorOutsideMotion']
-
- yield (uri, ROOM['state'], ROOM['motion'] if currentMotion else ROOM['noMotion'])
-
- now = datetime.datetime.now(tzutc())
- if currentMotion:
- try:
- dt = now - self.settings.history['motionStart']
- yield (uri, ROOM['motionDurationSec'], Literal(dt.total_seconds()))
- if dt > datetime.timedelta(seconds=4):
- yield (uri, ROOM['state'], ROOM['sustainedMotion'])
- except KeyError:
- pass
-
- # this is history without the db, which means the window is
- # limited and it could reset any time
- if 'motionHistory' in self.settings.history:
- yield ((uri, ROOM['history'],
- Literal(simplejson.dumps(
- [(round((t - now).total_seconds(), ndigits=2), v)
- for t,v in self.settings.history['motionHistory']]))))
-
- @inlineCallbacks
- def getBedroomStatements(self):
- trig = yield getPage("http://bang:9088/graph", timeout=1)
- stmts = set()
- for line in trig.splitlines():
- if "http://projects.bigasterisk.com/device/bedroomMotion" in line:
- g = Graph()
- g.parse(StringInputSource(line+"\n"), format="nt")
- for s in g:
- stmts.add(s)
- returnValue(stmts)
-
-class Activity(PrettyErrorHandler, cyclone.web.RequestHandler):
- def get(self):
- a = ActivityStream()
- self.settings.mongo.ensure_index('t')
- remaining = {'downstairsDoorMotion':10, 'downstairsDoorOpen':10,
- 'frontDoorMotion':30, 'frontDoor':50, 'bedroomMotion': 10}
- recent = {}
- toAdd = []
- for row in list(self.settings.mongo.find(sort=[('t', -1)],
- limit=10000)):
- try:
- r = remaining[row['name']]
- if r < 1:
- continue
- remaining[row['name']] = r - 1
- except KeyError:
- pass
-
- # lots todo
- if row['name'] == 'downstairsDoorMotion':
- if row['level'] == 0:
- continue
- kw = dict(
- actorUri="http://...",
- actorName="downstairs door",
- verbUri="...",
- verbEnglish="sees",
- objectUri="...",
- objectName="backyard motion",
- objectIcon="/magma/static/backyardMotion.png")
- elif row['name'] == 'downstairsDoorOpen':
- kw = dict(actorUri="http://bigasterisk.com/foaf/someone",
- actorName="someone",
- verbUri="op",
- verbEnglish="opens" if row['level'] else "closes",
- objectUri="...",
- objectName="downstairs door",
- objectIcon="/magma/static/downstairsDoor.png")
- elif row['name'] == 'frontDoor':
- kw = dict(actorUri="http://bigasterisk.com/foaf/someone",
- actorName="someone",
- verbUri="op",
- verbEnglish="opens" if row['state']=='open' else "closes",
- objectUri="...",
- objectName="front door",
- objectIcon="/magma/static/frontDoor.png")
- elif row['name'] == 'frontDoorMotion':
- if row['state'] == False:
- continue
- if 'frontDoorMotion' in recent:
- pass#if row['t'
- kw = dict(
- actorUri="http://...",
- actorName="front door",
- verbUri="...",
- verbEnglish="sees",
- objectUri="...",
- objectName="front yard motion",
- objectIcon="/magma/static/frontYardMotion.png")
- recent['frontDoorMotion'] = kw
- elif row['name'] == 'bedroomMotion':
- if not row['state']:
- continue
- kw = dict(
- actorUri="http://...",
- actorName="bedroom",
- verbUri="...",
- verbEnglish="sees",
- objectUri="...",
- objectName="bedroom motion",
- objectIcon="/magma/static/bedroomMotion.png")
- recent['bedroomMotion'] = kw
- else:
- raise NotImplementedError(row)
-
- kw.update({'published' : row['t'],
- 'entryUriComponents' : ('sensor', row['board'])})
- toAdd.append(kw)
-
- toAdd.reverse()
- for kw in toAdd:
- a.addEntry(**kw)
-
- self.set_header("Content-type", "application/atom+xml")
- self.write(a.makeAtom())
-
-class Application(cyclone.web.Application):
- def __init__(self):
- handlers = [
- (r'/()', cyclone.web.StaticFileHandler,
- {"path" : ".", "default_filename" : "watchpins.html"}),
- (r'/pinChange', PinChange),
- (r'/inputChange', InputChange),
- (r'/activity', Activity),
- (r'/graph', GraphHandler),
- ]
- settings = {
- 'mongo' : Connection('bang', 27017,
- tz_aware=True)['house']['sensor'],
- 'history' : {
- },
- }
- cyclone.web.Application.__init__(self, handlers, **settings)
-
-if __name__ == '__main__':
- #from twisted.python import log as twlog
- #twlog.startLogging(sys.stdout)
- reactor.listenTCP(9069, Application())
- reactor.run()