From 546fe62b1cc7c221caef62f976e0db618f1df0ac Mon Sep 17 00:00:00 2001 From: Heiko Rothe Date: Tue, 11 Feb 2020 20:08:17 +0100 Subject: [PATCH] feat: added Docker configuration A release will trigger an automatic build of a multi-arch Docker image that can be used on most Docker platforms, including the Raspberry Pi. --- .dockerignore | 3 ++ .github/workflows/docker.yml | 25 +++++++++++++++ .github/workflows/gh-pages.yml | 2 ++ Dockerfile | 22 +++++++++++++ dev.Dockerfile | 23 ++++++++++++++ docker-compose.yml | 15 ++++++--- docker/entrypoint.sh | 6 ++++ docker/supervisord.conf | 19 ++++++++++++ docs/guide/configuration.md | 31 ++++++++++++++++--- docs/guide/installation.md | 4 +++ .../home-assistant.service.spec.ts | 20 ++++++++++++ .../home-assistant/home-assistant.service.ts | 9 ++++-- 12 files changed, 168 insertions(+), 11 deletions(-) create mode 100644 .dockerignore create mode 100644 .github/workflows/docker.yml create mode 100644 Dockerfile create mode 100644 dev.Dockerfile create mode 100755 docker/entrypoint.sh create mode 100644 docker/supervisord.conf diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..f807491 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +* +!docker/ +!/*.tgz diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000..02ed4c5 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,25 @@ +name: Docker + +on: + push: + tags: + - v* + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Get the version + id: get_version + run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//} + - name: Login to DockerHub Registry + run: echo ${{ secrets.DOCKERHUB_PASSWORD }} | docker login -u ${{ secrets.DOCKERHUB_USERNAME }} --password-stdin + - name: Set up Docker Buildx + id: buildx + uses: crazy-max/ghaction-docker-buildx@v1 + with: + version: latest + - name: Run Buildx + run: docker buildx build --build-arg ROOM_ASSISTANT_VERSION=${{steps.get_version.outputs.VERSION}} --platform linux/386,linux/amd64,linux/arm/v7,linux/arm64 -t mkerix/room-assistant:${{steps.get_version.outputs.VERSION}} --push . diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index 14744d5..1402a4d 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -2,6 +2,8 @@ name: Documentation on: push: + paths: + - 'docs/**' branches: - nextgen diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..4b03989 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,22 @@ +FROM node:12-alpine as build +ARG ROOM_ASSISTANT_VERSION=latest + +RUN apk add --no-cache python make g++ libusb-dev eudev-dev avahi-dev +RUN npm install -g --unsafe-perm room-assistant@$ROOM_ASSISTANT_VERSION + +FROM node:12-alpine + +WORKDIR /room-assistant + +RUN apk add --no-cache supervisor bluez bluez-deprecated libusb avahi avahi-dev dmidecode \ + && mkdir -p /var/run/dbus \ + && setcap cap_net_raw+eip $(eval readlink -f `which node`) \ + && setcap cap_net_raw+eip $(eval readlink -f `which hcitool`) \ + && addgroup --gid 998 i2c \ + && addgroup node i2c \ + && ln -s /usr/local/lib/node_modules/room-assistant/bin/room-assistant.js /usr/local/bin/room-assistant +COPY docker/supervisord.conf docker/entrypoint.sh /etc/ +COPY --from=build /usr/local/lib/node_modules/room-assistant /usr/local/lib/node_modules/room-assistant + +ENTRYPOINT ["/bin/sh", "-c"] +CMD ["/etc/entrypoint.sh"] diff --git a/dev.Dockerfile b/dev.Dockerfile new file mode 100644 index 0000000..81b3625 --- /dev/null +++ b/dev.Dockerfile @@ -0,0 +1,23 @@ +FROM node:12-alpine as build +WORKDIR /room-assistant + +RUN apk add --no-cache python make g++ libusb-dev eudev-dev avahi-dev +COPY ./*.tgz /room-assistant/ +RUN npm install -g --unsafe-perm *.tgz + +FROM node:12-alpine + +WORKDIR /room-assistant + +RUN apk add --no-cache supervisor bluez bluez-deprecated libusb avahi avahi-dev dmidecode \ + && mkdir -p /var/run/dbus \ + && setcap cap_net_raw+eip $(eval readlink -f `which node`) \ + && setcap cap_net_raw+eip $(eval readlink -f `which hcitool`) \ + && addgroup --gid 998 i2c \ + && addgroup node i2c \ + && ln -s /usr/local/lib/node_modules/room-assistant/bin/room-assistant.js /usr/local/bin/room-assistant +COPY docker/supervisord.conf docker/entrypoint.sh /etc/ +COPY --from=build /usr/local/lib/node_modules/room-assistant /usr/local/lib/node_modules/room-assistant + +ENTRYPOINT ["/bin/sh", "-c"] +CMD ["/etc/entrypoint.sh"] diff --git a/docker-compose.yml b/docker-compose.yml index 93a2e99..a66a9c0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,13 @@ version: '3' services: - mqtt: - image: eclipse-mosquitto:1.6 - ports: - - 1883:1883 + room-assistant: + build: . + network_mode: host + environment: + NODE_CONFIG: > + { + "global": { + "instanceName": "dev-instance", + "integrations": [] + } + } diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh new file mode 100755 index 0000000..acfdd4e --- /dev/null +++ b/docker/entrypoint.sh @@ -0,0 +1,6 @@ +echo "Starting DBUS daemon..." +dbus-uuidgen > /var/lib/dbus/machine-id +dbus-daemon --config-file=/usr/share/dbus-1/system.conf + +echo "Starting supervisor..." +exec supervisord -c /etc/supervisord.conf diff --git a/docker/supervisord.conf b/docker/supervisord.conf new file mode 100644 index 0000000..07f21e3 --- /dev/null +++ b/docker/supervisord.conf @@ -0,0 +1,19 @@ +[supervisord] +nodaemon=true +user=root + +[program:avahi-daemon] +command=avahi-daemon +priority=50 +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 + +[program:room-assistant] +command=room-assistant +user=node +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 diff --git a/docs/guide/configuration.md b/docs/guide/configuration.md index 552f309..69c6bdc 100644 --- a/docs/guide/configuration.md +++ b/docs/guide/configuration.md @@ -1,14 +1,35 @@ # Configuration -room-assistant can be configured using YAML and JSON files. It will look for them in the `config` subdirectory of the current working directory. -The files in the config folder are loaded according to a [specific order](https://github.com/lorenwest/node-config/wiki/Configuration-Files#file-load-order) and then merged into a single configuration. -In most cases you should only need a single file called `local.yml` or `local.json` in the config folder though. +room-assistant can be configured using YAML and JSON files. It will look for them in the `config` subdirectory of the current working directory. The files in the config folder are loaded according to a [specific order](https://github.com/lorenwest/node-config/wiki/Configuration-Files#file-load-order) and then merged into a single configuration. In most cases you should only need a single file called `local.yml` or `local.json` in the config folder though. -For example, let's say you are launching room-assistant from the directory `/home/pi/room-assistant`. -In this case you should create a file `/home/pi/room-assistant/config/local.yml` and put your configuration in there. +For example, let's say you are launching room-assistant from the directory `/home/pi/room-assistant`. In this case you should create a file `/home/pi/room-assistant/config/local.yml` and put your configuration in there. You can find the global configuration options below and the ones specific to some integrations on their respective pages. +## Configuring with Docker + +The official [Docker image](https://hub.docker.com/r/mkerix/room-assistant/) can be configured in two different ways. You can either mount your local config folder into the container as `/room-assistant/config` or you can provide the configuration as JSON through an environment variable. + +::: details Example docker-compose.yml + +```yaml +version: '3' +services: + room-assistant: + image: mkerix/room-assistant + network_mode: host + environment: + NODE_CONFIG: > + { + "global": { + "instanceName": "living-room", + "integrations": ["homeAssistant", "bluetoothClassic"] + } + } +``` + +::: + ## Core Settings **Config Key:** `global` diff --git a/docs/guide/installation.md b/docs/guide/installation.md index 91dd9bb..5ff7580 100644 --- a/docs/guide/installation.md +++ b/docs/guide/installation.md @@ -26,6 +26,10 @@ sudo npm i --global --unsafe-perm room-assistant npm will link the binary for running the software, usually into `/usr/bin/room-assistant`. If the directory is already in your `PATH` you can start it directly by typing `room-assistant`. Otherwise you can start it by typing the full path name of where it was installed. +## Running with Docker + +This project provides official Docker images on [Docker Hub](https://hub.docker.com/r/mkerix/room-assistant/). You can either use the latest or a specific version by using the correct tag. It is strongly recommended to run this image with the `host` network, otherwise you may run into problems with many parts of the software. + ## Running with Hass.io You can install room-assistant as Hass.io add-on by [adding the following URL as repository](https://www.home-assistant.io/hassio/installing_third_party_addons/): diff --git a/src/integrations/home-assistant/home-assistant.service.spec.ts b/src/integrations/home-assistant/home-assistant.service.spec.ts index 9fab782..9ad5223 100644 --- a/src/integrations/home-assistant/home-assistant.service.spec.ts +++ b/src/integrations/home-assistant/home-assistant.service.spec.ts @@ -299,6 +299,26 @@ describe('HomeAssistantService', () => { }); }); + it('should make the instance name the device identifier if no serial was found', async () => { + mockSystem.mockResolvedValue({ + serial: '-', + model: 'Docker Container', + manufacturer: '' + } as SystemData); + + await service.onModuleInit(); + service.handleNewEntity(new Sensor('grideye-sensor', 'GridEYE Sensor')); + + expect(JSON.parse(mockMqttClient.publish.mock.calls[0][1])).toMatchObject({ + device: { + identifiers: 'test-instance', + name: 'test-instance', + model: 'Docker Container', + manufacturer: '' + } + }); + }); + it('should apply sensor customizations to the discovery message', async () => { await service.onModuleInit(); service.handleNewEntity(new Sensor('custom-sensor', 'Custom'), [ diff --git a/src/integrations/home-assistant/home-assistant.service.ts b/src/integrations/home-assistant/home-assistant.service.ts index 5cd41f2..8912f55 100644 --- a/src/integrations/home-assistant/home-assistant.service.ts +++ b/src/integrations/home-assistant/home-assistant.service.ts @@ -222,9 +222,14 @@ export class HomeAssistantService * @returns Device information */ protected async getDeviceInfo(): Promise { + const instanceName = this.configService.get('global').instanceName; const systemInfo = await system(); - const device = new Device(systemInfo.serial); - device.name = this.configService.get('global').instanceName; + const serial = + systemInfo.serial && systemInfo.serial !== '-' + ? systemInfo.serial + : makeId(instanceName); + const device = new Device(serial); + device.name = instanceName; device.model = systemInfo.model; device.manufacturer = systemInfo.manufacturer; return device;