Mercurial > code > home > repos > homeauto
changeset 732:fdddbdaf07b5
more service renaming; start a lot more serv.n3 job files
Ignore-this: 635aaefc7bd2fa5558eefb8b3fc9ec75
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/service/arduinoNode/serv.n3 Thu Feb 06 16:36:35 2020 -0800 @@ -0,0 +1,4 @@ +@prefix : <http://bigasterisk.com/ns/serv#> . +@prefix auth: <http://bigasterisk.com/ns/serv/auth#> . +@prefix serv: <http://bigasterisk.com/services/> . +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/service/audioInputLevels/serv.n3 Thu Feb 06 16:36:35 2020 -0800 @@ -0,0 +1,4 @@ +@prefix : <http://bigasterisk.com/ns/serv#> . +@prefix auth: <http://bigasterisk.com/ns/serv/auth#> . +@prefix serv: <http://bigasterisk.com/services/> . +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/service/collector/serv.n3 Thu Feb 06 16:36:35 2020 -0800 @@ -0,0 +1,4 @@ +@prefix : <http://bigasterisk.com/ns/serv#> . +@prefix auth: <http://bigasterisk.com/ns/serv/auth#> . +@prefix serv: <http://bigasterisk.com/services/> . +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/service/dhcpleases/serv.n3 Thu Feb 06 16:36:35 2020 -0800 @@ -0,0 +1,4 @@ +@prefix : <http://bigasterisk.com/ns/serv#> . +@prefix auth: <http://bigasterisk.com/ns/serv/auth#> . +@prefix serv: <http://bigasterisk.com/services/> . +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/service/dpms/serv.n3 Thu Feb 06 16:36:35 2020 -0800 @@ -0,0 +1,22 @@ +@prefix : <http://bigasterisk.com/ns/serv#> . +@prefix auth: <http://bigasterisk.com/ns/serv/auth#> . +@prefix serv: <http://bigasterisk.com/services/> . + +serv:dpms a :Service; + :path "/dpms/"; + :openid auth:admin; + :serverHost "bang"; + :internalPort 9095; + :prodDockerFlags ( + "-p" "9095:9095" + "--net=host" + ); + :localDockerFlags ( + "-v" "`pwd`:/opt" + ); + :localRunCmdline ( + "python3" "dpms.py" "-v" + ); + :dockerFile "Dockerfile" +. +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/service/environment/serv.n3 Thu Feb 06 16:36:35 2020 -0800 @@ -0,0 +1,4 @@ +@prefix : <http://bigasterisk.com/ns/serv#> . +@prefix auth: <http://bigasterisk.com/ns/serv/auth#> . +@prefix serv: <http://bigasterisk.com/services/> . +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/service/frontDoorLock/serv.n3 Thu Feb 06 16:36:35 2020 -0800 @@ -0,0 +1,4 @@ +@prefix : <http://bigasterisk.com/ns/serv#> . +@prefix auth: <http://bigasterisk.com/ns/serv/auth#> . +@prefix serv: <http://bigasterisk.com/services/> . +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/service/irRemote/serv.n3 Thu Feb 06 16:36:35 2020 -0800 @@ -0,0 +1,4 @@ +@prefix : <http://bigasterisk.com/ns/serv#> . +@prefix auth: <http://bigasterisk.com/ns/serv/auth#> . +@prefix serv: <http://bigasterisk.com/services/> . +
--- a/service/mqtt_graph_bridge/Dockerfile Wed Feb 05 17:20:28 2020 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ -FROM bang6:5000/base_x86 - -WORKDIR /opt - -COPY requirements.txt ./ -RUN pip3 install --index-url https://projects.bigasterisk.com/ --extra-index-url https://pypi.org/simple -r requirements.txt -RUN pip3 install -U 'https://github.com/drewp/cyclone/archive/python3.zip?v3' - -COPY *.py *.html *.css *.js ./ - -EXPOSE 10011:10011 - -CMD [ "python3", "./mqtt_graph_bridge.py" ]
--- a/service/mqtt_graph_bridge/config.n3 Wed Feb 05 17:20:28 2020 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,54 +0,0 @@ -@prefix : <http://projects.bigasterisk.com/room/> . -@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . -@prefix fr: <http://bigasterisk.com/foaf/> . - -:kitchenSkylight a :Device; - :mqttTopicHead ("h801_skylight" "light"); - :convertedAttr [ - :deviceAttr :white; - :mqttTopicTail ("kit_w1" "command"); - :valueConversion :to8Bit; - :message '{"state":"ON","brightness":%value%}' - ], - :convertedAttr [ - :deviceAttr :color; - :mqttTopicTail ("kit_r" "command"); - :valueConversion :extractRed8Bit; - :message '{"state":"ON","brightness":%value%}' - ], - :convertedAttr [ - :deviceAttr :color; - :mqttTopicTail ("kit_g" "command"); - :valueConversion :extractGreen8Bit; - :message '{"state":"ON","brightness":%value%}' - ], - :convertedAttr [ - :deviceAttr :color; - :mqttTopicTail ("kit_r" "command"); - :valueConversion :extractBlue8Bit; - :message '{"state":"ON","brightness":%value%}' - ], - -:nightlightAriTemperature a :ExportedMeasurement; - :mqttTopicHead ("nightlight_ari" "sensor" "temperature" "state"); - - :missingAfterSec 150; - :ignoreInputValueBelow -999; - - :valueProcess [ - :conversion :celsiusToFarenheit; #and round(x,2) - ]; - - :graphStatements [ - :outputPredicate :temperatureF; - :statementLifetime :untilReplaced; - - # is this just another valueProcess? - :outputRecentPredicate :recentLowTemperatureF; :recentPeriodSec 30; - ], - - :influxMeasurement [ - :measurement "temperatureF"; - :predicate :temperatureF; - :tag [:key "host"; :value "nightlight_ari"], - [:key "location"; :value "ariRoom"]] .
--- a/service/mqtt_graph_bridge/h801/config_counter.yaml Wed Feb 05 17:20:28 2020 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,73 +0,0 @@ -substitutions: - pwm_freq: 310 Hz - -#310 low buzz, nicer than 520 -#520 low pitch buzz, ok -#600 sound is present but might be ok -#800 bad sound -#1500 sound is bad but we had it like that -#3000 very bad snd -#6000 bad snd and flicker -#9000 flicker - -esphomeyaml: - name: h801_counter - platform: ESP8266 - board: esp8285 - build_path: build - -wifi: - ssid: !secret wifi_ssid - password: !secret wifi_password - manual_ip: - static_ip: 10.2.0.59 - gateway: 10.2.0.1 - subnet: 255.255.255.0 - -mqtt: - broker: '10.2.0.1' - port: 1883 - -logger: - baud_rate: 115200 - -ota: - -output: - - id: pwm_b - platform: esp8266_pwm - pin: 12 - frequency: ${pwm_freq} - - id: pwm_g - platform: esp8266_pwm - pin: 13 - frequency: ${pwm_freq} - - id: pwm_r - platform: esp8266_pwm - pin: 15 - frequency: ${pwm_freq} - - id: pwm_w1 - platform: esp8266_pwm - pin: 14 - frequency: ${pwm_freq} - - id: pwm_w2 - platform: esp8266_pwm - pin: 4 - frequency: ${pwm_freq} - -light: - - platform: monochromatic - name: "Kit_r" - output: pwm_r - - platform: monochromatic - name: "Kit_g" - output: pwm_g - - platform: monochromatic - name: "Kit_b" - output: pwm_b - - platform: monochromatic - name: "Kit_w1" - output: pwm_w1 - - platform: monochromatic - name: "Kit_w2" - output: pwm_w2
--- a/service/mqtt_graph_bridge/h801/config_skylight.yaml Wed Feb 05 17:20:28 2020 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,64 +0,0 @@ -substitutions: - pwm_freq: 310 Hz - -esphomeyaml: - name: h801_skylight - platform: ESP8266 - board: esp8285 - build_path: build - -wifi: - ssid: !secret wifi_ssid - password: !secret wifi_password - manual_ip: - static_ip: 10.2.0.67 - gateway: 10.2.0.1 - subnet: 255.255.255.0 - -mqtt: - broker: '10.2.0.1' - port: 1883 - -logger: - baud_rate: 115200 - -ota: - -output: - - id: pwm_b - platform: esp8266_pwm - pin: 12 - frequency: ${pwm_freq} - - id: pwm_r - platform: esp8266_pwm - pin: 13 - frequency: ${pwm_freq} - - id: pwm_g - platform: esp8266_pwm - pin: 15 - frequency: ${pwm_freq} - - id: pwm_w1 - platform: esp8266_pwm - pin: 14 - frequency: ${pwm_freq} - - id: pwm_w2 - platform: esp8266_pwm - pin: 4 - frequency: ${pwm_freq} - -light: - - platform: monochromatic - name: "Kit_r" - output: pwm_r - - platform: monochromatic - name: "Kit_g" - output: pwm_g - - platform: monochromatic - name: "Kit_b" - output: pwm_b - - platform: monochromatic - name: "Kit_w1" - output: pwm_w1 - - platform: monochromatic - name: "Kit_w2" - output: pwm_w2
--- a/service/mqtt_graph_bridge/h801/makefile Wed Feb 05 17:20:28 2020 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -update: - /my/dl/dl/esphomeyaml/bin/esphomeyaml config_skylight.yaml run --no-logs
--- a/service/mqtt_graph_bridge/index.html Wed Feb 05 17:20:28 2020 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,35 +0,0 @@ -<!doctype html> -<html> - <head> - <title>mqtt_graph_bridge</title> - <meta charset="utf-8"> - <script src="/lib/polymer/1.0.9/webcomponentsjs/webcomponents.min.js"></script> - <script src="/lib/require/require-2.3.3.js"></script> - <script src="/rdf/common_paths_and_ns.js"></script> - - <link rel="stylesheet" href="/rdf/browse/style.css"> - - </head> - <body class="rdfBrowsePage"> - mqtt_graph_bridge - - - <p>Send demo statements to bridge:</p> - <div><button data-post="output?s=:kitchenLight&p=:brightness" data-body="0.0">Send (:kitchenLight :brightness 0.0)</button></div> - <div><button data-post="output?s=:kitchenLight&p=:brightness" data-body="1.0">Send (:kitchenLight :brightness 1.0)</button></div> - <div><button data-post="output?s=:livingLampShelf&p=:brightness" data-body="0.0">Send (:livingLampShelf :brightness 0.0)</button></div> - <div><button data-post="output?s=:livingLampShelf&p=:brightness" data-body="1.0">Send (:livingLampShelf :brightness 1.0)</button></div> - - <script> - Array.from(document.querySelectorAll("button")).forEach((el) => { - el.addEventListener("click", (ev) => { - fetch(el.dataset.post, { - method: "PUT", - body: el.dataset.body - }); - }); - }); - </script> - - </body> -</html>
--- a/service/mqtt_graph_bridge/mqtt_graph_bridge.py Wed Feb 05 17:20:28 2020 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,130 +0,0 @@ -""" -We get output statements that are like light9's deviceAttrs (:dev1 :color "#ff0000"), -convert those to outputAttrs (:dev1 :red 255; :green 0; :blue 0) and post them to mqtt. - -This is like light9/bin/collector. -""" -import json - -from docopt import docopt -from rdflib import Namespace, Literal -from twisted.internet import reactor -import cyclone.web - -from mqtt_client import MqttClient -from patchablegraph import PatchableGraph, CycloneGraphHandler, CycloneGraphEventsHandler -from standardservice.logsetup import log, verboseLogging -import rdf_over_http - -ROOM = Namespace('http://projects.bigasterisk.com/room/') - -devs = { - ROOM['kitchenLight']: { - 'root': 'h801_skylight', - 'ctx': ROOM['kitchenH801'] - }, - ROOM['kitchenCounterLight']: { - 'root': 'h801_counter', - 'ctx': ROOM['kitchenH801'] - }, - ROOM['livingLampShelf']: { - 'root': 'sonoff_0/switch/sonoff_basic_relay/command', - 'ctx': ROOM['sonoff_0'], - 'values': 'binary', - }, -} - - -class OutputPage(cyclone.web.RequestHandler): - def put(self): - for stmt in rdf_over_http.rdfStatementsFromRequest( - self.request.arguments, - self.request.body, - self.request.headers): - self._onStatement(stmt) - - def _onStatement(self, stmt): - log.info(f'incoming statement: {stmt}') - ignored = True - for dev, attrs in devs.items(): - if stmt[0] == ROOM['frontWindow']: - ignored = ignored and self._publishFrontScreenText(stmt) - - if stmt[0:2] == (dev, ROOM['brightness']): - log.info(f'brightness request: {stmt}') - brightness = stmt[2].toPython() - - if attrs.get('values', '') == 'binary': - self._publishOnOff(attrs, brightness) - else: - self._publishRgbw(attrs, brightness) - # try to stop saving this; let the device be the master usually - self.settings.masterGraph.patchObject( - attrs['ctx'], - stmt[0], stmt[1], stmt[2]) - ignored = False - if ignored: - log.warn("ignoring %s", stmt) - - def _publishOnOff(self, attrs, brightness): - msg = 'OFF' - if brightness > 0: - msg = 'ON' - self._publish(topic=attrs['root'], message=msg) - - def _publishRgbw(self, attrs, brightness): - for chan, scale in [('w1', 1), - ('r', 1), - ('g', .8), - ('b', .8)]: - self._publish( - topic=f"{attrs['root']}/light/kit_{chan}/command", - messageJson={ - 'state': 'ON', - 'brightness': int(brightness * 255) - }) - - def _publishFrontScreenText(self, stmt): - ignored = True - for line in ['line1', 'line2', 'line3', 'line4']: - if stmt[1] == ROOM[line]: - ignored = False - self.settings.mqtt.publish( - b'frontwindow/%s' % line.encode('ascii'), - stmt[2].toPython()) - return ignored - - def _publish(self, topic: str, messageJson: object=None, - message: str=None): - if messageJson is not None: - message = json.dumps(messageJson) - self.settings.mqtt.publish( - topic.encode('ascii'), - message.encode('ascii')) - - -if __name__ == '__main__': - arg = docopt(""" - Usage: mqtt_graph_bridge.py [options] - - -v Verbose - """) - verboseLogging(arg['-v']) - - masterGraph = PatchableGraph() - - mqtt = MqttClient(clientId='mqtt_graph_bridge', brokerPort=1883) - - port = 10008 - reactor.listenTCP(port, cyclone.web.Application([ - (r"/()", cyclone.web.StaticFileHandler, - {"path": ".", "default_filename": "index.html"}), - (r'/output', OutputPage), - ], mqtt=mqtt, debug=arg['-v']), interface='::') - log.warn('serving on %s', port) - - for dev, attrs in devs.items(): - masterGraph.patchObject(attrs['ctx'], - dev, ROOM['brightness'], Literal(0.0)) - - reactor.run()
--- a/service/mqtt_graph_bridge/rdf_over_http.py Wed Feb 05 17:20:28 2020 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34 +0,0 @@ -from rdflib import Graph, URIRef, Literal, Namespace -from rdflib.parser import StringInputSource - -ROOM = Namespace('http://projects.bigasterisk.com/room/') - - -def rdfGraphBody(body, headers): - g = Graph() - g.parse(StringInputSource(body), format='nt') - return g - - -def expandQueryParamUri(txt) -> URIRef: - if txt.startswith(':'): - return ROOM[txt.lstrip(':')] - # etc - return URIRef(txt) - - -def rdfStatementsFromRequest(arg, body, headers): - if arg.get('s') and arg.get('p'): - subj = expandQueryParamUri(arg['s'][-1]) - pred = expandQueryParamUri(arg['p'][-1]) - turtleLiteral = body - try: - obj = Literal(float(turtleLiteral)) - except ValueError: - obj = Literal(turtleLiteral) - yield (subj, pred, obj) - else: - g = rdfGraphBody(body, headers) - assert len(g) == 1, len(g) - yield g.triples((None, None, None)).next() - # could support multiple stmts
--- a/service/mqtt_graph_bridge/readme Wed Feb 05 17:20:28 2020 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -tail all changes: -mosquitto_sub -v -t 004BD965/\# - -bang(pts/8):~% mosquitto_pub -t 004BD965/w1/light/switch -m ON -bang(pts/8):~% mosquitto_pub -t 004BD965/w1/light/switch -m OFF - -
--- a/service/mqtt_graph_bridge/requirements.txt Wed Feb 05 17:20:28 2020 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ -cyclone -rdflib-jsonld==0.4.0 -rdflib==4.2.2 -twisted-mqtt==0.3.6 -rx==1.6.1 -git+http://github.com/drewp/scales.git@448d59fb491b7631877528e7695a93553bfaaa93#egg=scales - -cycloneerr -patchablegraph==0.11.0 -rdfdb==0.21.0 -standardservice==0.6.0 -mqtt_client==0.7.0
--- a/service/mqtt_graph_bridge/tasks.py Wed Feb 05 17:20:28 2020 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,40 +0,0 @@ -from invoke import task - -JOB = 'mqtt_graph_bridge' -PORT = 10008 -TAG = f'bang6:5000/{JOB}_x86:latest' - -@task -def build_image(ctx): - ctx.run(f'docker build --network=host -t {TAG} .') - -@task(pre=[build_image]) -def push_image(ctx): - ctx.run(f'docker push {TAG}') - -@task(pre=[build_image]) -def shell(ctx): - ctx.run(f'docker run --name={JOB}_shell --rm -it --cap-add SYS_PTRACE --net=host {TAG} /bin/bash', pty=True) - -@task(pre=[build_image]) -def local_run(ctx): - ctx.run(f'docker run --name={JOB}_local --rm -it --net=host {TAG} python3 mqtt_graph_bridge.py -v', pty=True) - -@task(pre=[push_image]) -def redeploy(ctx): - ctx.run(f'supervisorctl -s http://bang:9001/ restart {JOB}_{PORT}') - -@task -def program_board_over_usb(ctx): - tag = 'esphome/esphome' - ctx.run(f"docker run --rm -v `pwd`:/config --device=/dev/ttyUSB0 -it {tag} door.yaml run", pty=True) -# config_skylight.yaml run --no-logs - -@task -def monitor_usb(ctx): - tag = 'esphome/esphome' - ctx.run(f"docker run --rm -v `pwd`:/config --device=/dev/ttyUSB0 -it {tag} door.yaml logs", pty=True) - -@task -def tail_mqtt(ctx): - ctx.run(f'mosquitto_sub -h bang -p 10010 -d -v -t \#')
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/service/mqtt_to_rdf/Dockerfile Thu Feb 06 16:36:35 2020 -0800 @@ -0,0 +1,13 @@ +FROM bang6:5000/base_x86 + +WORKDIR /opt + +COPY requirements.txt ./ +RUN pip3 install --index-url https://projects.bigasterisk.com/ --extra-index-url https://pypi.org/simple -r requirements.txt +RUN pip3 install -U 'https://github.com/drewp/cyclone/archive/python3.zip?v3' + +COPY *.py *.html *.css *.js *.n3 ./ + +EXPOSE 10018:10018 + +CMD [ "python3", "./rdf_from_mqtt.py" ]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/service/mqtt_to_rdf/config_bed_bar.n3 Thu Feb 06 16:36:35 2020 -0800 @@ -0,0 +1,27 @@ +@prefix : <http://projects.bigasterisk.com/room/> . +@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . +@prefix fr: <http://bigasterisk.com/foaf/> . + +:buttonMap a :ValueMap; + :map [:from "OFF"; :to :notPressed], [:from "ON"; :to :pressed] + . + +:bedBarAsherButton1 a :MqttStatementSource; + :mqttTopic ("bed_bar_asher" "binary_sensor" "button_1" "state"); + :parser :buttonMap; + :graphStatements [:outputPredicate :state;] . + +:bedBarAsherButton2 a :MqttStatementSource; + :mqttTopic ("bed_bar_asher" "binary_sensor" "button_2" "state"); + :parser :buttonMap; + :graphStatements [:outputPredicate :state;] . + +:bedBarAsherButton3 a :MqttStatementSource; + :mqttTopic ("bed_bar_asher" "binary_sensor" "button_3" "state"); + :parser :buttonMap; + :graphStatements [:outputPredicate :state;] . + +:bedBarAsherButton4 a :MqttStatementSource; + :mqttTopic ("bed_bar_asher" "binary_sensor" "button_4" "state"); + :parser :buttonMap; + :graphStatements [:outputPredicate :state;] . \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/service/mqtt_to_rdf/config_cardreader.n3 Thu Feb 06 16:36:35 2020 -0800 @@ -0,0 +1,14 @@ +@prefix : <http://projects.bigasterisk.com/room/> . +@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . +@prefix fr: <http://bigasterisk.com/foaf/> . + +:cardReader a :MqttStatementSource; + :mqttTopic ("frontwindow" "tag"); + :parser :tagIdToUri; # AA-BB-CC-DD to <http://bigasterisk.com/rfidCard/aabbccdd> + + :graphStatements [ + :outputPredicate :currentRead; + :statementLifetime "5s"; + ] + . + \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/service/mqtt_to_rdf/config_nightlight_ari.n3 Thu Feb 06 16:36:35 2020 -0800 @@ -0,0 +1,26 @@ +@prefix : <http://projects.bigasterisk.com/room/> . +@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . +@prefix fr: <http://bigasterisk.com/foaf/> . +@prefix xsd: <http://www.w3.org/2001/XMLSchema#> . + + +:nightlightAriTemperature a :MqttStatementSource; + :mqttTopic ("nightlight_ari" "sensor" "temperature" "state"); + + :parser xsd:double; + :conversions (:celsiusToFarenheit + [:ignoreValueBelow -999]); + :graphStatements [ + :outputPredicate :temperatureF; + :statementLifetime "150s"; + # ], [ + # :conversions ([:recentLow "30s"]); + # :outputPredicate :recentLowTemperatureF; + ]; + + :influxMeasurement [ # replaces this block in piNode configs + :measurement "temperatureF"; + :predicate :temperatureF; + :tag [:key "host"; :value "nightlight_ari"], + [:key "location"; :value "ariRoom"]] . +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/service/mqtt_to_rdf/index.html Thu Feb 06 16:36:35 2020 -0800 @@ -0,0 +1,32 @@ +<!doctype html> +<html> + <head> + <title>rdf_from_mqtt</title> + <meta charset="utf-8"> + <script src="/lib/polymer/1.0.9/webcomponentsjs/webcomponents.min.js"></script> + <script src="/lib/require/require-2.3.3.js"></script> + <script src="/rdf/common_paths_and_ns.js"></script> + + <link rel="stylesheet" href="/rdf/browse/style.css"> + + <link rel="import" href="/rdf/streamed-graph.html"> + <link rel="import" href="/lib/polymer/1.0.9/polymer/polymer.html"> + + <meta name="mobile-web-app-capable" content="yes"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + </head> + <body class="rdfBrowsePage"> + <template id="t" is="dom-bind"> + <streamed-graph url="mqtt/events" graph="{{graph}}"></streamed-graph> + <div id="out"></div> + <script type="module" src="/rdf/streamed_graph_view.js"></script> + </template> + + <div class="served-resources"> + <a href="stats/">/stats/</a> + <a href="mqtt">/mqtt</a> + <a href="mqtt/events">/mqtt/events</a> + </div> + + </body> +</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/service/mqtt_to_rdf/rdf_from_mqtt.py Thu Feb 06 16:36:35 2020 -0800 @@ -0,0 +1,202 @@ +""" +Subscribe to mqtt topics; generate RDF statements. +""" +import json +import sys +from docopt import docopt +from rdflib import Namespace, URIRef, Literal, Graph, RDF, XSD +from rdflib.parser import StringInputSource +from rdflib.term import Node +from twisted.internet import reactor +import cyclone.web +import rx, rx.operators, rx.scheduler.eventloop +from greplin import scales +from greplin.scales.cyclonehandler import StatsHandler + +from export_to_influxdb import InfluxExporter +from mqtt_client import MqttClient + +from patchablegraph import PatchableGraph, CycloneGraphHandler, CycloneGraphEventsHandler +from rdfdb.patch import Patch +from rdfdb.rdflibpatch import graphFromQuads +from standardservice.logsetup import log, verboseLogging +from standardservice.scalessetup import gatherProcessStats + +ROOM = Namespace('http://projects.bigasterisk.com/room/') + +gatherProcessStats() + +def parseDurationLiteral(lit: Literal) -> float: + if lit.endswith('s'): + return float(lit.split('s')[0]) + raise NotImplementedError(f'duration literal: {lit}') + + +class MqttStatementSource: + def __init__(self, uri, config, masterGraph, mqtt, influx): + self.uri = uri + self.config = config + self.masterGraph = masterGraph + self.mqtt = mqtt + self.influx = influx + + self.mqttTopic = self.topicFromConfig(self.config) + + statPath = '/subscribed_topic/' + self.mqttTopic.decode('ascii').replace('/', '|') + scales.init(self, statPath) + self._mqttStats = scales.collection( + statPath + '/incoming', scales.IntStat('count'), + scales.RecentFpsStat('fps')) + + + rawBytes = self.subscribeMqtt() + rawBytes = rx.operators.do_action(self.countIncomingMessage)(rawBytes) + parsed = self.getParser()(rawBytes) + + g = self.config + for conv in g.items(g.value(self.uri, ROOM['conversions'])): + parsed = self.conversionStep(conv)(parsed) + + outputQuadsSets = rx.combine_latest( + *[self.makeQuads(parsed, plan) + for plan in g.objects(self.uri, ROOM['graphStatements'])]) + + outputQuadsSets.subscribe_(self.updateQuads) + + def topicFromConfig(self, config) -> bytes: + topicParts = list(config.items(config.value(self.uri, ROOM['mqttTopic']))) + return b'/'.join(t.encode('ascii') for t in topicParts) + + + def subscribeMqtt(self): + return self.mqtt.subscribe(self.mqttTopic) + + def countIncomingMessage(self, _): + self._mqttStats.fps.mark() + self._mqttStats.count += 1 + + def getParser(self): + g = self.config + parser = g.value(self.uri, ROOM['parser']) + if parser == XSD.double: + return rx.operators.map(lambda v: Literal(float(v.decode('ascii')))) + elif parser == ROOM['tagIdToUri']: + return rx.operators.map(self.tagIdToUri) + elif parser == ROOM['onOffBrightness']: + return rx.operators.map(lambda v: Literal(0.0 if v == b'OFF' else 1.0)) + elif parser == ROOM['jsonBrightness']: + return rx.operators.map(self.parseJsonBrightness) + elif ROOM['ValueMap'] in g.objects(parser, RDF.type): + return rx.operators.map(lambda v: self.remap(parser, v.decode('ascii'))) + else: + raise NotImplementedError(parser) + + def parseJsonBrightness(self, mqttValue: bytes): + msg = json.loads(mqttValue.decode('ascii')) + return Literal(float(msg['brightness'] / 255) if msg['state'] == 'ON' else 0.0) + + def conversionStep(self, conv: Node): + g = self.config + if conv == ROOM['celsiusToFarenheit']: + return rx.operators.map(lambda value: Literal(round(value.toPython() * 1.8 + 32, 2))) + elif g.value(conv, ROOM['ignoreValueBelow'], default=None) is not None: + threshold = g.value(conv, ROOM['ignoreValueBelow']) + return rx.operators.filter(lambda value: value.toPython() >= threshold.toPython()) + else: + raise NotImplementedError(conv) + + def makeQuads(self, parsed, plan): + g = self.config + def quadsFromValue(valueNode): + return set([ + (self.uri, + g.value(plan, ROOM['outputPredicate']), + valueNode, + self.uri) + ]) + + def emptyQuads(element): + return set([]) + + quads = rx.operators.map(quadsFromValue)(parsed) + + dur = g.value(plan, ROOM['statementLifetime']) + if dur is not None: + sec = parseDurationLiteral(dur) + quads = quads.pipe( + rx.operators.debounce(sec, rx.scheduler.eventloop.TwistedScheduler(reactor)), + rx.operators.map(emptyQuads), + rx.operators.merge(quads), + ) + + return quads + + def updateQuads(self, newGraphs): + newQuads = set.union(*newGraphs) + g = graphFromQuads(newQuads) + log.debug(f'{self.uri} update to {len(newQuads)} statements') + + self.influx.exportToInflux(newQuads) + + self.masterGraph.patchSubgraph(self.uri, g) + + def tagIdToUri(self, value: bytearray) -> URIRef: + justHex = value.decode('ascii').replace('-', '').lower() + int(justHex, 16) # validate + return URIRef(f'http://bigasterisk.com/rfidCard/{justHex}') + + def remap(self, parser, valueStr: str): + g = self.config + value = Literal(valueStr) + for entry in g.objects(parser, ROOM['map']): + if value == g.value(entry, ROOM['from']): + return g.value(entry, ROOM['to']) + raise KeyError(value) + + +if __name__ == '__main__': + arg = docopt(""" + Usage: rdf_from_mqtt.py [options] + + -v Verbose + --cs=STR Only process config filenames with this substring + """) + verboseLogging(arg['-v']) + + config = Graph() + for fn in [ + "config_cardreader.n3", + "config_nightlight_ari.n3", + "config_bed_bar.n3", + "config_air_quality_indoor.n3", + "config_air_quality_outdoor.n3", + "config_living_lamps.n3", + "config_kitchen.n3", + ]: + if not arg['--cs'] or arg['--cs'] in fn: + config.parse(fn, format='n3') + + masterGraph = PatchableGraph() + + mqtt = MqttClient(clientId='rdf_from_mqtt', brokerHost='bang', + brokerPort=1883) + influx = InfluxExporter(config) + + srcs = [] + for src in config.subjects(RDF.type, ROOM['MqttStatementSource']): + srcs.append(MqttStatementSource(src, config, masterGraph, mqtt, influx)) + log.info(f'set up {len(srcs)} sources') + + port = 10018 + reactor.listenTCP(port, cyclone.web.Application([ + (r"/()", cyclone.web.StaticFileHandler, + {"path": ".", "default_filename": "index.html"}), + (r'/stats/(.*)', StatsHandler, {'serverName': 'rdf_from_mqtt'}), + (r"/graph/mqtt", CycloneGraphHandler, {'masterGraph': masterGraph}), + (r"/graph/mqtt/events", CycloneGraphEventsHandler, + {'masterGraph': masterGraph}), + ], mqtt=mqtt, masterGraph=masterGraph, debug=arg['-v']), + interface='::') + log.warn('serving on %s', port) + + reactor.run()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/service/mqtt_to_rdf/requirements.txt Thu Feb 06 16:36:35 2020 -0800 @@ -0,0 +1,15 @@ +pytype + +cyclone +rdflib-jsonld==0.4.0 +rdflib==4.2.2 +twisted-mqtt==0.3.6 +git+http://github.com/drewp/scales.git@550bcba42c5e96152ff5c5bd753fbb33ffdfe460#egg=scales +git+https://github.com/ReactiveX/RxPY.git@6deb66e827f34a88b4605773d7671322b9cbbd08#egg=rx + +cycloneerr +export_to_influxdb==0.4.0 +mqtt_client==0.9.0 +patchablegraph==0.11.0 +rdfdb==0.21.0 +standardservice==0.6.0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/service/mqtt_to_rdf/serv.n3 Thu Feb 06 16:36:35 2020 -0800 @@ -0,0 +1,24 @@ +@prefix : <http://bigasterisk.com/ns/serv#> . +@prefix auth: <http://bigasterisk.com/ns/serv/auth#> . +@prefix serv: <http://bigasterisk.com/services/> . + +serv:mqtt_to_rdf a :Service; + :path "/mqtt_to_rdf/"; + :openid auth:admin; + :serverHost "bang"; + :internalPort 10018; + :prodDockerFlags ( + "-p" "10018:10018" + "--net=host"); + :localDockerFlags ( + "-v" "`pwd`:/opt" + "-v" "/my/proj/homeauto/lib:/lib_src" + ); + :localRunCmdline ( + + "python3" "mqtt_to_rdf.py" "-v" +#"--cs" "living" +); + :dockerFile "Dockerfile" +. +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/service/mqtt_to_rdf/tasks.py Thu Feb 06 16:36:35 2020 -0800 @@ -0,0 +1,14 @@ +from invoke import task, Collection + +import sys +sys.path.append('/my/proj/release') +from serv_tasks import serv_tasks + +ns = Collection() +serv_tasks(ns, 'serv.n3', 'mqtt_to_rdf') + +@ns.add_task +@task +def tail_mqtt(ctx): + internal_mqtt_port = 10010 + ctx.run(f'mosquitto_sub -h bang -p 1883 -d -v -t \#')
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/service/piNode/serv.n3 Thu Feb 06 16:36:35 2020 -0800 @@ -0,0 +1,4 @@ +@prefix : <http://bigasterisk.com/ns/serv#> . +@prefix auth: <http://bigasterisk.com/ns/serv/auth#> . +@prefix serv: <http://bigasterisk.com/services/> . +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/service/powerEagle/serv.n3 Thu Feb 06 16:36:35 2020 -0800 @@ -0,0 +1,23 @@ +@prefix : <http://bigasterisk.com/ns/serv#> . +@prefix auth: <http://bigasterisk.com/ns/serv/auth#> . +@prefix serv: <http://bigasterisk.com/services/> . + + +serv:powerEagle a :Service; + :path "/powerEagle/"; + :openid auth:admin; + :serverHost "bang"; + :internalPort 10016; + :prodDockerFlags ( + "-p" "10016:10016" + "--net=host" + ); + :localDockerFlags ( + "-v" "`pwd`:/opt" + ); + :localRunCmdline ( + "python3" "reader.py" "-v" + ); + :dockerFile "Dockerfile" +. +
--- a/service/rdf_from_mqtt/Dockerfile Wed Feb 05 17:20:28 2020 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ -FROM bang6:5000/base_x86 - -WORKDIR /opt - -COPY requirements.txt ./ -RUN pip3 install --index-url https://projects.bigasterisk.com/ --extra-index-url https://pypi.org/simple -r requirements.txt -RUN pip3 install -U 'https://github.com/drewp/cyclone/archive/python3.zip?v3' - -COPY *.py *.html *.css *.js *.n3 ./ - -EXPOSE 10018:10018 - -CMD [ "python3", "./rdf_from_mqtt.py" ]
--- a/service/rdf_from_mqtt/config_bed_bar.n3 Wed Feb 05 17:20:28 2020 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,27 +0,0 @@ -@prefix : <http://projects.bigasterisk.com/room/> . -@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . -@prefix fr: <http://bigasterisk.com/foaf/> . - -:buttonMap a :ValueMap; - :map [:from "OFF"; :to :notPressed], [:from "ON"; :to :pressed] - . - -:bedBarAsherButton1 a :MqttStatementSource; - :mqttTopic ("bed_bar_asher" "binary_sensor" "button_1" "state"); - :parser :buttonMap; - :graphStatements [:outputPredicate :state;] . - -:bedBarAsherButton2 a :MqttStatementSource; - :mqttTopic ("bed_bar_asher" "binary_sensor" "button_2" "state"); - :parser :buttonMap; - :graphStatements [:outputPredicate :state;] . - -:bedBarAsherButton3 a :MqttStatementSource; - :mqttTopic ("bed_bar_asher" "binary_sensor" "button_3" "state"); - :parser :buttonMap; - :graphStatements [:outputPredicate :state;] . - -:bedBarAsherButton4 a :MqttStatementSource; - :mqttTopic ("bed_bar_asher" "binary_sensor" "button_4" "state"); - :parser :buttonMap; - :graphStatements [:outputPredicate :state;] . \ No newline at end of file
--- a/service/rdf_from_mqtt/config_cardreader.n3 Wed Feb 05 17:20:28 2020 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,14 +0,0 @@ -@prefix : <http://projects.bigasterisk.com/room/> . -@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . -@prefix fr: <http://bigasterisk.com/foaf/> . - -:cardReader a :MqttStatementSource; - :mqttTopic ("frontwindow" "tag"); - :parser :tagIdToUri; # AA-BB-CC-DD to <http://bigasterisk.com/rfidCard/aabbccdd> - - :graphStatements [ - :outputPredicate :currentRead; - :statementLifetime "5s"; - ] - . - \ No newline at end of file
--- a/service/rdf_from_mqtt/config_nightlight_ari.n3 Wed Feb 05 17:20:28 2020 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,26 +0,0 @@ -@prefix : <http://projects.bigasterisk.com/room/> . -@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . -@prefix fr: <http://bigasterisk.com/foaf/> . -@prefix xsd: <http://www.w3.org/2001/XMLSchema#> . - - -:nightlightAriTemperature a :MqttStatementSource; - :mqttTopic ("nightlight_ari" "sensor" "temperature" "state"); - - :parser xsd:double; - :conversions (:celsiusToFarenheit - [:ignoreValueBelow -999]); - :graphStatements [ - :outputPredicate :temperatureF; - :statementLifetime "150s"; - # ], [ - # :conversions ([:recentLow "30s"]); - # :outputPredicate :recentLowTemperatureF; - ]; - - :influxMeasurement [ # replaces this block in piNode configs - :measurement "temperatureF"; - :predicate :temperatureF; - :tag [:key "host"; :value "nightlight_ari"], - [:key "location"; :value "ariRoom"]] . -
--- a/service/rdf_from_mqtt/index.html Wed Feb 05 17:20:28 2020 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,32 +0,0 @@ -<!doctype html> -<html> - <head> - <title>rdf_from_mqtt</title> - <meta charset="utf-8"> - <script src="/lib/polymer/1.0.9/webcomponentsjs/webcomponents.min.js"></script> - <script src="/lib/require/require-2.3.3.js"></script> - <script src="/rdf/common_paths_and_ns.js"></script> - - <link rel="stylesheet" href="/rdf/browse/style.css"> - - <link rel="import" href="/rdf/streamed-graph.html"> - <link rel="import" href="/lib/polymer/1.0.9/polymer/polymer.html"> - - <meta name="mobile-web-app-capable" content="yes"> - <meta name="viewport" content="width=device-width, initial-scale=1"> - </head> - <body class="rdfBrowsePage"> - <template id="t" is="dom-bind"> - <streamed-graph url="mqtt/events" graph="{{graph}}"></streamed-graph> - <div id="out"></div> - <script type="module" src="/rdf/streamed_graph_view.js"></script> - </template> - - <div class="served-resources"> - <a href="stats/">/stats/</a> - <a href="mqtt">/mqtt</a> - <a href="mqtt/events">/mqtt/events</a> - </div> - - </body> -</html>
--- a/service/rdf_from_mqtt/rdf_from_mqtt.py Wed Feb 05 17:20:28 2020 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,202 +0,0 @@ -""" -Subscribe to mqtt topics; generate RDF statements. -""" -import json -import sys -from docopt import docopt -from rdflib import Namespace, URIRef, Literal, Graph, RDF, XSD -from rdflib.parser import StringInputSource -from rdflib.term import Node -from twisted.internet import reactor -import cyclone.web -import rx, rx.operators, rx.scheduler.eventloop -from greplin import scales -from greplin.scales.cyclonehandler import StatsHandler - -from export_to_influxdb import InfluxExporter -from mqtt_client import MqttClient - -from patchablegraph import PatchableGraph, CycloneGraphHandler, CycloneGraphEventsHandler -from rdfdb.patch import Patch -from rdfdb.rdflibpatch import graphFromQuads -from standardservice.logsetup import log, verboseLogging -from standardservice.scalessetup import gatherProcessStats - -ROOM = Namespace('http://projects.bigasterisk.com/room/') - -gatherProcessStats() - -def parseDurationLiteral(lit: Literal) -> float: - if lit.endswith('s'): - return float(lit.split('s')[0]) - raise NotImplementedError(f'duration literal: {lit}') - - -class MqttStatementSource: - def __init__(self, uri, config, masterGraph, mqtt, influx): - self.uri = uri - self.config = config - self.masterGraph = masterGraph - self.mqtt = mqtt - self.influx = influx - - self.mqttTopic = self.topicFromConfig(self.config) - - statPath = '/subscribed_topic/' + self.mqttTopic.decode('ascii').replace('/', '|') - scales.init(self, statPath) - self._mqttStats = scales.collection( - statPath + '/incoming', scales.IntStat('count'), - scales.RecentFpsStat('fps')) - - - rawBytes = self.subscribeMqtt() - rawBytes = rx.operators.do_action(self.countIncomingMessage)(rawBytes) - parsed = self.getParser()(rawBytes) - - g = self.config - for conv in g.items(g.value(self.uri, ROOM['conversions'])): - parsed = self.conversionStep(conv)(parsed) - - outputQuadsSets = rx.combine_latest( - *[self.makeQuads(parsed, plan) - for plan in g.objects(self.uri, ROOM['graphStatements'])]) - - outputQuadsSets.subscribe_(self.updateQuads) - - def topicFromConfig(self, config) -> bytes: - topicParts = list(config.items(config.value(self.uri, ROOM['mqttTopic']))) - return b'/'.join(t.encode('ascii') for t in topicParts) - - - def subscribeMqtt(self): - return self.mqtt.subscribe(self.mqttTopic) - - def countIncomingMessage(self, _): - self._mqttStats.fps.mark() - self._mqttStats.count += 1 - - def getParser(self): - g = self.config - parser = g.value(self.uri, ROOM['parser']) - if parser == XSD.double: - return rx.operators.map(lambda v: Literal(float(v.decode('ascii')))) - elif parser == ROOM['tagIdToUri']: - return rx.operators.map(self.tagIdToUri) - elif parser == ROOM['onOffBrightness']: - return rx.operators.map(lambda v: Literal(0.0 if v == b'OFF' else 1.0)) - elif parser == ROOM['jsonBrightness']: - return rx.operators.map(self.parseJsonBrightness) - elif ROOM['ValueMap'] in g.objects(parser, RDF.type): - return rx.operators.map(lambda v: self.remap(parser, v.decode('ascii'))) - else: - raise NotImplementedError(parser) - - def parseJsonBrightness(self, mqttValue: bytes): - msg = json.loads(mqttValue.decode('ascii')) - return Literal(float(msg['brightness'] / 255) if msg['state'] == 'ON' else 0.0) - - def conversionStep(self, conv: Node): - g = self.config - if conv == ROOM['celsiusToFarenheit']: - return rx.operators.map(lambda value: Literal(round(value.toPython() * 1.8 + 32, 2))) - elif g.value(conv, ROOM['ignoreValueBelow'], default=None) is not None: - threshold = g.value(conv, ROOM['ignoreValueBelow']) - return rx.operators.filter(lambda value: value.toPython() >= threshold.toPython()) - else: - raise NotImplementedError(conv) - - def makeQuads(self, parsed, plan): - g = self.config - def quadsFromValue(valueNode): - return set([ - (self.uri, - g.value(plan, ROOM['outputPredicate']), - valueNode, - self.uri) - ]) - - def emptyQuads(element): - return set([]) - - quads = rx.operators.map(quadsFromValue)(parsed) - - dur = g.value(plan, ROOM['statementLifetime']) - if dur is not None: - sec = parseDurationLiteral(dur) - quads = quads.pipe( - rx.operators.debounce(sec, rx.scheduler.eventloop.TwistedScheduler(reactor)), - rx.operators.map(emptyQuads), - rx.operators.merge(quads), - ) - - return quads - - def updateQuads(self, newGraphs): - newQuads = set.union(*newGraphs) - g = graphFromQuads(newQuads) - log.debug(f'{self.uri} update to {len(newQuads)} statements') - - self.influx.exportToInflux(newQuads) - - self.masterGraph.patchSubgraph(self.uri, g) - - def tagIdToUri(self, value: bytearray) -> URIRef: - justHex = value.decode('ascii').replace('-', '').lower() - int(justHex, 16) # validate - return URIRef(f'http://bigasterisk.com/rfidCard/{justHex}') - - def remap(self, parser, valueStr: str): - g = self.config - value = Literal(valueStr) - for entry in g.objects(parser, ROOM['map']): - if value == g.value(entry, ROOM['from']): - return g.value(entry, ROOM['to']) - raise KeyError(value) - - -if __name__ == '__main__': - arg = docopt(""" - Usage: rdf_from_mqtt.py [options] - - -v Verbose - --cs=STR Only process config filenames with this substring - """) - verboseLogging(arg['-v']) - - config = Graph() - for fn in [ - "config_cardreader.n3", - "config_nightlight_ari.n3", - "config_bed_bar.n3", - "config_air_quality_indoor.n3", - "config_air_quality_outdoor.n3", - "config_living_lamps.n3", - "config_kitchen.n3", - ]: - if not arg['--cs'] or arg['--cs'] in fn: - config.parse(fn, format='n3') - - masterGraph = PatchableGraph() - - mqtt = MqttClient(clientId='rdf_from_mqtt', brokerHost='bang', - brokerPort=1883) - influx = InfluxExporter(config) - - srcs = [] - for src in config.subjects(RDF.type, ROOM['MqttStatementSource']): - srcs.append(MqttStatementSource(src, config, masterGraph, mqtt, influx)) - log.info(f'set up {len(srcs)} sources') - - port = 10018 - reactor.listenTCP(port, cyclone.web.Application([ - (r"/()", cyclone.web.StaticFileHandler, - {"path": ".", "default_filename": "index.html"}), - (r'/stats/(.*)', StatsHandler, {'serverName': 'rdf_from_mqtt'}), - (r"/graph/mqtt", CycloneGraphHandler, {'masterGraph': masterGraph}), - (r"/graph/mqtt/events", CycloneGraphEventsHandler, - {'masterGraph': masterGraph}), - ], mqtt=mqtt, masterGraph=masterGraph, debug=arg['-v']), - interface='::') - log.warn('serving on %s', port) - - reactor.run()
--- a/service/rdf_from_mqtt/requirements.txt Wed Feb 05 17:20:28 2020 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,15 +0,0 @@ -pytype - -cyclone -rdflib-jsonld==0.4.0 -rdflib==4.2.2 -twisted-mqtt==0.3.6 -git+http://github.com/drewp/scales.git@550bcba42c5e96152ff5c5bd753fbb33ffdfe460#egg=scales -git+https://github.com/ReactiveX/RxPY.git@6deb66e827f34a88b4605773d7671322b9cbbd08#egg=rx - -cycloneerr -export_to_influxdb==0.4.0 -mqtt_client==0.9.0 -patchablegraph==0.11.0 -rdfdb==0.21.0 -standardservice==0.6.0
--- a/service/rdf_from_mqtt/serv.n3 Wed Feb 05 17:20:28 2020 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,24 +0,0 @@ -@prefix : <http://bigasterisk.com/ns/serv#> . -@prefix auth: <http://bigasterisk.com/ns/serv/auth#> . -@prefix serv: <http://bigasterisk.com/services/> . - -serv:rdf_from_mqtt a :Service; - :path "/rdf_from_mqtt/"; - :openid auth:admin; - :serverHost "bang"; - :internalPort 10018; - :prodDockerFlags ( - "-p" "10018:10018" - "--net=host"); - :localDockerFlags ( - "-v" "`pwd`:/opt" - "-v" "/my/proj/homeauto/lib:/lib_src" - ); - :localRunCmdline ( - - "python3" "rdf_from_mqtt.py" "-v" -#"--cs" "living" -); - :dockerFile "Dockerfile" -. -
--- a/service/rdf_from_mqtt/tasks.py Wed Feb 05 17:20:28 2020 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,14 +0,0 @@ -from invoke import task, Collection - -import sys -sys.path.append('/my/proj/release') -from serv_tasks import serv_tasks - -ns = Collection() -serv_tasks(ns, 'serv.n3', 'rdf_from_mqtt') - -@ns.add_task -@task -def tail_mqtt(ctx): - internal_mqtt_port = 10010 - ctx.run(f'mosquitto_sub -h bang -p 1883 -d -v -t \#')
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/service/rdf_to_mqtt/Dockerfile Thu Feb 06 16:36:35 2020 -0800 @@ -0,0 +1,13 @@ +FROM bang6:5000/base_x86 + +WORKDIR /opt + +COPY requirements.txt ./ +RUN pip3 install --index-url https://projects.bigasterisk.com/ --extra-index-url https://pypi.org/simple -r requirements.txt +RUN pip3 install -U 'https://github.com/drewp/cyclone/archive/python3.zip?v3' + +COPY *.py *.html *.css *.js ./ + +EXPOSE 10011:10011 + +CMD [ "python3", "./mqtt_graph_bridge.py" ]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/service/rdf_to_mqtt/config.n3 Thu Feb 06 16:36:35 2020 -0800 @@ -0,0 +1,54 @@ +@prefix : <http://projects.bigasterisk.com/room/> . +@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . +@prefix fr: <http://bigasterisk.com/foaf/> . + +:kitchenSkylight a :Device; + :mqttTopicHead ("h801_skylight" "light"); + :convertedAttr [ + :deviceAttr :white; + :mqttTopicTail ("kit_w1" "command"); + :valueConversion :to8Bit; + :message '{"state":"ON","brightness":%value%}' + ], + :convertedAttr [ + :deviceAttr :color; + :mqttTopicTail ("kit_r" "command"); + :valueConversion :extractRed8Bit; + :message '{"state":"ON","brightness":%value%}' + ], + :convertedAttr [ + :deviceAttr :color; + :mqttTopicTail ("kit_g" "command"); + :valueConversion :extractGreen8Bit; + :message '{"state":"ON","brightness":%value%}' + ], + :convertedAttr [ + :deviceAttr :color; + :mqttTopicTail ("kit_r" "command"); + :valueConversion :extractBlue8Bit; + :message '{"state":"ON","brightness":%value%}' + ], + +:nightlightAriTemperature a :ExportedMeasurement; + :mqttTopicHead ("nightlight_ari" "sensor" "temperature" "state"); + + :missingAfterSec 150; + :ignoreInputValueBelow -999; + + :valueProcess [ + :conversion :celsiusToFarenheit; #and round(x,2) + ]; + + :graphStatements [ + :outputPredicate :temperatureF; + :statementLifetime :untilReplaced; + + # is this just another valueProcess? + :outputRecentPredicate :recentLowTemperatureF; :recentPeriodSec 30; + ], + + :influxMeasurement [ + :measurement "temperatureF"; + :predicate :temperatureF; + :tag [:key "host"; :value "nightlight_ari"], + [:key "location"; :value "ariRoom"]] .
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/service/rdf_to_mqtt/h801/config_counter.yaml Thu Feb 06 16:36:35 2020 -0800 @@ -0,0 +1,73 @@ +substitutions: + pwm_freq: 310 Hz + +#310 low buzz, nicer than 520 +#520 low pitch buzz, ok +#600 sound is present but might be ok +#800 bad sound +#1500 sound is bad but we had it like that +#3000 very bad snd +#6000 bad snd and flicker +#9000 flicker + +esphomeyaml: + name: h801_counter + platform: ESP8266 + board: esp8285 + build_path: build + +wifi: + ssid: !secret wifi_ssid + password: !secret wifi_password + manual_ip: + static_ip: 10.2.0.59 + gateway: 10.2.0.1 + subnet: 255.255.255.0 + +mqtt: + broker: '10.2.0.1' + port: 1883 + +logger: + baud_rate: 115200 + +ota: + +output: + - id: pwm_b + platform: esp8266_pwm + pin: 12 + frequency: ${pwm_freq} + - id: pwm_g + platform: esp8266_pwm + pin: 13 + frequency: ${pwm_freq} + - id: pwm_r + platform: esp8266_pwm + pin: 15 + frequency: ${pwm_freq} + - id: pwm_w1 + platform: esp8266_pwm + pin: 14 + frequency: ${pwm_freq} + - id: pwm_w2 + platform: esp8266_pwm + pin: 4 + frequency: ${pwm_freq} + +light: + - platform: monochromatic + name: "Kit_r" + output: pwm_r + - platform: monochromatic + name: "Kit_g" + output: pwm_g + - platform: monochromatic + name: "Kit_b" + output: pwm_b + - platform: monochromatic + name: "Kit_w1" + output: pwm_w1 + - platform: monochromatic + name: "Kit_w2" + output: pwm_w2
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/service/rdf_to_mqtt/h801/config_skylight.yaml Thu Feb 06 16:36:35 2020 -0800 @@ -0,0 +1,64 @@ +substitutions: + pwm_freq: 310 Hz + +esphomeyaml: + name: h801_skylight + platform: ESP8266 + board: esp8285 + build_path: build + +wifi: + ssid: !secret wifi_ssid + password: !secret wifi_password + manual_ip: + static_ip: 10.2.0.67 + gateway: 10.2.0.1 + subnet: 255.255.255.0 + +mqtt: + broker: '10.2.0.1' + port: 1883 + +logger: + baud_rate: 115200 + +ota: + +output: + - id: pwm_b + platform: esp8266_pwm + pin: 12 + frequency: ${pwm_freq} + - id: pwm_r + platform: esp8266_pwm + pin: 13 + frequency: ${pwm_freq} + - id: pwm_g + platform: esp8266_pwm + pin: 15 + frequency: ${pwm_freq} + - id: pwm_w1 + platform: esp8266_pwm + pin: 14 + frequency: ${pwm_freq} + - id: pwm_w2 + platform: esp8266_pwm + pin: 4 + frequency: ${pwm_freq} + +light: + - platform: monochromatic + name: "Kit_r" + output: pwm_r + - platform: monochromatic + name: "Kit_g" + output: pwm_g + - platform: monochromatic + name: "Kit_b" + output: pwm_b + - platform: monochromatic + name: "Kit_w1" + output: pwm_w1 + - platform: monochromatic + name: "Kit_w2" + output: pwm_w2
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/service/rdf_to_mqtt/h801/makefile Thu Feb 06 16:36:35 2020 -0800 @@ -0,0 +1,2 @@ +update: + /my/dl/dl/esphomeyaml/bin/esphomeyaml config_skylight.yaml run --no-logs
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/service/rdf_to_mqtt/index.html Thu Feb 06 16:36:35 2020 -0800 @@ -0,0 +1,35 @@ +<!doctype html> +<html> + <head> + <title>mqtt_graph_bridge</title> + <meta charset="utf-8"> + <script src="/lib/polymer/1.0.9/webcomponentsjs/webcomponents.min.js"></script> + <script src="/lib/require/require-2.3.3.js"></script> + <script src="/rdf/common_paths_and_ns.js"></script> + + <link rel="stylesheet" href="/rdf/browse/style.css"> + + </head> + <body class="rdfBrowsePage"> + mqtt_graph_bridge + + + <p>Send demo statements to bridge:</p> + <div><button data-post="output?s=:kitchenLight&p=:brightness" data-body="0.0">Send (:kitchenLight :brightness 0.0)</button></div> + <div><button data-post="output?s=:kitchenLight&p=:brightness" data-body="1.0">Send (:kitchenLight :brightness 1.0)</button></div> + <div><button data-post="output?s=:livingLampShelf&p=:brightness" data-body="0.0">Send (:livingLampShelf :brightness 0.0)</button></div> + <div><button data-post="output?s=:livingLampShelf&p=:brightness" data-body="1.0">Send (:livingLampShelf :brightness 1.0)</button></div> + + <script> + Array.from(document.querySelectorAll("button")).forEach((el) => { + el.addEventListener("click", (ev) => { + fetch(el.dataset.post, { + method: "PUT", + body: el.dataset.body + }); + }); + }); + </script> + + </body> +</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/service/rdf_to_mqtt/mqtt_graph_bridge.py Thu Feb 06 16:36:35 2020 -0800 @@ -0,0 +1,130 @@ +""" +We get output statements that are like light9's deviceAttrs (:dev1 :color "#ff0000"), +convert those to outputAttrs (:dev1 :red 255; :green 0; :blue 0) and post them to mqtt. + +This is like light9/bin/collector. +""" +import json + +from docopt import docopt +from rdflib import Namespace, Literal +from twisted.internet import reactor +import cyclone.web + +from mqtt_client import MqttClient +from patchablegraph import PatchableGraph, CycloneGraphHandler, CycloneGraphEventsHandler +from standardservice.logsetup import log, verboseLogging +import rdf_over_http + +ROOM = Namespace('http://projects.bigasterisk.com/room/') + +devs = { + ROOM['kitchenLight']: { + 'root': 'h801_skylight', + 'ctx': ROOM['kitchenH801'] + }, + ROOM['kitchenCounterLight']: { + 'root': 'h801_counter', + 'ctx': ROOM['kitchenH801'] + }, + ROOM['livingLampShelf']: { + 'root': 'sonoff_0/switch/sonoff_basic_relay/command', + 'ctx': ROOM['sonoff_0'], + 'values': 'binary', + }, +} + + +class OutputPage(cyclone.web.RequestHandler): + def put(self): + for stmt in rdf_over_http.rdfStatementsFromRequest( + self.request.arguments, + self.request.body, + self.request.headers): + self._onStatement(stmt) + + def _onStatement(self, stmt): + log.info(f'incoming statement: {stmt}') + ignored = True + for dev, attrs in devs.items(): + if stmt[0] == ROOM['frontWindow']: + ignored = ignored and self._publishFrontScreenText(stmt) + + if stmt[0:2] == (dev, ROOM['brightness']): + log.info(f'brightness request: {stmt}') + brightness = stmt[2].toPython() + + if attrs.get('values', '') == 'binary': + self._publishOnOff(attrs, brightness) + else: + self._publishRgbw(attrs, brightness) + # try to stop saving this; let the device be the master usually + self.settings.masterGraph.patchObject( + attrs['ctx'], + stmt[0], stmt[1], stmt[2]) + ignored = False + if ignored: + log.warn("ignoring %s", stmt) + + def _publishOnOff(self, attrs, brightness): + msg = 'OFF' + if brightness > 0: + msg = 'ON' + self._publish(topic=attrs['root'], message=msg) + + def _publishRgbw(self, attrs, brightness): + for chan, scale in [('w1', 1), + ('r', 1), + ('g', .8), + ('b', .8)]: + self._publish( + topic=f"{attrs['root']}/light/kit_{chan}/command", + messageJson={ + 'state': 'ON', + 'brightness': int(brightness * 255) + }) + + def _publishFrontScreenText(self, stmt): + ignored = True + for line in ['line1', 'line2', 'line3', 'line4']: + if stmt[1] == ROOM[line]: + ignored = False + self.settings.mqtt.publish( + b'frontwindow/%s' % line.encode('ascii'), + stmt[2].toPython()) + return ignored + + def _publish(self, topic: str, messageJson: object=None, + message: str=None): + if messageJson is not None: + message = json.dumps(messageJson) + self.settings.mqtt.publish( + topic.encode('ascii'), + message.encode('ascii')) + + +if __name__ == '__main__': + arg = docopt(""" + Usage: mqtt_graph_bridge.py [options] + + -v Verbose + """) + verboseLogging(arg['-v']) + + masterGraph = PatchableGraph() + + mqtt = MqttClient(clientId='mqtt_graph_bridge', brokerPort=1883) + + port = 10008 + reactor.listenTCP(port, cyclone.web.Application([ + (r"/()", cyclone.web.StaticFileHandler, + {"path": ".", "default_filename": "index.html"}), + (r'/output', OutputPage), + ], mqtt=mqtt, debug=arg['-v']), interface='::') + log.warn('serving on %s', port) + + for dev, attrs in devs.items(): + masterGraph.patchObject(attrs['ctx'], + dev, ROOM['brightness'], Literal(0.0)) + + reactor.run()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/service/rdf_to_mqtt/rdf_over_http.py Thu Feb 06 16:36:35 2020 -0800 @@ -0,0 +1,34 @@ +from rdflib import Graph, URIRef, Literal, Namespace +from rdflib.parser import StringInputSource + +ROOM = Namespace('http://projects.bigasterisk.com/room/') + + +def rdfGraphBody(body, headers): + g = Graph() + g.parse(StringInputSource(body), format='nt') + return g + + +def expandQueryParamUri(txt) -> URIRef: + if txt.startswith(':'): + return ROOM[txt.lstrip(':')] + # etc + return URIRef(txt) + + +def rdfStatementsFromRequest(arg, body, headers): + if arg.get('s') and arg.get('p'): + subj = expandQueryParamUri(arg['s'][-1]) + pred = expandQueryParamUri(arg['p'][-1]) + turtleLiteral = body + try: + obj = Literal(float(turtleLiteral)) + except ValueError: + obj = Literal(turtleLiteral) + yield (subj, pred, obj) + else: + g = rdfGraphBody(body, headers) + assert len(g) == 1, len(g) + yield g.triples((None, None, None)).next() + # could support multiple stmts
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/service/rdf_to_mqtt/readme Thu Feb 06 16:36:35 2020 -0800 @@ -0,0 +1,7 @@ +tail all changes: +mosquitto_sub -v -t 004BD965/\# + +bang(pts/8):~% mosquitto_pub -t 004BD965/w1/light/switch -m ON +bang(pts/8):~% mosquitto_pub -t 004BD965/w1/light/switch -m OFF + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/service/rdf_to_mqtt/requirements.txt Thu Feb 06 16:36:35 2020 -0800 @@ -0,0 +1,12 @@ +cyclone +rdflib-jsonld==0.4.0 +rdflib==4.2.2 +twisted-mqtt==0.3.6 +rx==1.6.1 +git+http://github.com/drewp/scales.git@448d59fb491b7631877528e7695a93553bfaaa93#egg=scales + +cycloneerr +patchablegraph==0.11.0 +rdfdb==0.21.0 +standardservice==0.6.0 +mqtt_client==0.7.0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/service/rdf_to_mqtt/serv.n3 Thu Feb 06 16:36:35 2020 -0800 @@ -0,0 +1,23 @@ +@prefix : <http://bigasterisk.com/ns/serv#> . +@prefix auth: <http://bigasterisk.com/ns/serv/auth#> . +@prefix serv: <http://bigasterisk.com/services/> . + + +serv:rdf_to_mqtt a :Service; + :path "/rdf_to_mqtt/"; + :openid auth:admin; + :serverHost "bang"; + :internalPort 10008; + :prodDockerFlags ( + "-p" "10008:10008" + "--net=host" + ); + :localDockerFlags ( + "-v" "`pwd`:/opt" + ); + :localRunCmdline ( + "python3" "rdf_to_mqtt.py" "-v" + ); + :dockerFile "Dockerfile" +. +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/service/rdf_to_mqtt/tasks.py Thu Feb 06 16:36:35 2020 -0800 @@ -0,0 +1,40 @@ +from invoke import task + +JOB = 'rdf_to_mqtt' +PORT = 10008 +TAG = f'bang6:5000/{JOB}_x86:latest' + +@task +def build_image(ctx): + ctx.run(f'docker build --network=host -t {TAG} .') + +@task(pre=[build_image]) +def push_image(ctx): + ctx.run(f'docker push {TAG}') + +@task(pre=[build_image]) +def shell(ctx): + ctx.run(f'docker run --name={JOB}_shell --rm -it --cap-add SYS_PTRACE --net=host {TAG} /bin/bash', pty=True) + +@task(pre=[build_image]) +def local_run(ctx): + ctx.run(f'docker run --name={JOB}_local --rm -it --net=host -v /my/proj/homeauto/lib:/lib_src {TAG} python3 rdf_to_mqtt.py -v', pty=True) + +@task(pre=[push_image]) +def redeploy(ctx): + ctx.run(f'supervisorctl -s http://bang:9001/ restart {JOB}_{PORT}') + +@task +def program_board_over_usb(ctx): + tag = 'esphome/esphome' + ctx.run(f"docker run --rm -v `pwd`:/config --device=/dev/ttyUSB0 -it {tag} door.yaml run", pty=True) +# config_skylight.yaml run --no-logs + +@task +def monitor_usb(ctx): + tag = 'esphome/esphome' + ctx.run(f"docker run --rm -v `pwd`:/config --device=/dev/ttyUSB0 -it {tag} door.yaml logs", pty=True) + +@task +def tail_mqtt(ctx): + ctx.run(f'mosquitto_sub -h bang -p 10010 -d -v -t \#')
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/service/reasoning/serv.n3 Thu Feb 06 16:36:35 2020 -0800 @@ -0,0 +1,23 @@ +@prefix : <http://bigasterisk.com/ns/serv#> . +@prefix auth: <http://bigasterisk.com/ns/serv/auth#> . +@prefix serv: <http://bigasterisk.com/services/> . + + +serv:reasoning a :Service; + :path "/reasoning/"; + :openid auth:admin; + :serverHost "bang"; + :internalPort 9071; + :prodDockerFlags ( + "-p" "9071:9071" + "--net=host" + ); + :localDockerFlags ( + "-v" "`pwd`:/opt" + ); + :localRunCmdline ( + "python3" "reasoning.py" "-v" + ); + :dockerFile "Dockerfile" +. +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/service/store/serv.n3 Thu Feb 06 16:36:35 2020 -0800 @@ -0,0 +1,22 @@ +@prefix : <http://bigasterisk.com/ns/serv#> . +@prefix auth: <http://bigasterisk.com/ns/serv/auth#> . +@prefix serv: <http://bigasterisk.com/services/> . + +serv:store a :Service; + :path "/store/"; + :openid auth:admin; + :serverHost "bang"; + :internalPort 10015; + :prodDockerFlags ( + "-p" "10015:10015" + "--net=host" + ); + :localDockerFlags ( + "-v" "`pwd`:/opt" + ); + :localRunCmdline ( + "python3" "store.py" "-v" + ); + :dockerFile "Dockerfile" +. +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/service/wifi/serv.n3 Thu Feb 06 16:36:35 2020 -0800 @@ -0,0 +1,22 @@ +@prefix : <http://bigasterisk.com/ns/serv#> . +@prefix auth: <http://bigasterisk.com/ns/serv/auth#> . +@prefix serv: <http://bigasterisk.com/services/> . + + +serv:wifi a :Service; + :path "/wifi/"; + :openid auth:admin; + :serverHost "bang"; + :internalPort 9070; + :prodDockerFlags ( + "-p" "9070:9070" + "--net=host" + ); + :localDockerFlags ( + "-v" "`pwd`:/opt" + ); + :localRunCmdline ( + "python3" "wifi.py" "-v" + ); + :dockerFile "Dockerfile" +.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/service/xidle/serv.n3 Thu Feb 06 16:36:35 2020 -0800 @@ -0,0 +1,23 @@ +@prefix : <http://bigasterisk.com/ns/serv#> . +@prefix auth: <http://bigasterisk.com/ns/serv/auth#> . +@prefix serv: <http://bigasterisk.com/services/> . + + +serv:xidle a :Service; + :path "/xidle/"; + :openid auth:admin; + :serverHost "bang"; + :internalPort 9107; + :prodDockerFlags ( + "-p" "9107:9107" + "--net=host" + ); + :localDockerFlags ( + "-v" "`pwd`:/opt" + ); + :localRunCmdline ( + "python3" "xidle.py" "-v" + ); + :dockerFile "Dockerfile" +. +