From 098c55866f0a3869cb546cd9e39327a3e90f3040 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Mon, 14 Aug 2017 17:41:07 +0200 Subject: [PATCH] :sparkles: Add new Hass.io add-on: Homebridge --- CHANGELOG.md | 13 ++ Dockerfile | 29 +++ README.md | 288 +++++++++++++++++++++++ config.json | 23 ++ files/avahi-daemon.conf | 48 ++++ files/homebridge-config.json | 34 +++ files/run.sh | 434 +++++++++++++++++++++++++++++++++++ 7 files changed, 869 insertions(+) create mode 100644 CHANGELOG.md create mode 100644 Dockerfile create mode 100644 README.md create mode 100644 config.json create mode 100644 files/avahi-daemon.conf create mode 100644 files/homebridge-config.json create mode 100644 files/run.sh diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..a53951c --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,13 @@ +# Hass.io Add-on Changelog: Homebridge + +All notable changes to this add-on will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) +and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## 1.0.0 - 2017-08-14 +### Added +- First version of the Homebridge Add-on +- This CHANGELOG file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..9ed1adb --- /dev/null +++ b/Dockerfile @@ -0,0 +1,29 @@ +FROM %%BASE_IMAGE%% +MAINTAINER Franck Nijhof + +# Add env +ENV LANG="C.UTF-8" + +# Copy run file +COPY files/run.sh / + +# Setup base +RUN apk add --no-cache jq git python make g++ libffi-dev openssl-dev openrc dbus \ + avahi-compat-libdns_sd avahi-dev avahi-tools avahi-ui-tools \ + nodejs-current nodejs-current-npm yarn && \ + mkdir -p /var/run/dbus && \ + mkdir -p /var/run/avahi-daemon && \ + chown messagebus:messagebus /var/run/dbus && \ + chown avahi:avahi /var/run/avahi-daemon && \ + yarn global add node-gyp && \ + yarn global add homebridge@0.4.22 && \ + yarn global add homebridge-homeassistant && \ + npm cache clean && \ + yarn cache clean && \ + chmod a+x /run.sh + +# Copy configuration files +COPY files/avahi-daemon.conf /etc/avahi/ +COPY files/homebridge-config.json /root/ + +CMD [ "/run.sh" ] diff --git a/README.md b/README.md new file mode 100644 index 0000000..571cac8 --- /dev/null +++ b/README.md @@ -0,0 +1,288 @@ +# Hass.io Add-on: Homebridge + +![Project Stage][project-stage-shield] +![Maintenance][maintenance-shield] +![Awesome][awesome-shield] +[![License][license-shield]](LICENSE.md) + + +This add-on provides the installation, configuration, and integration for +[Homebridge][homebridge]. + +Homebridge is a server that emulates the iOS HomeKit API, allowing you to +control your Home Assistant through via Apple devices (including Siri). + +Since Siri supports devices added through HomeKit, this means that with +Homebridge you can ask Siri to control devices that don't have any support for +HomeKit at all. For instance, using just some of the available plugins, +you can say: + +* _Siri, unlock the back door._ +* _Siri, open the garage door._ +* _Siri, turn on the coffee maker._ +* _Siri, turn on the living room lights._ +* _Siri, good morning!_ + +## Docker status + +[![Docker Architecture][armhf-arch-shield]][armhf-dockerhub] +[![Docker Version][armhf-version-shield]][armhf-microbadger] +[![Docker Layers][armhf-layers-shield]][armhf-microbadger] +[![Docker Pulls][armhf-pulls-shield]][armhf-dockerhub] + +[![Docker Architecture][aarch64-arch-shield]][aarch64-dockerhub] +[![Docker Version][aarch64-version-shield]][aarch64-microbadger] +[![Docker Layers][aarch64-layers-shield]][aarch64-microbadger] +[![Docker Pulls][aarch64-pulls-shield]][aarch64-dockerhub] + +[![Docker Architecture][amd64-arch-shield]][amd64-dockerhub] +[![Docker Version][amd64-version-shield]][amd64-microbadger] +[![Docker Layers][amd64-layers-shield]][amd64-microbadger] +[![Docker Pulls][amd64-pulls-shield]][amd64-dockerhub] + +[![Docker Architecture][i386-arch-shield]][i386-dockerhub] +[![Docker Version][i386-version-shield]][i386-microbadger] +[![Docker Layers][i386-layers-shield]][i386-microbadger] +[![Docker Pulls][i386-pulls-shield]][i386-dockerhub] + +## Installation + +The installation of this add-on is pretty straight forward and not different in +comparison to installing any other Hass.io add-on. + +1. [Add our Hass.io add-ons repository][repository] to your Hass.io instance +2. Install the "Homebridge" add-on +3. Start the "Homebridge" add-on +4. Check the logs of the "Homebridge" add-on to see if everything went well and +to find the pin code needed to add your Home Assistant instance to +your iOS device + +Please read the rest of this document further instructions. + +## Configuration + +On the first run, this add-on will create the necessary configuration files +for you. Stored in `/config/homebridge/`. You can modify the configuration +to your own liking. For documentation on configuring Homebridge, please refer +to the [Homebridge GitHub][homebridge] repository. For documentation on +configuring the Home Assistant for Homebridge plugin, please refer to the +[Home Assistant for Homebridge GitHub][homebridge-homeassistant] repository. + +The add-on has a limited configuration possibility as well. + +_Please remember to restart the add-on when the configuration changes._ + +Example add-on configuration: +```json +{ + "debug": false, + "plugins": [] +} +``` + +**Option: `debug`** + +When set to `true` the addon will output more information in the logs of the +add-on. The add-on will also start all the services in debug mode, which causes +more output in the log files. + +This might be useful when you're dealing with an unknown issue. It is +recommended leaving to option set to `false`, unless you are troubleshooting. + +**Option: `plugins`** + +This Homebridge add-on has support for installing additional Homebridge plugins. +Plugins are NodeJS modules published through NPM and tagged with the keyword +`homebridge-plugin`. They must have a name with the prefix `homebridge-`, +like `homebridge-mysmartlock`. + +The `homebridge-homeassistant` plugin is already installed for you. + +You can install a plugin by adding it to the add-on configuration. The add-on +will ensure the plugin is installed on start. + +Example add-on configuration: +```json +{ + "debug": false, + "plugins": [ + "homebridge-dummy", + "homebridge-mysmartlock" + ] +} +``` + +You can explore all available plugins at the NPM website by [searching for the +keyword `homebridge-plugin`][homebridge-plugins]. + +## Adding Homebridge to iOS + +Using the Home app (or most other HomeKit apps), you should be able to add the +single accessory "Home Assistant", assuming that you're still running the +Homebridge add-on and you're on the same (Wifi) network. Adding this accessory +will automatically add all accessories and platforms defined in your +Home Assistant instance. + +When you attempt to add the "Home Assistant" accessory, it will ask for a +"PIN code". This pin code is randomly generated when this add-on is run for +the first time. You can find the generated PIN code in the add-on logs and in +your `/config/homebridge/config.json` file (where you, of course, +can change it as well). + + +## Known issues and limitations + +* Once your device has been added to HomeKit, you should be able to tell Siri to +control your devices. However, realize that Siri is a cloud service, and iOS may +need some time to synchronize your device information with iCloud. +* Siri will almost always prefer its default phrase handling over HomeKit +devices. For instance, if you name your Sonos device "Radio" and try saying +"Siri, turn on the Radio" then Siri will probably start playing an iTunes Radio +station on your phone. Even if you name it "Sonos" and say +"Siri, turn on the Sonos", Siri will probably just launch the Sonos app instead. +This is why, for instance, the suggested `name` for the Sonos accessory +is "Speakers". +* One installation of Homebridge can only expose 100 accessories due to a +HomeKit limit. +* Once an accessory has been added to the Home app, changing its name via +Homebridge won't be automatically reflected in iOS. You must change it via +the Home app as well. +* IPv6 support has been removed from this addon, because of known issues with +libraries used by Homebridge. If you rely solely on IPv6 in your network, +this add-on would not work. +* If you have set up SSL using a self-signed certificate, you will need to +set verify_ssl to false in your `/config/homebridge/config.json` file to allow +bypassing the Node.js certificate checks. +* Errors on startup. The following errors are experienced when starting +Homebridge and can be safely ignored. +``` +*** WARNING *** The program 'nodejs' uses the Apple Bonjour compatibility layer of Avahi +*** WARNING *** Please fix your application to use the native API of Avahi! +*** WARNING *** For more information see http://0pointerde/avahi-compat?s=libdns_sd&e=nodejs +*** WARNING *** The program 'nodejs' called 'DNSServiceRegister()' which is not supported (or only supported partially) in the Apple Bonjour compatibility layer of Avahi +*** WARNING *** Please fix your application to use the native API of Avahi! +*** WARNING *** For more information see http://0pointerde/avahi-compat?s=libdns_sd&e=nodejs&f=DNSServiceRegister +``` + +## FAQ + +**Homebridge cannot connect or login to Home Assistant** + +Please be sure to set `host` and `password` parameters in the +`/config/homebridge/config.json` file. +Normally `http://172.17.0.1:8123` should be a sufficient `host` to use when +running hass.io. +If a password protects you Home Assistant instance, you'll also need to +set the `password`, which is empty by default. + +**My iOS App Can't Find Homebridge/Home Assistant** + +Two reasons why Homebridge may not be discoverable: + + 1. Homebridge server thinks it's been paired with, but iOS thinks otherwise. + Fix: deleted `persist/` directory which is in your `config/homebridge` directory + and restart the add-on. + + 2. iOS device has gotten your Homebridge `username` (looks like a MAC address) + "stuck" somehow, where it's in the database but inactive. Fix: change your + `username` in the "bridge" section of `/config/homebridge/config.json` to be + some new value and restart the add-on. + + +## Support + +Got questions? Got some unexpected behavior caused by this plugin? + +Please [open an issue on our GitHub repository][issues] and we'll do our best +to help you out. + +## Credits + +A big shout out to the following people, without them this add-on wasn't possible: +- The team & community of [Home Assistant][home-assistant] for developing such an +excellent home automation toolkit +- [Nick Farina][nfarina] for developing [Homebrige][homebridge] + +This add-on has been inspired by the following repositories: +- [docker-homebridge][oznu-docker-homebridge] by [Oznu][oznu] +- [homebridge-docker][ckuburlis-homebridge-docker]by [ckuburlis] +- [hassio-addons][olivierguerriat-hassio-addons] by [Olivier Guerriat][olivierguerriat] + +Thank you all! + + +## More Hass.io add-ons + +Do you like this add-on? Want some more functionality to your Hass.io Home +Assistant instance? + +We've created multiple add-ons for Hass.io. For a full list, check out +our[GitHub Repository][repository]. + +## License + +MIT License + +Copyright (c) 2017 Franck Nijhof + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + +[aarch64-arch-shield]: https://img.shields.io/badge/architecture-aarch64-blue.svg +[aarch64-dockerhub]: https://hub.docker.com/r/hassioaddons/homebridge-aarch64 +[aarch64-layers-shield]: https://images.microbadger.com/badges/image/hassioaddons/homebridge-aarch64.svg +[aarch64-microbadger]: https://microbadger.com/images/hassioaddons/homebridge-aarch64 +[aarch64-pulls-shield]: https://img.shields.io/docker/pulls/hassioaddons/homebridge-aarch64.svg +[aarch64-version-shield]: https://images.microbadger.com/badges/version/hassioaddons/homebridge-aarch64.svg +[amd64-arch-shield]: https://img.shields.io/badge/architecture-amd64-blue.svg +[amd64-dockerhub]: https://hub.docker.com/r/hassioaddons/homebridge-amd64 +[amd64-layers-shield]: https://images.microbadger.com/badges/image/hassioaddons/homebridge-amd64.svg +[amd64-microbadger]: https://microbadger.com/images/hassioaddons/homebridge-amd64 +[amd64-pulls-shield]: https://img.shields.io/docker/pulls/hassioaddons/homebridge-amd64.svg +[amd64-version-shield]: https://images.microbadger.com/badges/version/hassioaddons/homebridge-amd64.svg +[armhf-arch-shield]: https://img.shields.io/badge/architecture-armhf-blue.svg +[armhf-dockerhub]: https://hub.docker.com/r/hassioaddons/homebridge-armhf +[armhf-layers-shield]: https://images.microbadger.com/badges/image/hassioaddons/homebridge-armhf.svg +[armhf-microbadger]: https://microbadger.com/images/hassioaddons/homebridge-armhf +[armhf-pulls-shield]: https://img.shields.io/docker/pulls/hassioaddons/homebridge-armhf.svg +[armhf-version-shield]: https://images.microbadger.com/badges/version/hassioaddons/homebridge-armhf.svg +[awesome-shield]: https://img.shields.io/badge/awesome%3F-yes-brightgreen.svg +[ckuburlis-homebridge-docker]: https://github.com/ckuburlis/homebridge-docker +[ckuburlis]: https://github.com/ckuburlis +[home-assistant]: https://home-assistant.io +[homebridge-homeassistant]: https://github.com/home-assistant/homebridge-homeassistant +[homebridge-plugins]: https://www.npmjs.com/search?q=homebridge-plugin +[homebridge]: https://github.com/nfarina/homebridge +[i386-arch-shield]: https://img.shields.io/badge/architecture-i386-blue.svg +[i386-dockerhub]: https://hub.docker.com/r/hassioaddons/homebridge-i386 +[i386-layers-shield]: https://images.microbadger.com/badges/image/hassioaddons/homebridge-i386.svg +[i386-microbadger]: https://microbadger.com/images/hassioaddons/homebridge-i386 +[i386-pulls-shield]: https://img.shields.io/docker/pulls/hassioaddons/homebridge-i386.svg +[i386-version-shield]: https://images.microbadger.com/badges/version/hassioaddons/homebridge-i386.svg +[issues]: https://github.com/hassio-addons/repository/issues +[license-shield]: https://img.shields.io/github/license/hassio-addons/repository.svg +[maintenance-shield]: https://img.shields.io/maintenance/yes/2017.svg +[nfarina]: https://github.com/nfarina +[olivierguerriat-hassio-addons]: https://github.com/olivierguerriat/hassio-addons +[olivierguerriat]: https://github.com/olivierguerriat +[oznu-docker-homebridge]: https://github.com/oznu/docker-homebridge +[oznu]: https://github.com/oznu +[project-stage-shield]: https://img.shields.io/badge/Project%20Stage-Development-yellowgreen.svg +[repository]: https://github.com/hassio-addons/repository diff --git a/config.json b/config.json new file mode 100644 index 0000000..6cea78c --- /dev/null +++ b/config.json @@ -0,0 +1,23 @@ +{ + "name": "Homebridge", + "version": "1.0.0", + "slug": "homebridge", + "description": "HomeKit support for your Home Assistant instance using Homebridge", + "url": "https://github.com/hassio-addons/homebridge", + "startup": "application", + "boot": "auto", + "host_network": true, + "privileged": [], + "map": [ + "config:rw" + ], + "options": { + "debug": false, + "plugins": [] + }, + "schema": { + "debug": "bool", + "plugins": ["str"] + }, + "image": "hassioaddons/homebridge-{arch}" +} diff --git a/files/avahi-daemon.conf b/files/avahi-daemon.conf new file mode 100644 index 0000000..f78d512 --- /dev/null +++ b/files/avahi-daemon.conf @@ -0,0 +1,48 @@ +[server] +host-name=hassio +domain-name=local +#browse-domains=0pointer.de, zeroconf.org +use-ipv4=yes +use-ipv6=no +#allow-interfaces=eth0 +#deny-interfaces=eth1 +#check-response-ttl=no +#use-iff-running=no +enable-dbus=yes +#disallow-other-stacks=no +#allow-point-to-point=no +#cache-entries-max=4096 +#clients-max=4096 +#objects-per-client-max=1024 +#entries-per-entry-group-max=32 +ratelimit-interval-usec=1000000 +ratelimit-burst=1000 + +[wide-area] +enable-wide-area=yes + +[publish] +#disable-publishing=no +#disable-user-service-publishing=no +#add-service-cookie=no +#publish-addresses=yes +publish-hinfo=no +publish-workstation=no +#publish-domain=yes +#publish-dns-servers=192.168.50.1, 192.168.50.2 +#publish-resolv-conf-dns-servers=yes +#publish-aaaa-on-ipv4=yes +#publish-a-on-ipv6=no + +[reflector] +#enable-reflector=no +#reflect-ipv=no + +[rlimits] +#rlimit-as= +rlimit-core=0 +rlimit-data=4194304 +rlimit-fsize=0 +rlimit-nofile=768 +rlimit-stack=4194304 +#rlimit-nproc=3 diff --git a/files/homebridge-config.json b/files/homebridge-config.json new file mode 100644 index 0000000..632d9aa --- /dev/null +++ b/files/homebridge-config.json @@ -0,0 +1,34 @@ +{ + "bridge": { + "name": "Home Assistant", + "username": "%%USERNAME%%", + "port": 51826, + "pin": "%%PIN%%" + }, + "description": "Homebridge for Home Assistant", + "accessories": [], + "platforms": [ + { + "platform": "HomeAssistant", + "name": "HomeAssistant", + "host": "http://172.17.0.1:8123", + "password": "", + "supported_types": [ + "binary_sensor", + "climate", + "cover", + "device_tracker", + "fan", + "group", + "input_boolean", + "light", + "lock", + "media_player", + "scene", + "sensor", + "switch" + ], + "logging": true + } + ] +} diff --git a/files/run.sh b/files/run.sh new file mode 100644 index 0000000..9ab1302 --- /dev/null +++ b/files/run.sh @@ -0,0 +1,434 @@ +#!/usr/bin/env bash +# ============================================================================== +# Hass.io Add-on for Home Assistant: Homebridge +# ============================================================================== + +set -o errexit # Exit script when a command exits with non-zero status +set -o nounset # Exit script on use of an undefined variable +set -o pipefail # Return exit status of the last command in the pipe that failed + +# ============================================================================== +# GLOBALS +# ============================================================================== + +# Exit codes +readonly EX_OK=0 # Successful termination +readonly EX_DBUS_ID_FAILED=3 # Failed to generated a unique D-Bus ID +readonly EX_PLUGIN_FAILED=4 # Homebridge plugin installation failed +readonly EX_DBUS_DAEMON=5 # Failed to start D-Bus daemon +readonly EX_PID_AVAHI=6 # Failed to remove Avahi daemon PID file +readonly EX_PID_DBUS=7 # Failed to remove D-Bus daemon PID file +readonly EX_PATCH_ERROR=8 # Failed to apply patch +readonly EX_HOMEBRIDGE_DIR=9 # Failed to create Homebridge config directory +readonly EX_HOMEBRIDGE_FILE=10 # Failed to create Homebridge config file +readonly EX_HOMEBRIDGE_USER=11 # Failed to set Homebridge user config +readonly EX_HOMEBRIDGE_PIN=12 # Failed to set Homebridge user pin + +# Configuration paths +readonly CONFIG_PATH=/data/options.json +readonly HOMEBRIDGE_CONFIG_FILE=/config/homebridge/config.json +readonly HOMEBRIDGE_TEMPLATE_CONFIG_FILE=/root/homebridge-config.json + +# PID Files +readonly PID_FILE_DBUS=/var/run/dbus.pid +readonly PID_FILE_AVAHI=/var/run/avahi-daemon/pid + +# D-Bus System bus socket file location +readonly DBUS_SYSTEM_SOCKET=/var/run/dbus/system_bus_socket + +# Debug mode? +readonly DEBUG=$(jq --raw-output ".debug" $CONFIG_PATH) + +# ============================================================================== +# UTILITY +# ============================================================================== + +# ------------------------------------------------------------------------------ +# Displays a status message +# Globals: +# None +# Arguments: +# $* Status message to display +# Returns: +# None +# ------------------------------------------------------------------------------ +display_status_message() { + local status=$* + + echo "[ADD-ON] $status" +} + +# ------------------------------------------------------------------------------ +# Displays a debug message +# Globals: +# None +# Arguments: +# $* Status message to display +# Returns: +# None +# ------------------------------------------------------------------------------ +display_debug_message() { + local status=$* + + if [[ "$DEBUG" == "true" ]]; + then + display_status_message "[DEBUG] $status" + fi +} + +# ------------------------------------------------------------------------------ +# Displays a error message and is able to terminate te script execution +# Globals: +# None +# Arguments: +# $1 Error message +# $2 Exitcode, script will continue execution when omitted +# Returns: +# None +# ------------------------------------------------------------------------------ +display_error_message() { + local status=$1 + local exitcode=${2:-0} + + echo "[ADD-ON][ERROR] An error occured!" >&2 + echo >&2 + echo "!!! ERROR: $status" >&2 + echo >&2 + + if [[ $exitcode -ne 0 ]]; + then + exit "$exitcode" + fi +} + +# ------------------------------------------------------------------------------ +# Gets a configuration parameter form the configuration file +# Globals: +# CONFIG_PATH +# Arguments: +# $1 Key of the configuration parameter to get +# $2 'true' If the configuration value is an array, false otherwise +# Returns: +# The configuration value +# ------------------------------------------------------------------------------ +get_config() { + local key=$1 + local is_array=$2 + + if $is_array; + then + command jq --raw-output ".$key[]" $CONFIG_PATH + else + command jq --raw-output ".$key" $CONFIG_PATH + fi +} + +# ------------------------------------------------------------------------------ +# Finds the MAC address of the main interface +# Globals: +# None +# Arguments: +# None +# Returns: +# MAC address of the main interface (upper-cased) +# ------------------------------------------------------------------------------ +get_mac_addr() { + local interface + local mac + + interface=$(ip route show default | awk '/default/ {print $5}') + mac=$(cat "/sys/class/net/$interface/address") + + echo "${mac^^}" +} + +# ------------------------------------------------------------------------------ +# Generates a random pin number for HomeKit (###-##-### format) +# Globals: +# None +# Arguments: +# None +# Returns: +# The generated random pin number +# ------------------------------------------------------------------------------ +generate_homekit_pin() { + local pin + + pin=$(< /dev/urandom tr -dc 0-9 | head -c3) + pin+="-" + pin+=-$(< /dev/urandom tr -dc 0-9 | head -c2) + pin+="-" + pin+=$(< /dev/urandom tr -dc 0-9 | head -c3) + + echo "$pin" +} + +# ============================================================================== +# SCRIPT LOGIC +# ============================================================================== + +# ------------------------------------------------------------------------------ +# Applies temporary but necessary patch to HAP NodeJS +# Globals: +# EX_PATCH_ERROR +# Arguments: +# None +# Returns: +# None +# ------------------------------------------------------------------------------ +apply_hap_nodejs_ipv6_hotfix() { + local eventedhttp_file='/usr/local/share/.config/yarn/global/node_modules/hap-nodejs/lib/util/eventedhttp.js' + + display_status_message 'Applying HAP NodeJS IPV6 HOTFIX' + command patch $eventedhttp_file <