diff options
| author | Luke Hoersten <[email protected]> | 2018-01-19 11:48:25 -0600 |
|---|---|---|
| committer | Luke Hoersten <[email protected]> | 2018-01-19 11:48:25 -0600 |
| commit | 5f6b0c0e6e2ea3621cb0033b9f5498372c89db74 (patch) | |
| tree | cfade4f68d242333f20d8d5b44906f6bb2b94315 | |
Initial public commit.
| -rw-r--r-- | .hgignore | 5 | ||||
| -rw-r--r-- | README.md | 12 | ||||
| -rw-r--r-- | ansible.cfg | 2 | ||||
| -rw-r--r-- | hap.yaml | 8 | ||||
| -rw-r--r-- | inventory/raspberrypi | 2 | ||||
| -rw-r--r-- | roles/automationhat/tasks/main.yaml | 13 | ||||
| -rw-r--r-- | roles/base/defaults/main.yaml | 6 | ||||
| -rw-r--r-- | roles/base/tasks/main.yml | 21 | ||||
| -rw-r--r-- | roles/base/templates/wpa_supplicant.conf.j2 | 12 | ||||
| -rw-r--r-- | roles/hap-nodejs/defaults/main.yaml | 13 | ||||
| -rw-r--r-- | roles/hap-nodejs/files/Door_accessory.js | 115 | ||||
| -rwxr-xr-x | roles/hap-nodejs/files/doord.py | 51 | ||||
| -rw-r--r-- | roles/hap-nodejs/handlers/main.yaml | 5 | ||||
| -rw-r--r-- | roles/hap-nodejs/meta/main.yaml | 4 | ||||
| -rw-r--r-- | roles/hap-nodejs/tasks/main.yaml | 45 | ||||
| -rw-r--r-- | roles/hap-nodejs/templates/hap-nodejs.service.j2 | 16 | ||||
| -rw-r--r-- | roles/nodejs/tasks/main.yaml | 38 |
17 files changed, 368 insertions, 0 deletions
diff --git a/.hgignore b/.hgignore new file mode 100644 index 0000000..c41d01f --- /dev/null +++ b/.hgignore @@ -0,0 +1,5 @@ +syntax: regexp +~$ +\#.*\#$ +\.\# +\.DS_Store$ diff --git a/README.md b/README.md new file mode 100644 index 0000000..94bf70c --- /dev/null +++ b/README.md @@ -0,0 +1,12 @@ +# Raspberry Pi Homekit Door Accessory + +## Ansible Inventory + +`inventory/host_vars/raspberrypi.local.yaml` + + github_user: "<github user>" + nodejs_version: "8.9.0" + nodejs_dir: "node-v{{nodejs_version}}-linux-{{ansible_architecture}}" + wpa_networks: + - ssid: "<your ssid>" + psk: "<your wifi password>" diff --git a/ansible.cfg b/ansible.cfg new file mode 100644 index 0000000..7838245 --- /dev/null +++ b/ansible.cfg @@ -0,0 +1,2 @@ +[defaults] +inventory = inventory/ diff --git a/hap.yaml b/hap.yaml new file mode 100644 index 0000000..cfb7717 --- /dev/null +++ b/hap.yaml @@ -0,0 +1,8 @@ +--- + +- hosts: raspberrypi + roles: + - base + # - automationhat + - hap-nodejs + - homebridge diff --git a/inventory/raspberrypi b/inventory/raspberrypi new file mode 100644 index 0000000..70fa707 --- /dev/null +++ b/inventory/raspberrypi @@ -0,0 +1,2 @@ +[raspberrypi] +raspberrypi.local ansible_user=pi diff --git a/roles/automationhat/tasks/main.yaml b/roles/automationhat/tasks/main.yaml new file mode 100644 index 0000000..405b450 --- /dev/null +++ b/roles/automationhat/tasks/main.yaml @@ -0,0 +1,13 @@ +--- + +- name: download automationhat install script + get_url: + url: "https://get.pimoroni.com/automationhat" + dest: "/tmp/automationhat.sh" + mode: 0750 + +- name: install automationhat + shell: "/tmp/automationhat.sh" + args: + chdir: "/tmp" + executable: "/bin/bash" diff --git a/roles/base/defaults/main.yaml b/roles/base/defaults/main.yaml new file mode 100644 index 0000000..0a496ce --- /dev/null +++ b/roles/base/defaults/main.yaml @@ -0,0 +1,6 @@ +--- + +ubuntu_base_apt_packages: + - emacs-nox + - python3 + - htop diff --git a/roles/base/tasks/main.yml b/roles/base/tasks/main.yml new file mode 100644 index 0000000..4c5e76f --- /dev/null +++ b/roles/base/tasks/main.yml @@ -0,0 +1,21 @@ +--- + +- name: setup wifi + become: yes + template: + src: "wpa_supplicant.conf.j2" + dest: "/etc/wpa_supplicant/wpa_supplicant.conf" + mode: 0644 + +- name: update apt package cache + become: yes + apt: upgrade="dist" update_cache="yes" cache_valid_time="3600" + +- name: install extra apt packages + become: yes + apt: name="{{item}}" + with_items: "{{ubuntu_base_apt_packages}}" + +- name: authorize admin ssh keys + become: yes + authorized_key: "user=pi key=https://github.com/{{github_user}}.keys" diff --git a/roles/base/templates/wpa_supplicant.conf.j2 b/roles/base/templates/wpa_supplicant.conf.j2 new file mode 100644 index 0000000..c0d6354 --- /dev/null +++ b/roles/base/templates/wpa_supplicant.conf.j2 @@ -0,0 +1,12 @@ +# {{ansible_managed}} + +country=US +ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev +update_config=1 +{% for network in wpa_networks %} + +network={ + ssid="{{network.ssid}}" + psk="{{network.psk}}" +} +{% endfor %} diff --git a/roles/hap-nodejs/defaults/main.yaml b/roles/hap-nodejs/defaults/main.yaml new file mode 100644 index 0000000..22c1fdb --- /dev/null +++ b/roles/hap-nodejs/defaults/main.yaml @@ -0,0 +1,13 @@ +--- + +hap_ubuntu_apt_packages: + - "libavahi-compat-libdnssd-dev" + +hap_npm_packages: + - "python-shell" + +hap_user: "pi" +hap_restart_sec: 10 +hap_src: "https://github.com/KhaosT/HAP-NodeJS/archive/master.zip" +hap_home: "/home/{{hap_user}}/" +hap_dest: "{{hap_home}}/HAP-NodeJS-master" diff --git a/roles/hap-nodejs/files/Door_accessory.js b/roles/hap-nodejs/files/Door_accessory.js new file mode 100644 index 0000000..e1d16ef --- /dev/null +++ b/roles/hap-nodejs/files/Door_accessory.js @@ -0,0 +1,115 @@ +var Accessory = require('../').Accessory; +var Service = require('../').Service; +var Characteristic = require('../').Characteristic; +var uuid = require('../').uuid; +var PythonShell = require('python-shell'); + +var door = exports.accessory = new Accessory('Door', uuid.generate('hap-nodejs:accessories:door')); +door.username = 'C1:5D:3A:EA:54:AB'; +door.pincode = '031-45-154'; + +door.getService(Service.AccessoryInformation) + .setCharacteristic(Characteristic.Manufacturer, 'Raspberry Pi') + .setCharacteristic(Characteristic.Model, 'Zero W') + .setCharacteristic(Characteristic.SerialNumber, 'A1S2NASF88EW'); + +var DOOR = { + lockTimeout: 10000, // milliseconds - 10 sec + + pyshell: new PythonShell('doord.py', { + mode: 'text', + pythonPath: '/usr/bin/python3', + pythonOptions: ['-u'], + scriptPath: 'python/' + }), + + lock: function() { + console.log('locking door'); + this.pyshell.send('lock'); + }, + + unlock: function() { + console.log('unlocking door'); + this.pyshell.send('unlock'); + }, + + identify: function() { + console.log('identify door'); + }, + + listenDoorbell: function(doorbellOnCallback, doorbellOffCallback) { + this.pyshell.on('message', function (message) { + console.log(message); + switch(message) { + case 'doorbell_on': + doorbellOnCallback(); + break; + case 'doorbell_off': + doorbellOffCallback(); + break; + } + }); + } +}; + +door.on('identify', function(paired, callback) { + DOOR.identify(); + callback(); +}); + +door.addService(Service.Doorbell, 'Doorbell'); +door.addService(Service.CameraRTPStreamManagement, 'Psudo-Camera'); +door.addService(Service.Speaker, 'Psudo-Speaker'); +door.addService(Service.Microphone, 'Psudo-Microphone'); + +door.addService(Service.LockMechanism, 'Door') + .setCharacteristic(Characteristic.LockTargetState, Characteristic.LockTargetState.SECURED) // force initial state + .setCharacteristic(Characteristic.LockCurrentState, Characteristic.LockCurrentState.SECURED) + .getCharacteristic(Characteristic.LockTargetState) + .on('set', function(value, callback) { + setDoorTargetState(value); + callback(); + }); + +function setDoorTargetState(value) { + switch(value) { + case Characteristic.LockTargetState.UNSECURED: + unlockDoor(); + break; + case Characteristic.LockTargetState.SECURED: + lockDoor(); + break; + } +} + +function unlockDoor() { + DOOR.unlock(); + door.getService(Service.LockMechanism) + .setCharacteristic(Characteristic.LockCurrentState, + Characteristic.LockCurrentState.UNSECURED); + scheduleUnlockTimeout(); +} + +function lockDoor() { + DOOR.lock(); + door.getService(Service.LockMechanism) + .setCharacteristic(Characteristic.LockCurrentState, + Characteristic.LockCurrentState.SECURED); +} + +function scheduleUnlockTimeout() { + setTimeout(function() { + console.log('unlock timeout door'); + door.getService(Service.LockMechanism) + .setCharacteristic(Characteristic.LockTargetState, + Characteristic.LockTargetState.SECURED); + }, DOOR.lockTimeout); +} + +DOOR.listenDoorbell( + function() { + door.getService(Service.Doorbell) + .setCharacteristic(Characteristic.ProgrammableSwitchEvent, + Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS); + }, + function() {}); diff --git a/roles/hap-nodejs/files/doord.py b/roles/hap-nodejs/files/doord.py new file mode 100755 index 0000000..4cde487 --- /dev/null +++ b/roles/hap-nodejs/files/doord.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 + +import time +import automationhat +import sys + +import queue +import threading + + +def main(): + command_queue = queue.LifoQueue() + read_thread = threading.Thread(target=read_loop, args=[command_queue]) + read_thread.start() + run_loop(command_queue) + + +def read_loop(command_queue): + while True: + command_queue.put_nowait(sys.stdin.readline().rstrip('\n')) + + +def run_loop(command_queue): + thread_local = threading.local() + thread_local.doorbell_on_state = False + + while True: + run_command(command_queue) + read_doorbell(thread_local) + + +def run_command(command_queue): + try: + command = command_queue.get(timeout=0.5) + except queue.Empty: + pass + else: + automationhat.relay.on() if command == "unlock" else automationhat.relay.off() + + +def read_doorbell(thread_local): + analog_value = automationhat.analog.one.read() + doorbell_on_state = 6.0 < analog_value and analog_value < 6.3 + + if doorbell_on_state != thread_local.doorbell_on_state: + thread_local.doorbell_on_state = doorbell_on_state + print("doorbell_on") if doorbell_on_state else print("doorbell_off") + + +if __name__ == "__main__": + main() diff --git a/roles/hap-nodejs/handlers/main.yaml b/roles/hap-nodejs/handlers/main.yaml new file mode 100644 index 0000000..25d24bd --- /dev/null +++ b/roles/hap-nodejs/handlers/main.yaml @@ -0,0 +1,5 @@ +--- + +- name: restart hap-nodejs service + systemd: name="hap-nodejs" state="restarted" daemon_reload="yes" + become: yes diff --git a/roles/hap-nodejs/meta/main.yaml b/roles/hap-nodejs/meta/main.yaml new file mode 100644 index 0000000..3c8004a --- /dev/null +++ b/roles/hap-nodejs/meta/main.yaml @@ -0,0 +1,4 @@ +--- + +dependencies: + - role: nodejs diff --git a/roles/hap-nodejs/tasks/main.yaml b/roles/hap-nodejs/tasks/main.yaml new file mode 100644 index 0000000..3fac212 --- /dev/null +++ b/roles/hap-nodejs/tasks/main.yaml @@ -0,0 +1,45 @@ +--- + +- name: install homekit apt packages + become: yes + apt: name="{{item}}" + with_items: "{{hap_ubuntu_apt_packages}}" + +- name: download and unarchive HAP-NodeJS + unarchive: src="{{hap_src}}" dest="{{hap_home}}" remote_src="yes" creates="{{hap_dest}}" + +- name: install npm packages + npm: name="{{item}}" path="{{hap_dest}}" + with_items: "{{hap_npm_packages}}" + +- name: clean up accessory examples + file: path="{{hap_dest}}/accessories" state="absent" + +- name: create accessories dir + file: path="{{hap_dest}}/accessories" state="directory" + +- name: create python dir + file: path="{{hap_dest}}/python" state="directory" + +- name: install doord.py + copy: src="doord.py" dest="{{hap_dest}}/python/doord.py" + notify: restart hap-nodejs service + +- name: install door accessory + copy: src="Door_accessory.js" dest="{{hap_dest}}/accessories/Door_accessory.js" + notify: restart hap-nodejs service + +- name: build HAP-NodeJS + command: "npm install" + args: + chdir: "{{hap_dest}}" + changed_when: false + +- name: configure systemd service + become: yes + template: src="hap-nodejs.service.j2" dest="/lib/systemd/system/hap-nodejs.service" + notify: restart hap-nodejs service + +- name: ensure hap-nodejs is started + become: yes + systemd: name="hap-nodejs.service" enabled="yes" state="started" diff --git a/roles/hap-nodejs/templates/hap-nodejs.service.j2 b/roles/hap-nodejs/templates/hap-nodejs.service.j2 new file mode 100644 index 0000000..f657555 --- /dev/null +++ b/roles/hap-nodejs/templates/hap-nodejs.service.j2 @@ -0,0 +1,16 @@ +# {{ansible_managed}} + +[Unit] +Description=HAP-NodeJS + +[Service] +User={{hap_user}} +Group={{hap_user}} +Restart=always +RestartSec={{hap_restart_sec}} + +WorkingDirectory={{hap_dest}} +ExecStart=/usr/bin/node {{hap_dest}}/Core.js + +[Install] +WantedBy=default.target diff --git a/roles/nodejs/tasks/main.yaml b/roles/nodejs/tasks/main.yaml new file mode 100644 index 0000000..fca9008 --- /dev/null +++ b/roles/nodejs/tasks/main.yaml @@ -0,0 +1,38 @@ +--- + +# https://github.com/nfarina/homebridge/wiki/Running-HomeBridge-on-a-Raspberry-Pi + +- name: download and unarchive nodejs + become: yes + unarchive: + src: "https://nodejs.org/dist/v{{nodejs_version}}/{{nodejs_dir}}.tar.xz" + dest: "/opt/" + remote_src: yes + creates: "/opt/{{nodejs_dir}}" + +# sudo update-alternatives --install "/usr/bin/node" "node" "/opt/node/bin/node" 1 +- name: update node alterantives + become: yes + alternatives: + link: "/usr/bin/{{item}}" + name: "{{item}}" + path: "/opt/{{nodejs_dir}}/bin/{{item}}" + priority: "1" + with_items: + - "node" + - "npm" + +- name: npm install global deps + become: yes + npm: name="{{item}}" global="yes" + with_items: + - "npm" + - "node-gyp" + +- name: update node-gyp alterantives + become: yes + alternatives: + link: "/usr/bin/node-gyp" + name: "node-gyp" + path: "/opt/{{nodejs_dir}}/bin/node-gyp" + priority: "1" |
