From 5f7869f0aed2c253abff2db2e821fbc16c83ec6f Mon Sep 17 00:00:00 2001 From: Mike Degatano Date: Tue, 16 Nov 2021 10:38:21 -0500 Subject: [PATCH 1/7] Initial state of the addon --- .devcontainer/Dockerfile | 34 +++ .devcontainer/devcontainer.json | 14 ++ .devcontainer/supervisor.sh | 141 ++++++++++++ .github/ISSUE_TEMPLATE/bug_report.md | 36 +++ .github/ISSUE_TEMPLATE/feature_request.md | 19 ++ .github/dependabot.yaml | 11 + .github/labels.yml | 78 +++++++ .github/release-drafter.yml | 56 +++++ .github/workflows/ci.yaml | 191 ++++++++++++++++ .github/workflows/deploy.yaml | 212 ++++++++++++++++++ .github/workflows/labels.yaml | 22 ++ .github/workflows/lock.yaml | 21 ++ .github/workflows/pr-labels.yaml | 21 ++ .github/workflows/release-drafter.yaml | 18 ++ .github/workflows/stale.yaml | 38 ++++ .vscode/tasks.json | 30 +++ .yamllint | 66 ++++++ amridm2mqtt/Dockerfile | 82 +++++++ amridm2mqtt/build.yaml | 6 + amridm2mqtt/config.yaml | 35 +++ amridm2mqtt/rootfs/amridm2mqtt/amridm2mqtt.py | 145 ++++++++++++ amridm2mqtt/rootfs/amridm2mqtt/settings.py | 69 ++++++ .../rootfs/etc/cont-init.d/30-config.sh | 48 ++++ .../rootfs/etc/fix-attrs.d/permissions | 2 + .../rootfs/etc/services.d/amridm2mqtt/finish | 9 + .../rootfs/etc/services.d/amridm2mqtt/run | 66 ++++++ 26 files changed, 1470 insertions(+) create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.json create mode 100755 .devcontainer/supervisor.sh create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/dependabot.yaml create mode 100644 .github/labels.yml create mode 100644 .github/release-drafter.yml create mode 100644 .github/workflows/ci.yaml create mode 100644 .github/workflows/deploy.yaml create mode 100644 .github/workflows/labels.yaml create mode 100644 .github/workflows/lock.yaml create mode 100644 .github/workflows/pr-labels.yaml create mode 100644 .github/workflows/release-drafter.yaml create mode 100644 .github/workflows/stale.yaml create mode 100644 .vscode/tasks.json create mode 100644 .yamllint create mode 100644 amridm2mqtt/Dockerfile create mode 100644 amridm2mqtt/build.yaml create mode 100644 amridm2mqtt/config.yaml create mode 100644 amridm2mqtt/rootfs/amridm2mqtt/amridm2mqtt.py create mode 100644 amridm2mqtt/rootfs/amridm2mqtt/settings.py create mode 100644 amridm2mqtt/rootfs/etc/cont-init.d/30-config.sh create mode 100644 amridm2mqtt/rootfs/etc/fix-attrs.d/permissions create mode 100755 amridm2mqtt/rootfs/etc/services.d/amridm2mqtt/finish create mode 100644 amridm2mqtt/rootfs/etc/services.d/amridm2mqtt/run diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..f696222 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,34 @@ +FROM mcr.microsoft.com/vscode/devcontainers/base:debian + +WORKDIR /workspaces + +SHELL ["/bin/bash", "-o", "pipefail", "-c"] + +# Set Docker daemon config +RUN \ + mkdir -p /etc/docker \ + && echo '{"storage-driver": "vfs"}' > /etc/docker/daemon.json + +# Installa aditional tools +RUN \ + apt-get update \ + && apt-get install -y --no-install-recommends \ + dbus \ + network-manager \ + libpulse0 \ + xz-utils + +# Install docker +RUN curl -fsSL https://get.docker.com | sh - + +# Install shellcheck +RUN \ + curl -fLs \ + "https://github.com/koalaman/shellcheck/releases/download/stable/shellcheck-stable.linux.x86_64.tar.xz" \ + | tar -xJ \ + \ + && mv -f "./shellcheck-stable/shellcheck" "/usr/bin/shellcheck" \ + && rm -rf "./shellcheck-stable" + +# Generate a machine-id for this container +RUN rm /etc/machine-id && dbus-uuidgen --ensure=/etc/machine-id diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..22b768f --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,14 @@ +{ + "name": "Home Assistant Add-Ons", + "context": "..", + "dockerFile": "Dockerfile", + "appPort": ["7123:8123", "7357:4357"], + "postStartCommand": "service docker start", + "runArgs": ["-e", "GIT_EDITOR=code --wait", "--privileged"], + "extensions": ["timonwong.shellcheck", "esbenp.prettier-vscode"], + "settings": { + "terminal.integrated.shell.linux": "/bin/bash" + }, + "workspaceMount": "source=${localWorkspaceFolder},target=/workspaces/addons,type=bind,consistency=delegated", + "workspaceFolder": "/workspaces/addons" +} diff --git a/.devcontainer/supervisor.sh b/.devcontainer/supervisor.sh new file mode 100755 index 0000000..7c2ffe5 --- /dev/null +++ b/.devcontainer/supervisor.sh @@ -0,0 +1,141 @@ +#!/bin/bash +set -eE + +DOCKER_TIMEOUT=30 +DOCKER_PID=0 + +SUPERVISOR_VERSON="$(curl -s https://version.home-assistant.io/dev.json | jq -e -r '.supervisor')" + + +function start_docker() { + local starttime + local endtime + + echo "Starting docker." + dockerd 2> /dev/null & + DOCKER_PID=$! + + echo "Waiting for docker to initialize..." + starttime="$(date +%s)" + endtime="$(date +%s)" + until docker info >/dev/null 2>&1; do + if [ $((endtime - starttime)) -le $DOCKER_TIMEOUT ]; then + sleep 1 + endtime=$(date +%s) + else + echo "Timeout while waiting for docker to come up" + exit 1 + fi + done + echo "Docker was initialized" +} + + +function stop_docker() { + local starttime + local endtime + + echo "Stopping in container docker..." + if [ "$DOCKER_PID" -gt 0 ] && kill -0 "$DOCKER_PID" 2> /dev/null; then + starttime="$(date +%s)" + endtime="$(date +%s)" + + # Now wait for it to die + kill "$DOCKER_PID" + while kill -0 "$DOCKER_PID" 2> /dev/null; do + if [ $((endtime - starttime)) -le $DOCKER_TIMEOUT ]; then + sleep 1 + endtime=$(date +%s) + else + echo "Timeout while waiting for container docker to die" + exit 1 + fi + done + else + echo "Your host might have been left with unreleased resources" + fi +} + + +function cleanup_lastboot() { + if [[ -f /tmp/supervisor_data/config.json ]]; then + echo "Cleaning up last boot" + cp /tmp/supervisor_data/config.json /tmp/config.json + jq -rM 'del(.last_boot)' /tmp/config.json > /tmp/supervisor_data/config.json + rm /tmp/config.json + fi +} + + +function cleanup_docker() { + echo "Cleaning up stopped containers..." + docker rm "$(docker ps -a -q)" || true +} + +function run_supervisor() { + mkdir -p /tmp/supervisor_data + docker run --rm --privileged \ + --name hassio_supervisor \ + --security-opt seccomp=unconfined \ + --security-opt apparmor:unconfined \ + -v /run/docker.sock:/run/docker.sock:rw \ + -v /run/dbus:/run/dbus:ro \ + -v /run/udev:/run/udev:ro \ + -v /tmp/supervisor_data:/data:rw \ + -v "/workspaces/addons":/data/addons/local:rw \ + -v /etc/machine-id:/etc/machine-id:ro \ + -e SUPERVISOR_SHARE="/tmp/supervisor_data" \ + -e SUPERVISOR_NAME=hassio_supervisor \ + -e SUPERVISOR_DEV=1 \ + -e SUPERVISOR_MACHINE="qemux86-64" \ + "homeassistant/amd64-hassio-supervisor:${SUPERVISOR_VERSON}" +} + +function init_dbus() { + if pgrep dbus-daemon; then + echo "Dbus is running" + return 0 + fi + + echo "Startup dbus" + mkdir -p /var/lib/dbus + cp -f /etc/machine-id /var/lib/dbus/machine-id + + # cleanups + mkdir -p /run/dbus + rm -f /run/dbus/pid + + # run + dbus-daemon --system --print-address +} + +function init_udev() { + if pgrep systemd-udevd; then + echo "udev is running" + return 0 + fi + + echo "Startup udev" + + # cleanups + mkdir -p /run/udev + + # run + /lib/systemd/systemd-udevd --daemon + sleep 3 + udevadm trigger && udevadm settle +} + +echo "Start Test-Env" + +start_docker +trap "stop_docker" ERR + +docker system prune -f + +cleanup_lastboot +cleanup_docker +init_dbus +init_udev +run_supervisor +stop_docker diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..1582c81 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,36 @@ +--- +name: Bug report +about: Create a report to help us improve +title: "" +labels: "" +assignees: "" +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: + +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Logs** + +``` +Include any relevant logs +``` + +**Environment (please complete the following information):** + +- Add-on version: [e.g. 1.0.2] +- Supervisor version: [e.g.supervisor-2021.03.6] +- Operating system [e.g. Home Assistant OS 5.12] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..2bc5d5f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,19 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: "" +labels: "" +assignees: "" +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml new file mode 100644 index 0000000..d519b7c --- /dev/null +++ b/.github/dependabot.yaml @@ -0,0 +1,11 @@ +--- +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: daily + - package-ecosystem: docker + directory: "/amridm2mqtt" + schedule: + interval: daily diff --git a/.github/labels.yml b/.github/labels.yml new file mode 100644 index 0000000..ff8ca8d --- /dev/null +++ b/.github/labels.yml @@ -0,0 +1,78 @@ +--- +- name: "breaking-change" + color: ee0701 + description: "A breaking change for existing users." +- name: "bugfix" + color: ee0701 + description: "Inconsistencies or issues which will cause a problem for users or implementors." +- name: "documentation" + color: 0052cc + description: "Solely about the documentation of the project." +- name: "enhancement" + color: 1d76db + description: "Enhancement of the code, not introducing new features." +- name: "refactor" + color: 1d76db + description: "Improvement of existing code, not introducing new features." +- name: "performance" + color: 1d76db + description: "Improving performance, not introducing new features." +- name: "new-feature" + color: 0e8a16 + description: "New features or options." +- name: "maintenance" + color: 2af79e + description: "Generic maintenance tasks." +- name: "ci" + color: 1d76db + description: "Work that improves the continue integration." +- name: "dependencies" + color: 1d76db + description: "Upgrade or downgrade of project dependencies." + +- name: "in-progress" + color: fbca04 + description: "Issue is currently being resolved by a developer." +- name: "stale" + color: fef2c0 + description: "There has not been activity on this issue or PR for quite some time." +- name: "no-stale" + color: fef2c0 + description: "This issue or PR is exempted from the stable bot." + +- name: "security" + color: ee0701 + description: "Marks a security issue that needs to be resolved asap." +- name: "incomplete" + color: fef2c0 + description: "Marks a PR or issue that is missing information." +- name: "invalid" + color: fef2c0 + description: "Marks a PR or issue that is missing information." + +- name: "beginner-friendly" + color: 0e8a16 + description: "Good first issue for people wanting to contribute to the project." +- name: "help-wanted" + color: 0e8a16 + description: "We need some extra helping hands or expertise in order to resolve this." + +- name: "priority-critical" + color: ee0701 + description: "This should be dealt with ASAP. Not fixing this issue would be a serious error." +- name: "priority-high" + color: b60205 + description: "After critical issues are fixed, these should be dealt with before any further issues." +- name: "priority-medium" + color: 0e8a16 + description: "This issue may be useful, and needs some attention." +- name: "priority-low" + color: e4ea8a + description: "Nice addition, maybe... someday..." + +- name: "major" + color: b60205 + description: "This PR causes a major version bump in the version number." +- name: "minor" + color: 0e8a16 + description: "This PR causes a minor version bump in the version number." diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 0000000..445a16a --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,56 @@ +--- +name-template: "v$RESOLVED_VERSION" +tag-template: "v$RESOLVED_VERSION" +change-template: "- $TITLE @$AUTHOR (#$NUMBER)" +sort-direction: ascending + +categories: + - title: "🚨 Breaking changes" + labels: + - "breaking-change" + - title: "✨ New features" + labels: + - "new-feature" + - title: "πŸ› Bug fixes" + labels: + - "bugfix" + - title: "πŸš€ Enhancements" + labels: + - "enhancement" + - "refactor" + - "performance" + - title: "🧰 Maintenance" + labels: + - "maintenance" + - "ci" + - title: "πŸ“š Documentation" + labels: + - "documentation" + - title: "⬆️ Dependency updates" + labels: + - "dependencies" + +version-resolver: + major: + labels: + - "major" + - "breaking-change" + minor: + labels: + - "minor" + - "new-feature" + patch: + labels: + - "bugfix" + - "chore" + - "ci" + - "dependencies" + - "documentation" + - "enhancement" + - "performance" + - "refactor" + default: patch + +template: | + ## What’s changed + $CHANGES diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..34e4049 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,191 @@ +--- +name: CI + +# yamllint disable-line rule:truthy +on: + schedule: + - cron: "0 4 * * *" + push: + branches: + - main + pull_request: + types: + - opened + - reopened + - synchronize + workflow_dispatch: + +jobs: + information: + name: Gather add-on information + runs-on: ubuntu-latest + outputs: + architectures: ${{ steps.information.outputs.architectures }} + build: ${{ steps.information.outputs.build }} + description: ${{ steps.information.outputs.description }} + name: ${{ steps.information.outputs.name }} + slug: ${{ steps.information.outputs.slug }} + target: ${{ steps.information.outputs.target }} + steps: + - name: ‡️ Check out code from GitHub + uses: actions/checkout@v2.4.0 + - name: πŸš€ Run add-on information action + id: information + uses: frenck/action-addon-information@v1.3 + + lint-addon: + name: Lint Add-on + needs: + - information + runs-on: ubuntu-latest + steps: + - name: ‡️ Check out code from GitHub + uses: actions/checkout@v2.4.0 + - name: πŸš€ Run Add-on Lint + uses: frenck/action-addon-linter@v2.5 + with: + path: "./${{ needs.information.outputs.target }}" + + lint-hadolint: + name: Hadolint + needs: + - information + runs-on: ubuntu-latest + steps: + - name: ‡️ Check out code from GitHub + uses: actions/checkout@v2.4.0 + - name: πŸš€ Run Hadolint + uses: brpaz/hadolint-action@v1.5.0 + with: + dockerfile: "./${{ needs.information.outputs.target }}/Dockerfile" + + lint-json: + name: JSON Lint + runs-on: ubuntu-latest + steps: + - name: ‡️ Check out code from GitHub + uses: actions/checkout@v2.4.0 + - name: πŸš€ Run JQ + run: | + shopt -s globstar + cat **/*.json | jq '.' + lint-markdown: + name: MarkdownLint + runs-on: ubuntu-latest + steps: + - name: ‡️ Check out code from GitHub + uses: actions/checkout@v2.4.0 + - name: πŸš€ Run mdl + uses: actionshub/markdownlint@2.0.2 + + lint-shellcheck: + name: Shellcheck + runs-on: ubuntu-latest + steps: + - name: ‡️ Check out code from GitHub + uses: actions/checkout@v2.4.0 + - name: πŸš€ Run Shellcheck + uses: ludeeus/action-shellcheck@1.1.0 + env: + SHELLCHECK_OPTS: -s bash + + lint-yamllint: + name: YAMLLint + runs-on: ubuntu-latest + steps: + - name: ‡️ Check out code from GitHub + uses: actions/checkout@v2.4.0 + - name: πŸš€ Run YAMLLint + uses: frenck/action-yamllint@v1.1 + + lint-prettier: + name: Prettier + runs-on: ubuntu-latest + steps: + - name: ‡️ Check out code from GitHub + uses: actions/checkout@v2.4.0 + - name: πŸš€ Run Prettier + uses: creyD/prettier_action@v4.0 + with: + prettier_options: --write **/*.{json,js,md,yaml} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + build: + name: Build ${{ matrix.architecture }} + needs: + - information + - lint-addon + - lint-hadolint + - lint-json + - lint-markdown + - lint-prettier + - lint-shellcheck + - lint-yamllint + runs-on: ubuntu-latest + strategy: + matrix: + architecture: ${{ fromJson(needs.information.outputs.architectures) }} + steps: + - name: ‡️ Check out code from GitHub + uses: actions/checkout@v2.4.0 + - name: πŸ— Set up build cache + id: cache + uses: actions/cache@v2.1.6 + with: + path: /tmp/.docker-cache + key: docker-${{ github.ref }}-${{ matrix.architecture }}-${{ github.sha }} + restore-keys: | + docker-${{ github.ref }}-${{ matrix.architecture }} + - name: πŸ— Set up QEMU + uses: docker/setup-qemu-action@v1.2.0 + - name: πŸ— Set up Docker Buildx + uses: docker/setup-buildx-action@v1.6.0 + - name: ℹ️ Compose build flags + id: flags + run: | + echo "::set-output name=date::$(date +"%Y-%m-%dT%H:%M:%SZ")" + from=$(jq --raw-output ".build_from.${{ matrix.architecture }}" "${{ needs.information.outputs.build }}") + echo "::set-output name=from::${from}" + if [[ "${{ matrix.architecture}}" = "amd64" ]]; then + echo "::set-output name=platform::linux/amd64" + elif [[ "${{ matrix.architecture }}" = "i386" ]]; then + echo "::set-output name=platform::linux/386" + elif [[ "${{ matrix.architecture }}" = "armhf" ]]; then + echo "::set-output name=platform::linux/arm/v6" + elif [[ "${{ matrix.architecture }}" = "armv7" ]]; then + echo "::set-output name=platform::linux/arm/v7" + elif [[ "${{ matrix.architecture }}" = "aarch64" ]]; then + echo "::set-output name=platform::linux/arm64/v8" + else + echo "::error ::Could not determine platform for architecture ${{ matrix.architecture }}" + exit 1 + fi + - name: πŸš€ Build + uses: docker/build-push-action@v2.7.0 + with: + push: false + context: ${{ needs.information.outputs.target }} + file: ${{ needs.information.outputs.target }}/Dockerfile + cache-from: | + type=local,src=/tmp/.docker-cache + ghcr.io/mdegat01/${{ needs.information.outputs.slug }}/${{ matrix.architecture }}:edge + cache-to: type=local,mode=max,dest=/tmp/.docker-cache-new + platforms: ${{ steps.flags.outputs.platform }} + build-args: | + BUILD_ARCH=${{ matrix.architecture }} + BUILD_DATE=${{ steps.flags.outputs.date }} + BUILD_DESCRIPTION=${{ needs.information.outputs.description }} + BUILD_FROM=${{ steps.flags.outputs.from }} + BUILD_NAME=${{ needs.information.outputs.name }} + BUILD_REF=${{ github.sha }} + BUILD_REPOSITORY=${{ github.repository }} + BUILD_VERSION=edge + # This ugly bit is necessary, or our cache will grow forever... + # Well until we hit GitHub's limit of 5GB :) + # https://github.com/docker/build-push-action/issues/252 + # https://github.com/moby/buildkit/issues/1896 + - name: 🚚 Swap build cache + run: | + rm -rf /tmp/.docker-cache + mv /tmp/.docker-cache-new /tmp/.docker-cache diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml new file mode 100644 index 0000000..da7fc05 --- /dev/null +++ b/.github/workflows/deploy.yaml @@ -0,0 +1,212 @@ +--- +name: Deploy + +# yamllint disable-line rule:truthy +on: + release: + types: + - published + workflow_run: + workflows: ["CI"] + branches: [main] + types: + - completed + +jobs: + information: + if: | + github.event_name == 'release' + || ( + github.event_name == 'workflow_run' + && github.event.workflow_run.conclusion == 'success' + ) + name: ℹ️ Gather add-on information + runs-on: ubuntu-latest + outputs: + architectures: ${{ steps.information.outputs.architectures }} + build: ${{ steps.information.outputs.build }} + description: ${{ steps.information.outputs.description }} + environment: ${{ steps.release.outputs.environment }} + name: ${{ steps.information.outputs.name }} + slug: ${{ steps.information.outputs.slug }} + target: ${{ steps.information.outputs.target }} + version: ${{ steps.release.outputs.version }} + steps: + - name: ‡️ Check out code from GitHub + uses: actions/checkout@v2.4.0 + - name: πŸš€ Run add-on information action + id: information + uses: frenck/action-addon-information@v1.3 + - name: ℹ️ Gather version and environment + id: release + run: | + sha="${{ github.sha }}" + environment="edge" + version="${sha:0:7}" + if [[ "${{ github.event_name }}" = "release" ]]; then + version="${{ github.event.release.tag_name }}" + version="${version,,}" + version="${version#v}" + environment="stable" + if [[ "${{ github.event.release.prerelease }}" = "true" ]]; then + environment="beta" + fi + fi + echo "::set-output name=environment::${environment}" + echo "::set-output name=version::${version}" + deploy: + name: πŸ‘· Build & Deploy ${{ matrix.architecture }} + needs: information + runs-on: ubuntu-latest + strategy: + matrix: + architecture: ${{ fromJson(needs.information.outputs.architectures) }} + steps: + - name: πŸ”‚ Wait for other runs to complete + uses: softprops/turnstyle@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: ‡️ Check out code from GitHub + uses: actions/checkout@v2.4.0 + - name: πŸ— Set up build cache + id: cache + uses: actions/cache@v2.1.6 + with: + path: /tmp/.docker-cache + key: docker-${{ github.ref }}-${{ matrix.architecture }}-${{ github.sha }} + restore-keys: | + docker-${{ github.ref }}-${{ matrix.architecture }} + - name: πŸ— Set up QEMU + uses: docker/setup-qemu-action@v1.2.0 + - name: πŸ— Set up Docker Buildx + uses: docker/setup-buildx-action@v1.6.0 + - name: ℹ️ Compose build flags + id: flags + run: | + echo "::set-output name=date::$(date +"%Y-%m-%dT%H:%M:%SZ")" + from=$(jq --raw-output ".build_from.${{ matrix.architecture }}" "${{ needs.information.outputs.build }}") + echo "::set-output name=from::${from}" + if [[ "${{ matrix.architecture}}" = "amd64" ]]; then + echo "::set-output name=platform::linux/amd64" + elif [[ "${{ matrix.architecture }}" = "i386" ]]; then + echo "::set-output name=platform::linux/386" + elif [[ "${{ matrix.architecture }}" = "armhf" ]]; then + echo "::set-output name=platform::linux/arm/v6" + elif [[ "${{ matrix.architecture }}" = "armv7" ]]; then + echo "::set-output name=platform::linux/arm/v7" + elif [[ "${{ matrix.architecture }}" = "aarch64" ]]; then + echo "::set-output name=platform::linux/arm64/v8" + else + echo "::error ::Could not determine platform for architecture ${{ matrix.architecture }}" + exit 1 + fi + - name: πŸ— Login to GitHub Container Registry + uses: docker/login-action@v1.10.0 + with: + registry: ghcr.io + username: ${{ secrets.GHCR_USERNAME }} + password: ${{ secrets.GHCR_TOKEN }} + - name: πŸš€ Build and push + uses: docker/build-push-action@v2.7.0 + with: + push: true + # yamllint disable rule:line-length + tags: | + ghcr.io/mdegat01/${{ needs.information.outputs.slug }}/${{ matrix.architecture }}:${{ needs.information.outputs.environment }} + ghcr.io/mdegat01/${{ needs.information.outputs.slug }}/${{ matrix.architecture }}:${{ needs.information.outputs.version }} + # yamllint enable rule:line-length + context: ${{ needs.information.outputs.target }} + file: ${{ needs.information.outputs.target }}/Dockerfile + cache-from: | + type=local,src=/tmp/.docker-cache + ghcr.io/mdegat01/${{ needs.information.outputs.slug }}/${{ matrix.architecture }}:edge + cache-to: type=local,mode=max,dest=/tmp/.docker-cache-new + platforms: ${{ steps.flags.outputs.platform }} + build-args: | + BUILD_ARCH=${{ matrix.architecture }} + BUILD_DATE=${{ steps.flags.outputs.date }} + BUILD_DESCRIPTION=${{ needs.information.outputs.description }} + BUILD_FROM=${{ steps.flags.outputs.from }} + BUILD_NAME=${{ needs.information.outputs.name }} + BUILD_REF=${{ github.sha }} + BUILD_REPOSITORY=${{ github.repository }} + BUILD_VERSION=${{ needs.information.outputs.version }} + # This ugly bit is necessary, or our cache will grow forever... + # Well until we hit GitHub's limit of 5GB :) + # https://github.com/docker/build-push-action/issues/252 + # https://github.com/moby/buildkit/issues/1896 + - name: 🚚 Swap build cache + run: | + rm -rf /tmp/.docker-cache + mv /tmp/.docker-cache-new /tmp/.docker-cache + publish-edge: + name: πŸ“’ Publish to edge repository + if: needs.information.outputs.environment == 'edge' + needs: + - information + - deploy + environment: + name: edge + runs-on: ubuntu-latest + steps: + - name: πŸš€ Dispatch repository updater update signal + uses: peter-evans/repository-dispatch@v1.1.3 + with: + token: ${{ secrets.MY_GITHUB_TOKEN }} + repository: mdegat01/hassio-addons-edge + event-type: update + client-payload: > + { + "addon": "${{ needs.information.outputs.slug }}", + "name": "${{ needs.information.outputs.name }}", + "repository": "${{ github.repository }}", + "version": "${{ needs.information.outputs.version }}" + } + publish-beta: + name: πŸ“’ Publish to beta repository + if: | + needs.information.outputs.environment == 'beta' || + needs.information.outputs.environment == 'stable' + needs: + - information + - deploy + environment: + name: beta + runs-on: ubuntu-latest + steps: + - name: πŸš€ Dispatch repository updater update signal + uses: peter-evans/repository-dispatch@v1.1.3 + with: + token: ${{ secrets.MY_GITHUB_TOKEN }} + repository: mdegat01/hassio-addons-beta + event-type: update + client-payload: > + { + "addon": "${{ needs.information.outputs.slug }}", + "name": "${{ needs.information.outputs.name }}", + "repository": "${{ github.repository }}", + "version": "${{ needs.information.outputs.version }}" + } + publish-stable: + name: πŸ“’ Publish to stable repository + if: needs.information.outputs.environment == 'stable' + needs: + - information + - deploy + environment: + name: stable + runs-on: ubuntu-latest + steps: + - name: πŸš€ Dispatch repository updater update signal + uses: peter-evans/repository-dispatch@v1.1.3 + with: + token: ${{ secrets.MY_GITHUB_TOKEN }} + repository: mdegat01/hassio-addons + event-type: update + client-payload: > + { + "addon": "${{ needs.information.outputs.slug }}", + "name": "${{ needs.information.outputs.name }}", + "repository": "${{ github.repository }}", + "version": "${{ github.event.release.tag_name }}" + } diff --git a/.github/workflows/labels.yaml b/.github/workflows/labels.yaml new file mode 100644 index 0000000..81d6bb2 --- /dev/null +++ b/.github/workflows/labels.yaml @@ -0,0 +1,22 @@ +--- +name: Sync labels + +# yamllint disable-line rule:truthy +on: + push: + branches: + - main + paths: + - .github/labels.yml + +jobs: + labels: + name: ♻️ Sync labels + runs-on: ubuntu-latest + steps: + - name: ‡️ Check out code from GitHub + uses: actions/checkout@v2.4.0 + - name: πŸš€ Run Label Syncer + uses: micnncim/action-label-syncer@v1.3.0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/lock.yaml b/.github/workflows/lock.yaml new file mode 100644 index 0000000..cee6e4c --- /dev/null +++ b/.github/workflows/lock.yaml @@ -0,0 +1,21 @@ +--- +name: Lock + +# yamllint disable-line rule:truthy +on: + schedule: + - cron: "0 9 * * *" + workflow_dispatch: + +jobs: + lock: + name: πŸ”’ Lock closed issues and PRs + runs-on: ubuntu-latest + steps: + - uses: dessant/lock-threads@v3 + with: + github-token: ${{ github.token }} + issue-lock-inactive-days: "30" + issue-lock-reason: "" + pr-lock-inactive-days: "1" + pr-lock-reason: "" diff --git a/.github/workflows/pr-labels.yaml b/.github/workflows/pr-labels.yaml new file mode 100644 index 0000000..41da9cf --- /dev/null +++ b/.github/workflows/pr-labels.yaml @@ -0,0 +1,21 @@ +--- +name: PR Labels + +# yamllint disable-line rule:truthy +on: + pull_request: + types: [opened, labeled, unlabeled, synchronize] + +jobs: + pr_labels: + name: Verify + runs-on: ubuntu-latest + steps: + - name: 🏷 Verify PR has a valid label + uses: jesusvasquez333/verify-pr-label-action@v1.4.0 + with: + github-token: "${{ secrets.GITHUB_TOKEN }}" + valid-labels: >- + breaking-change, bugfix, documentation, enhancement, refactor, + performance, new-feature, maintenance, ci, dependencies + disable-reviews: true diff --git a/.github/workflows/release-drafter.yaml b/.github/workflows/release-drafter.yaml new file mode 100644 index 0000000..06cc26a --- /dev/null +++ b/.github/workflows/release-drafter.yaml @@ -0,0 +1,18 @@ +--- +name: Release Drafter + +# yamllint disable-line rule:truthy +on: + push: + branches: + - main + +jobs: + update_release_draft: + name: ✏️ Draft release + runs-on: ubuntu-latest + steps: + - name: πŸš€ Run Release Drafter + uses: release-drafter/release-drafter@v5.15.0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/stale.yaml b/.github/workflows/stale.yaml new file mode 100644 index 0000000..cccc221 --- /dev/null +++ b/.github/workflows/stale.yaml @@ -0,0 +1,38 @@ +--- +name: Stale + +# yamllint disable-line rule:truthy +on: + schedule: + - cron: "0 8 * * *" + workflow_dispatch: + +jobs: + stale: + name: 🧹 Clean up stale issues and PRs + runs-on: ubuntu-latest + steps: + - name: πŸš€ Run stale + uses: actions/stale@v4 + with: + repo-token: ${{ secrets.MY_GITHUB_TOKEN }} + days-before-stale: 30 + days-before-close: 7 + remove-stale-when-updated: true + stale-issue-label: "stale" + exempt-issue-labels: "no-stale,help-wanted" + stale-issue-message: > + There hasn't been any activity on this issue recently, so we + clean up some of the older and inactive issues. + Please make sure to update to the latest version and + check if that solves the issue. Let us know if that works for you + by leaving a comment πŸ‘ + This issue has now been marked as stale and will be closed if no + further activity occurs. Thanks! + stale-pr-label: "stale" + exempt-pr-labels: "no-stale" + stale-pr-message: > + There hasn't been any activity on this pull request recently. This + pull request has been automatically marked as stale because of that + and will be closed if no further activity occurs within 7 days. + Thank you for your contributions. diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..69b089b --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,30 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Start Home Assistant", + "type": "shell", + "command": "./.devcontainer/supervisor.sh", + "group": { + "kind": "test", + "isDefault": true + }, + "presentation": { + "reveal": "always", + "panel": "new" + }, + "problemMatcher": [] + }, + { + "label": "Run Home Assistant CLI", + "type": "shell", + "command": "docker exec -ti hassio_cli /usr/bin/cli.sh", + "group": "test", + "presentation": { + "reveal": "always", + "panel": "new" + }, + "problemMatcher": [] + } + ] +} diff --git a/.yamllint b/.yamllint new file mode 100644 index 0000000..f4bc5a4 --- /dev/null +++ b/.yamllint @@ -0,0 +1,66 @@ +--- +rules: + braces: + level: error + min-spaces-inside: 0 + max-spaces-inside: 1 + min-spaces-inside-empty: -1 + max-spaces-inside-empty: -1 + brackets: + level: error + min-spaces-inside: 0 + max-spaces-inside: 0 + min-spaces-inside-empty: -1 + max-spaces-inside-empty: -1 + colons: + level: error + max-spaces-before: 0 + max-spaces-after: 1 + commas: + level: error + max-spaces-before: 0 + min-spaces-after: 1 + max-spaces-after: 1 + comments: + level: error + require-starting-space: true + min-spaces-from-content: 2 + comments-indentation: + level: error + document-end: + level: error + present: false + document-start: + level: error + present: true + empty-lines: + level: error + max: 1 + max-start: 0 + max-end: 1 + hyphens: + level: error + max-spaces-after: 1 + indentation: + level: error + spaces: 2 + indent-sequences: true + check-multi-line-strings: false + key-duplicates: + level: error + line-length: + ignore: | + .github/support.yml + level: warning + max: 120 + allow-non-breakable-words: true + allow-non-breakable-inline-mappings: true + new-line-at-end-of-file: + level: error + new-lines: + level: error + type: unix + trailing-spaces: + level: error + truthy: + level: error diff --git a/amridm2mqtt/Dockerfile b/amridm2mqtt/Dockerfile new file mode 100644 index 0000000..cc2c1df --- /dev/null +++ b/amridm2mqtt/Dockerfile @@ -0,0 +1,82 @@ +ARG BUILD_FROM=ghcr.io/hassio-addons/debian-base/amd64 + +# https://hub.docker.com/_/alpine +FROM alpine:3.14.3 as build_rtlamr +# https://github.com/bemasher/rtlamr/releases +ENV RTLAMR_VERSION 0.9.1 + +RUN set -eux; \ + apk update; \ + apk add --no-cache --virtual .build-deps \ + tar=1.34-r0 \ + curl=7.79.1-r0 \ + ; \ + APKARCH="$(apk --print-arch)"; \ + case "${APKARCH}" in \ + x86_64) BINARCH='amd64' ;; \ + armhf) BINARCH='arm' ;; \ + armv7) BINARCH='arm' ;; \ + aarch64) BINARCH='arm64' ;; \ + *) echo >&2 "error: unsupported architecture (${APKARCH})"; exit 1 ;; \ + esac; \ + curl -J -L -o /tmp/rtlamr.tar.gz \ + "https://github.com/bemasher/rtlamr/releases/download/v${RTLAMR_VERSION}/rtlamr_linux_${BINARCH}.tar.gz"; \ + tar -xf /tmp/rtlamr.tar.gz -C /usr/bin; \ + mv /usr/bin/rtlamr_linux_${BINARCH} /usr/bin/rtlamr; \ + chmod a+x /usr/bin/rtlamr; \ + rm /tmp/rtlamr.tar.gz; + +# https://github.com/hassio-addons/addon-debian-base/releases +FROM ${BUILD_FROM}:5.2.2 + +RUN set -eux; \ + apt-get update; \ + apt-get install -qy --no-install-recommends \ + ca-certificates=20210119 \ + librtlsdr-dev=0.6.0-3 \ + python3-paho-mqtt=1.5.1-1 \ + rtl-sdr=0.6.0-3 \ + ; \ + update-ca-certificates; \ + rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*; \ + \ + echo "Add user for AMRIDM2MQTT"; \ + mkdir -p /data/amridm2mqtt; \ + addgroup -S abc; \ + adduser -u 12345 -h /data/amridm2mqtt/ -D -S abc -G abc; + +# Add rtlamr +COPY --from=build_rtlamr /usr/bin/rtlamr /usr/bin/rtlamr +RUN rtlamr --version + +COPY rootfs / +WORKDIR /data/amridm2mqtt + +# Build arguments +ARG BUILD_ARCH +ARG BUILD_DATE +ARG BUILD_DESCRIPTION +ARG BUILD_NAME +ARG BUILD_REF +ARG BUILD_REPOSITORY +ARG BUILD_VERSION + +# Labels +LABEL \ + io.hass.name="${BUILD_NAME}" \ + io.hass.description="${BUILD_DESCRIPTION}" \ + io.hass.arch="${BUILD_ARCH}" \ + io.hass.type="addon" \ + io.hass.version=${BUILD_VERSION} \ + maintainer="mdegat01" \ + org.opencontainers.image.title="${BUILD_NAME}" \ + org.opencontainers.image.description="${BUILD_DESCRIPTION}" \ + org.opencontainers.image.vendor="mdegat01's Home Assistant Add-ons" \ + org.opencontainers.image.authors="mdegat01" \ + org.opencontainers.image.licenses="MIT" \ + org.opencontainers.image.url="https://github.com/mdegat01/hassio-addons" \ + org.opencontainers.image.source="https://github.com/${BUILD_REPOSITORY}" \ + org.opencontainers.image.documentation="https://github.com/${BUILD_REPOSITORY}/blob/main/README.md" \ + org.opencontainers.image.created=${BUILD_DATE} \ + org.opencontainers.image.revision=${BUILD_REF} \ + org.opencontainers.image.version=${BUILD_VERSION} diff --git a/amridm2mqtt/build.yaml b/amridm2mqtt/build.yaml new file mode 100644 index 0000000..3f8affc --- /dev/null +++ b/amridm2mqtt/build.yaml @@ -0,0 +1,6 @@ +--- +build_from: + amd64: ghcr.io/hassio-addons/debian-base/amd64 + armhf: ghcr.io/hassio-addons/debian-base/armhf + armv7: ghcr.io/hassio-addons/debian-base/armv7 + aarch64: ghcr.io/hassio-addons/debian-base/aarch64 diff --git a/amridm2mqtt/config.yaml b/amridm2mqtt/config.yaml new file mode 100644 index 0000000..77227f5 --- /dev/null +++ b/amridm2mqtt/config.yaml @@ -0,0 +1,35 @@ +name: AMRIDM2MQTT +url: https://github.com/mdegat01/addon-amridm2mqtt +version: edge +slug: amridm2mqtt +arch: + - aarch64 + - amd64 + - armv7 + - armhf + - i386 +description: AMRIDM2MQTT for Home Assistant +uart: true +services: + - mqtt:want +map: + - ssl +options: + watched_meters: [] + wh_multiplier: 1000 + readings_per_hour: 12 +schema: + watched_meters: + - int + wh_multiplier: int(1,) + readings_per_hour: int(1,) + mqtt: + host: str? + ca: str? + cert: str? + key: str? + username: str? + password: password? + client_id: str? + base_topic: str? + log_level: list(trace|debug|info|notice|warning|error|fatal)? diff --git a/amridm2mqtt/rootfs/amridm2mqtt/amridm2mqtt.py b/amridm2mqtt/rootfs/amridm2mqtt/amridm2mqtt.py new file mode 100644 index 0000000..0b8d9d8 --- /dev/null +++ b/amridm2mqtt/rootfs/amridm2mqtt/amridm2mqtt.py @@ -0,0 +1,145 @@ +#!/usr/bin/env python3 +""" +Runs rtlamr to watch for IDM broadcasts from power meter. If meter id +is in the list, usage is sent to 'readings/{meter id}/meter_reading' +topic on the MQTT broker specified in settings. + +WATCHED_METERS = A Python list indicating those meter IDs to record and post. +MQTT_HOST = String containing the MQTT server address. +MQTT_PORT = An int containing the port the MQTT server is active on. + +""" +import logging +import subprocess +import signal +import sys +import time +import paho.mqtt.publish as publish +import settings + + +def shutdown(): + """Uses signal to shutdown and hard kill opened processes and self.""" + rtltcp.send_signal(15) + rtlamr.send_signal(15) + time.sleep(1) + rtltcp.send_signal(9) + rtlamr.send_signal(9) + sys.exit(0) + + +signal.signal(signal.SIGTERM, shutdown) +signal.signal(signal.SIGINT, shutdown) + +# stores last interval id to avoid duplication, includes getter and setter +last_reading = {} + +if len(settings.MQTT_USER) and len(settings.MQTT_PASSWORD): + AUTH = {"username": settings.MQTT_USER, "password": settings.MQTT_PASSWORD} +else: + AUTH = None + +logging.basicConfig() +logging.getLogger().setLevel(settings.LOG_LEVEL) + + +def get_last_interval(meter_id): # pylint: disable=redefined-outer-name + """Get last interval.""" + return last_reading.get(meter_id, (None)) + + +def set_last_interval(meter_id, interval_id): # pylint: disable=redefined-outer-name + """Set last interval.""" + last_reading[meter_id] = interval_id + + +def send_mqtt( + topic, + payload, +): + """Send data to MQTT broker defined in settings.""" + try: + publish.single( + topic, + payload=payload, + qos=1, + hostname=settings.MQTT_HOST, + port=settings.MQTT_PORT, + auth=AUTH, + tls=settings.MQTT_TLS, + ) + except Exception as ex: # pylint: disable=broad-except + logging.error("MQTT Publish Failed: %s", str(ex)) + + +# start the rtl_tcp program +rtltcp = subprocess.Popen( + [settings.RTL_TCP + " > /dev/null 2>&1 &"], + shell=True, + stdin=None, + stdout=None, + stderr=None, + close_fds=True, +) +time.sleep(5) + +# start the rtlamr program. +rtlamr_cmd = [settings.RTLAMR, "-msgtype=idm", "-format=csv"] +rtlamr = subprocess.Popen( + rtlamr_cmd, + stdout=subprocess.PIPE, + universal_newlines=True, +) + +while True: + try: + amrline = rtlamr.stdout.readline().strip() + flds = amrline.split(",") + + if len(flds) != 66: + # proper IDM results have 66 fields + continue + + # make sure the meter id is one we want + meter_id = int(flds[9]) + if settings.WATCHED_METERS and meter_id not in settings.WATCHED_METERS: + continue + + # get some required info: current meter reading, + # current interval id, most recent interval usage + read_cur = int(flds[15]) + interval_cur = int(flds[10]) + idm_read_cur = int(flds[16]) + + # retreive the interval id of the last time we sent to MQTT + interval_last = get_last_interval(meter_id) + + if interval_cur != interval_last: + + # as observed on on my meter... + # using values set in settings... + # each idm interval is 5 minutes (12x per hour), + # measured in hundredths of a kilowatt hour + # take the last interval usage times 10 to get watt-hours, + # then times 12 to get average usage in watts + rate = idm_read_cur * settings.WH_MULTIPLIER * settings.READINGS_PER_HOUR + + current_reading_in_kwh = (read_cur * settings.WH_MULTIPLIER) / 1000 + + logging.debug( + "Sending meter %s reading: %s", meter_id, current_reading_in_kwh + ) + send_mqtt( + f"readings/${meter_id}/meter_reading", + str(current_reading_in_kwh), + ) + + logging.debug("Sending meter %s rate: %s", meter_id, rate) + send_mqtt(f"readings/${meter_id}/meter_rate", str(rate)) + + # store interval ID to avoid duplicating data + set_last_interval(meter_id, interval_cur) + + except Exception as ex: # pylint: disable=broad-except + logging.debug("Exception squashed! %s: %s", ex.__class__.__name__, ex) + time.sleep(2) diff --git a/amridm2mqtt/rootfs/amridm2mqtt/settings.py b/amridm2mqtt/rootfs/amridm2mqtt/settings.py new file mode 100644 index 0000000..111cbfb --- /dev/null +++ b/amridm2mqtt/rootfs/amridm2mqtt/settings.py @@ -0,0 +1,69 @@ +"""Get our settings from os.environ to facilitate running in Docker. +""" +import os +import logging + +# List of the Meter IDs to watch +# Use empty brackets to read all meters - [] +# List may contain only one entry - [12345678] +# or multiple entries - [12345678, 98765432, 12340123] +power_meters = os.environ['WATCHED_METERS'].replace(',', ' ').split(' ') +WATCHED_METERS = [int(meter_id) for meter_id in power_meters] + +# multiplier to get reading to Watt Hours (Wh) +# examples: +# for meter providing readings in kWh +# MULTIPLIER = 1000 +# for meter providing readings in kWh +# with 2 extra digits of precision +# MULTIPLIER = 10 +# MULTIPLIER needs to be a number +WH_MULTIPLIER = int(os.environ.get('WH_MULTIPLIER', 1000)) + +# number of IDM intervals per hour reported by the meter +# examples: +# for meter providing readings every 5 minutes +# or 12 times every hour +# READINGS_PER_HOUR = 12 +# for meter providing readings every 15 minutes +# or 12 times every hour +# READINGS_PER_HOUR = 4 +READINGS_PER_HOUR = int(os.environ.get('READINGS_PER_HOUR', 12)) + +# MQTT Server settings +# MQTT_HOST needs to be a string +# MQTT_PORT needs to be an int +# MQTT_USER needs to be a string +# MQTT_PASSWORD needs to be a string +# If no authentication, leave MQTT_USER and MQTT_PASSWORD empty +MQTT_HOST = os.environ.get('MQTT_HOST', '127.0.0.1') +MQTT_PORT = int(os.environ.get('MQTT_PORT', 1883)) +MQTT_USER = os.environ.get('MQTT_USER', '') +MQTT_PASSWORD = os.environ.get('MQTT_PASSWORD', '') + +# If TLS in use, a CA file must be provided +# Client Key+Cert can optionally be provided as well +if os.environ.get('MQTT_CA'): + MQTT_TLS = { + 'ca_certs': os.environ.get('MQTT_CA'), + 'certfile': os.environ.get('MQTT_CERT'), + 'keyfile': os.environ.get('MQTT_KEY') + } +else: + MQTT_TLS = None + +# Set logging level +EV_TO_LOG_LEVEL = { + 'DEBUG': logging.DEBUG, + 'INFO': logging.INFO, + 'WARNING': logging.WARNING, + 'ERROR': logging.ERROR, + 'CRITICAL': logging.CRITICAL +} +LOG_LEVEL = EV_TO_LOG_LEVEL.get(os.environ.get('LOG_LEVEL')) + +# path to rtlamr +RTLAMR = '/root/go/bin/rtlamr' + +# path to rtl_tcp +RTL_TCP = '/usr/bin/rtl_tcp' diff --git a/amridm2mqtt/rootfs/etc/cont-init.d/30-config.sh b/amridm2mqtt/rootfs/etc/cont-init.d/30-config.sh new file mode 100644 index 0000000..439a7c6 --- /dev/null +++ b/amridm2mqtt/rootfs/etc/cont-init.d/30-config.sh @@ -0,0 +1,48 @@ +#!/usr/bin/with-contenv bashio +# shellcheck shell=bash +# ============================================================================== +# Home Assistant Add-on: AMRIDM2MQTT +# This validates config and sets up app files/folders +# ============================================================================== + +# -- CONFIG SUGGESTIONS/VALIDATIONS --- +bashio::log.debug "Validate config and look for suggestions" + +if bashio::config.exists 'mqtt.ca'; then + if ! bashio::fs.file_exists "/ssl/$(bashio::config 'mqtt.ca')"; then + bashio::log.fatal + bashio::log.fatal "The file specified for 'mqtt.ca' does not exist!" + bashio::log.fatal "Ensure CA file exists in /ssl and correct path is provided." + bashio::log.fatal + bashio::exit.nok + fi +else + if bashio::config.exists 'mqtt.cert'; then + bashio::log.fatal + bashio::log.fatal "Client certificate provided but TLS isn't in use!" + bashio::log.fatal "Please provide a CA certificate to enable TLS connection to your broker." + bashio::log.fatal + bashio::exit.nok + fi +fi +if bashio::config.exists 'mqtt.cert'; then + if ! bashio::fs.file_exists "/ssl/$(bashio::config 'mqtt.cert')"; then + bashio::log.fatal + bashio::log.fatal "The file specified for 'mqtt.cert' does not exist!" + bashio::log.fatal "Ensure client certificate file exists in /ssl and correct path is provided." + bashio::log.fatal + bashio::exit.nok + fi + if ! bashio::fs.file_exists "/ssl/$(bashio::config 'mqtt.key')"; then + bashio::log.fatal + bashio::log.fatal "The file specified for 'mqtt.key' does not exist!" + bashio::log.fatal "Ensure client certificate key file exists in /ssl and correct path is provided." + bashio::log.fatal + bashio::exit.nok + fi +else + if bashio::config.exists 'mqtt.key'; then + bashio::log.warning "Invalid option: 'mqtt.key' set without 'mqtt.cert'. Removing..." + bashio::addon.option 'mqtt.key' + fi +fi \ No newline at end of file diff --git a/amridm2mqtt/rootfs/etc/fix-attrs.d/permissions b/amridm2mqtt/rootfs/etc/fix-attrs.d/permissions new file mode 100644 index 0000000..df8054a --- /dev/null +++ b/amridm2mqtt/rootfs/etc/fix-attrs.d/permissions @@ -0,0 +1,2 @@ +/data/amridm2mqtt true abc 0755 0755 +/amridm2mqtt true abc 0555 0555 \ No newline at end of file diff --git a/amridm2mqtt/rootfs/etc/services.d/amridm2mqtt/finish b/amridm2mqtt/rootfs/etc/services.d/amridm2mqtt/finish new file mode 100755 index 0000000..93e4ee0 --- /dev/null +++ b/amridm2mqtt/rootfs/etc/services.d/amridm2mqtt/finish @@ -0,0 +1,9 @@ +#!/usr/bin/execlineb -S0 +# ============================================================================== +# Home Assistant Add-on: AMRIDM2MQTT +# Take down the S6 supervision tree when AMRIDM2MQTT fails +# ============================================================================== +if { s6-test ${1} -ne 0 } +if { s6-test ${1} -ne 256 } + +s6-svscanctl -t /var/run/s6/service \ No newline at end of file diff --git a/amridm2mqtt/rootfs/etc/services.d/amridm2mqtt/run b/amridm2mqtt/rootfs/etc/services.d/amridm2mqtt/run new file mode 100644 index 0000000..16bebee --- /dev/null +++ b/amridm2mqtt/rootfs/etc/services.d/amridm2mqtt/run @@ -0,0 +1,66 @@ +#!/usr/bin/with-contenv bashio +# shellcheck shell=bash +# ============================================================================== +# Home Assistant Add-on: Hedgedoc +# Runs Hedgedoc +# ============================================================================== + +declare host +declare port +declare username +declare password + +# --- LOAD BASIC SETTINGS --- +bashio::log.debug "Loading basic settings..." +export "WH_MULTIPLIER=$(bashio::config 'wh_multiplier')" +export "READINGS_PER_HOUR=$(bashio::config 'readings_per_hour')" + +IFS="," +export "WATCHED_METERS=$(bashio::config 'watched_meters')" +unset IFS + +# --- LOAD MQTT SETTINGS --- +bashio::log.debug "Setting MQTT details..." +if ! bashio::config.is_empty 'mqtt.host'; then + host=$(bashio::config 'mqtt.host') + port=$(bashio::config 'mqtt.port') + username=$(bashio::config 'mqtt.username') + password=$(bashio::config 'mqtt.password') +else + host=$(bashio::services 'mqtt' 'host') + port=$(bashio::services 'mqtt' 'port') + username=$(bashio::services 'mqtt' 'username') + password=$(bashio::services 'mqtt' 'password') +fi + +export "MQTT_HOST=${host}" +export "MQTT_PORT=${port}" +export "MQTT_USERNAME=${username}" +export "MQTT_PASSWORD=${password}" + +if ! bashio::config.is_empty 'mqtt.ca'; then + export "MQTT_CA=$(bashio::config 'mqtt.ca')" + + if ! bashio::config.is_empty 'mqtt.cert'; then + export "MQTT_CERT=$(bashio::config 'mqtt.cert')" + export "MQTT_KEY=$(bashio::config 'mqtt.key')" + fi +fi + +# --- SET LOG LEVEL --- +case "$(bashio::config 'log_level')" in \ + trace) ;& \ + debug) log_level='DEBUG' ;; \ + notice) ;& \ + warning) log_level='WARNING' ;; \ + error) log_level='ERROR' ;; \ + fatal) log_level='CRITICAL' ;; \ + *) log_level='INFO' ;; \ +esac; +export CMD_LOGLEVEL="${log_level}" +bashio::log.info "Hedgedoc log level set to ${log_level}" + + +bashio::log.info 'Handing over control to AMRIDM2MQTT...' +exec s6-setuidgid abc \ + /usr/bin/python3 /amridm2mqtt/amridm2mqtt.py From 01193b19cc3b260495c33dcfc830ec04d4156bc3 Mon Sep 17 00:00:00 2001 From: Mike Degatano Date: Tue, 16 Nov 2021 10:50:23 -0500 Subject: [PATCH 2/7] Missing yaml document start --- amridm2mqtt/config.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/amridm2mqtt/config.yaml b/amridm2mqtt/config.yaml index 77227f5..49b7ee3 100644 --- a/amridm2mqtt/config.yaml +++ b/amridm2mqtt/config.yaml @@ -1,3 +1,4 @@ +--- name: AMRIDM2MQTT url: https://github.com/mdegat01/addon-amridm2mqtt version: edge From e9c8548f0542eb7f81d42309efc89fca7081ce86 Mon Sep 17 00:00:00 2001 From: Mike Degatano Date: Tue, 16 Nov 2021 10:55:05 -0500 Subject: [PATCH 3/7] No i386 support --- amridm2mqtt/config.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/amridm2mqtt/config.yaml b/amridm2mqtt/config.yaml index 49b7ee3..8d9411c 100644 --- a/amridm2mqtt/config.yaml +++ b/amridm2mqtt/config.yaml @@ -8,7 +8,6 @@ arch: - amd64 - armv7 - armhf - - i386 description: AMRIDM2MQTT for Home Assistant uart: true services: From 8ef90f55b0e55778f7410fd10051fb3fa1d03960 Mon Sep 17 00:00:00 2001 From: Mike Degatano Date: Tue, 16 Nov 2021 11:07:39 -0500 Subject: [PATCH 4/7] yq not jq since config is yaml now --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 34e4049..cd130dd 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -145,7 +145,7 @@ jobs: id: flags run: | echo "::set-output name=date::$(date +"%Y-%m-%dT%H:%M:%SZ")" - from=$(jq --raw-output ".build_from.${{ matrix.architecture }}" "${{ needs.information.outputs.build }}") + from=$(yq --no-colors eval ".build_from.${{ matrix.architecture }}" "${{ needs.information.outputs.build }}") echo "::set-output name=from::${from}" if [[ "${{ matrix.architecture}}" = "amd64" ]]; then echo "::set-output name=platform::linux/amd64" From 5dd5a0f7049053aafa4c587e5c6d51606d797a5c Mon Sep 17 00:00:00 2001 From: Mike Degatano Date: Tue, 16 Nov 2021 11:14:12 -0500 Subject: [PATCH 5/7] rtlamr binary is already correct name --- amridm2mqtt/Dockerfile | 1 - 1 file changed, 1 deletion(-) diff --git a/amridm2mqtt/Dockerfile b/amridm2mqtt/Dockerfile index cc2c1df..47b66c2 100644 --- a/amridm2mqtt/Dockerfile +++ b/amridm2mqtt/Dockerfile @@ -22,7 +22,6 @@ RUN set -eux; \ curl -J -L -o /tmp/rtlamr.tar.gz \ "https://github.com/bemasher/rtlamr/releases/download/v${RTLAMR_VERSION}/rtlamr_linux_${BINARCH}.tar.gz"; \ tar -xf /tmp/rtlamr.tar.gz -C /usr/bin; \ - mv /usr/bin/rtlamr_linux_${BINARCH} /usr/bin/rtlamr; \ chmod a+x /usr/bin/rtlamr; \ rm /tmp/rtlamr.tar.gz; From ad75dff68de1b4ae97cadce0e4141e906ff4752d Mon Sep 17 00:00:00 2001 From: Mike Degatano Date: Tue, 16 Nov 2021 11:33:46 -0500 Subject: [PATCH 6/7] Add user correctly for debian --- amridm2mqtt/Dockerfile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/amridm2mqtt/Dockerfile b/amridm2mqtt/Dockerfile index 47b66c2..4ce7451 100644 --- a/amridm2mqtt/Dockerfile +++ b/amridm2mqtt/Dockerfile @@ -41,8 +41,7 @@ RUN set -eux; \ \ echo "Add user for AMRIDM2MQTT"; \ mkdir -p /data/amridm2mqtt; \ - addgroup -S abc; \ - adduser -u 12345 -h /data/amridm2mqtt/ -D -S abc -G abc; + useradd -u 12345 -U -d /data/amridm2mqtt abc; # Add rtlamr COPY --from=build_rtlamr /usr/bin/rtlamr /usr/bin/rtlamr From 95bbb6865fe0482fd97799ebfddc5e143363c200 Mon Sep 17 00:00:00 2001 From: Mike Degatano Date: Tue, 16 Nov 2021 12:45:47 -0500 Subject: [PATCH 7/7] Initial docs and support new settings --- README.md | 102 +++++++++++ amridm2mqtt/.README.j2 | 149 +++++++++++++++ amridm2mqtt/DOCS.md | 170 ++++++++++++++++++ amridm2mqtt/rootfs/amridm2mqtt/amridm2mqtt.py | 5 +- amridm2mqtt/rootfs/amridm2mqtt/settings.py | 47 +++-- .../rootfs/etc/services.d/amridm2mqtt/run | 7 + 6 files changed, 459 insertions(+), 21 deletions(-) create mode 100644 README.md create mode 100644 amridm2mqtt/.README.j2 create mode 100644 amridm2mqtt/DOCS.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..101526f --- /dev/null +++ b/README.md @@ -0,0 +1,102 @@ +# Home Assistant Add-on: AMRIDM2MQTT + +[![GitHub Release][releases-shield]][releases] +![Project Stage][project-stage-shield] +[![License][license-shield]](LICENSE) + +![Supports aarch64 Architecture][aarch64-shield] +![Supports amd64 Architecture][amd64-shield] +![Supports armhf Architecture][armhf-shield] +![Supports armv7 Architecture][armv7-shield] +![Supports i386 Architecture][i386-shield] + +[![Github Actions][github-actions-shield]][github-actions] +![Project Maintenance][maintenance-shield] +[![GitHub Activity][commits-shield]][commits] +[![Community Forum][forum-shield]][forum] + +_Runs rtlamr to read IDM power meter data and send to MQTT broker._ + +[![Open your Home Assistant instance and show the add add-on repository dialog +with a specific repository URL pre-filled.][add-repo-shield]][add-repo] +[![Open your Home Assistant instance and show the dashboard of a Supervisor add-on.][add-addon-shield]][add-addon] + +## About + +Port of ragingcomputer's [amridm2mqtt][amridm2mqtt] to a Home Assistant add-on +in order to make it easier to install and use in supervised and HAOS setups. Allows +you to use an rtl-sdr dongle to listen for signals from compatible smart meters. + +## Support + +Got questions? + +You have several ways to get them answered: + +- The Home Assistant [Community Forum][forum]. I am + [CentralCommand][forum-centralcommand] there. +- The Home Assistant [Discord Chat Server][discord-ha]. Use the #add-ons channel, + I am CentralCommand#0913 there. + +You could also [open an issue here][issue] on GitHub. + +## Authors & contributors + +The original setup of this repository is by [Mike Degatano][mdegat01]. + +The amridm2mqtt service ported here was created by [ragingcomputer][ragingcomputer]. + +For a full list of all authors and contributors, +check [the contributor's page][contributors]. + +## License + +MIT License + +Copyright (c) 2021 mdegat01 + +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-shield]: https://img.shields.io/badge/aarch64-yes-green.svg +[add-addon-shield]: https://my.home-assistant.io/badges/supervisor_addon.svg +[add-addon]: https://my.home-assistant.io/redirect/supervisor_addon/?addon=39bd2704_amridm2mqtt +[add-repo-shield]: https://my.home-assistant.io/badges/supervisor_add_addon_repository.svg +[add-repo]: https://my.home-assistant.io/redirect/supervisor_add_addon_repository/?repository_url=https%3A%2F%2Fgithub.com%2Fmdegat01%2Fhassio-addons +[amd64-shield]: https://img.shields.io/badge/amd64-yes-green.svg +[amridm2mqtt]: https://github.com/ragingcomputer/amridm2mqtt +[armhf-shield]: https://img.shields.io/badge/armhf-yes-green.svg +[armv7-shield]: https://img.shields.io/badge/armv7-yes-green.svg +[commits-shield]: https://img.shields.io/github/commit-activity/y/mdegat01/addon-amridm2mqtt.svg +[commits]: https://github.com/mdegat01/addon-amridm2mqtt/commits/main +[contributors]: https://github.com/mdegat01/addon-amridm2mqtt/graphs/contributors +[discord-ha]: https://discord.gg/c5DvZ4e +[forum-centralcommand]: https://community.home-assistant.io/u/CentralCommand/?u=CentralCommand +[forum-shield]: https://img.shields.io/badge/community-forum-brightgreen.svg +[forum]: https://community.home-assistant.io +[mdegat01]: https://github.com/mdegat01 +[github-actions-shield]: https://github.com/mdegat01/addon-amridm2mqtt/workflows/CI/badge.svg +[github-actions]: https://github.com/mdegat01/addon-amridm2mqtt/actions +[i386-shield]: https://img.shields.io/badge/i386-no-red.svg +[issue]: https://github.com/mdegat01/addon-amridm2mqtt/issues +[license-shield]: https://img.shields.io/github/license/mdegat01/addon-amridm2mqtt.svg +[maintenance-shield]: https://img.shields.io/maintenance/yes/2021.svg +[project-stage-shield]: https://img.shields.io/badge/project%20stage-experimental-yellow.svg +[ragingcomputer]: https://github.com/ragingcomputer +[releases-shield]: https://img.shields.io/github/release/mdegat01/addon-amridm2mqtt.svg +[releases]: https://github.com/mdegat01/addon-amridm2mqtt/releases diff --git a/amridm2mqtt/.README.j2 b/amridm2mqtt/.README.j2 new file mode 100644 index 0000000..b1a9896 --- /dev/null +++ b/amridm2mqtt/.README.j2 @@ -0,0 +1,149 @@ +# Home Assistant Add-on: AMRIDM2MQTT + +[![GitHub Release][releases-shield]][releases] +![Project Stage][project-stage-shield] +[![License][license-shield]](LICENSE) + +![Supports aarch64 Architecture][aarch64-shield] +![Supports amd64 Architecture][amd64-shield] +![Supports armhf Architecture][armhf-shield] +![Supports armv7 Architecture][armv7-shield] +![Supports i386 Architecture][i386-shield] + +[![Github Actions][github-actions-shield]][github-actions] +![Project Maintenance][maintenance-shield] +[![GitHub Activity][commits-shield]][commits] +[![Community Forum][forum-shield]][forum] + +_Runs rtlamr to read IDM power meter data and send to MQTT broker._ + +{% set repository = namespace(url='https%3A//github.com/mdegat01/hassio-addons', slug='39bd2704') %} +{% if channel == "edge" %} +{% set repository.url = repository.url + '-edge' %} +{% set repository.slug = '7eb274d5' %} +## WARNING! THIS IS AN EDGE REPOSITORY + +This Add-ons repository contains edge builds of add-ons. Edge +builds of add-ons are based upon the latest development version. + +- They may not work at all. +- They might stop working at any time. +- They could have a negative impact on your system. + +This repository was created for: + +- Anybody willing to test. +- Anybody interested in trying out upcoming add-ons or add-on features. +- Developers. + +If you are more interested in stable releases of these add-ons: + + + +{% elif channel == "beta" %} +{% set repository.url = repository.url + '-beta' %} +{% set repository.slug = 'e9a81774' %} +## WARNING! THIS IS A BETA REPOSITORY + +This Add-ons repository contains beta builds of add-ons. Beta +builds of add-ons are based upon the latest release including pre-releases. + +- They might stop working at any time. +- They could have a negative impact on your system. + +This repository was created for: + +- Anybody willing to test. +- Anybody interested in trying out upcoming add-ons or add-on features. + +If you are more interested in stable releases of these add-ons: + + + +{% endif %} +## About + +Port of ragingcomputer's [amridm2mqtt][amridm2mqtt] to a Home Assistant add-on +in order to make it easier to install and use in supervised and HAOS setups. Allows +you to use an rtl-sdr dongle to listen for signals from compatible smart meters. + +## Support + +Got questions? + +You have several ways to get them answered: + +- The Home Assistant [Community Forum][forum]. I am + [CentralCommand][forum-centralcommand] there. +- The Home Assistant [Discord Chat Server][discord-ha]. Use the #add-ons channel, + I am CentralCommand#0913 there. + +You could also [open an issue here][issue] on GitHub. + +## Authors & contributors + +The original setup of this repository is by [Mike Degatano][mdegat01]. + +The amridm2mqtt service ported here was created by [ragingcomputer][ragingcomputer]. + +For a full list of all authors and contributors, +check [the contributor's page][contributors]. + +## License + +MIT License + +Copyright (c) 2021 mdegat01 + +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. + +{% if channel == "edge" %} +[project-stage-shield]: https://img.shields.io/badge/project%20stage-experimental-yellow.svg +{% elif channel == "beta" %} +[project-stage-shield]: https://img.shields.io/badge/project%20stage-beta-orange.svg +{% else %} +[project-stage-shield]: https://img.shields.io/badge/project%20stage-production%20ready-brightgreen.svg +{% endif %} +[aarch64-shield]: https://img.shields.io/badge/aarch64-yes-green.svg +[add-addon-shield]: https://my.home-assistant.io/badges/supervisor_addon.svg +[add-addon]: https://my.home-assistant.io/redirect/supervisor_addon/?addon=39bd2704_amridm2mqtt +[add-repo-shield]: https://my.home-assistant.io/badges/supervisor_add_addon_repository.svg +[add-repo]: https://my.home-assistant.io/redirect/supervisor_add_addon_repository/?repository_url=https%3A%2F%2Fgithub.com%2Fmdegat01%2Fhassio-addons +[amd64-shield]: https://img.shields.io/badge/amd64-yes-green.svg +[amridm2mqtt]: https://github.com/ragingcomputer/amridm2mqtt +[armhf-shield]: https://img.shields.io/badge/armhf-yes-green.svg +[armv7-shield]: https://img.shields.io/badge/armv7-yes-green.svg +[commits-shield]: https://img.shields.io/github/commit-activity/y/mdegat01/addon-amridm2mqtt.svg +[commits]: https://github.com/mdegat01/addon-amridm2mqtt/commits/main +[contributors]: https://github.com/mdegat01/addon-amridm2mqtt/graphs/contributors +[discord-ha]: https://discord.gg/c5DvZ4e +[forum-centralcommand]: https://community.home-assistant.io/u/CentralCommand/?u=CentralCommand +[forum-shield]: https://img.shields.io/badge/community-forum-brightgreen.svg +[forum]: https://community.home-assistant.io +[mdegat01]: https://github.com/mdegat01 +[github-actions-shield]: https://github.com/mdegat01/addon-amridm2mqtt/workflows/CI/badge.svg +[github-actions]: https://github.com/mdegat01/addon-amridm2mqtt/actions +[i386-shield]: https://img.shields.io/badge/i386-no-red.svg +[issue]: https://github.com/mdegat01/addon-amridm2mqtt/issues +[license-shield]: https://img.shields.io/github/license/mdegat01/addon-amridm2mqtt.svg +[maintenance-shield]: https://img.shields.io/maintenance/yes/2021.svg +[project-stage-shield]: https://img.shields.io/badge/project%20stage-experimental-yellow.svg +[ragingcomputer]: https://github.com/ragingcomputer +[releases-shield]: https://img.shields.io/github/release/mdegat01/addon-amridm2mqtt.svg +[releases]: https://github.com/mdegat01/addon-amridm2mqtt/releases diff --git a/amridm2mqtt/DOCS.md b/amridm2mqtt/DOCS.md new file mode 100644 index 0000000..fb5a248 --- /dev/null +++ b/amridm2mqtt/DOCS.md @@ -0,0 +1,170 @@ +# Home Assistant Add-on: HedgeDoc + +## Install + +First add the repository to the add-on store (`https://github.com/mdegat01/hassio-addons`): + +[![Open your Home Assistant instance and show the add add-on repository dialog +with a specific repository URL pre-filled.][add-repo-shield]][add-repo] + +Then find HedgeDoc in the store and click install: + +[![Open your Home Assistant instance and show the dashboard of a Supervisor add-on.][add-addon-shield]][add-addon] + +## MQTT Setup + +This addon requires an MQTT broker. It will send the metrics it collects there +and expect clients like Home Assistant to read from that. By default it will use +the broker set up by the [Mosquitto addon][addon-mosquitto]. That add-on must be +installed and running prior to start-up. You generally don't need any additional +configuration for this, as long as that add-on is running this one will find and +use it. + +[![Open your Home Assistant instance and show the dashboard of a Supervisor add-on.][add-addon-shield]][add-addon-mosquitto] + +If you prefer to use your own MQTT broker, you must fill in the `mqtt` options +below. Please note there is no easy upgrade path between these two options. + +This addon will publish messages to the topics `readings/{meter_id}/meter_reading` +and `readings/{meter_id}/meter_rate`. You can customize this by setting `mqtt.base_topic` +below. + +## RTL-SDR Dongle Setup + +TODO + +## Configuration + +Example add-on configuration: + +``` +TODO +``` + +**Note**: _This is just an example, don't copy and paste it! Create your own!_ + +### Option: `watched_meters` + +TODO + +### Option: `wh_multiplier` + +TODO + +### Option: `readings_per_hour` + +TODO + +### Option: `mqtt.host` + +TODO + +### Option: `mqtt.ca` + +TODO + +**Note**: _The file MUST be stored in `/ssl/`_ + +### Option: `mqtt.cert` + +TODO + +**Note**: _The file MUST be stored in `/ssl/`_ + +### Option: `mqtt.key` + +TODO + +**Note**: _The file MUST be stored in `/ssl/`_ + +### Option: `mqtt.username` + +TODO + +### Option: `mqtt.password` + +TODO + +### Option: `mqtt.client_id` + +TODO + +### Option: `mqtt.base_topic` + +TODO + +## Changelog & Releases + +This repository keeps a change log using [GitHub's releases][releases] +functionality. + +Releases are based on [Semantic Versioning][semver], and use the format +of `MAJOR.MINOR.PATCH`. In a nutshell, the version will be incremented +based on the following: + +- `MAJOR`: Incompatible or major changes. +- `MINOR`: Backwards-compatible new features and enhancements. +- `PATCH`: Backwards-compatible bugfixes and package updates. + +## Support + +Got questions? + +You have several ways to get them answered: + +- The Home Assistant [Community Forum][forum]. I am + [CentralCommand][forum-centralcommand] there. +- The Home Assistant [Discord Chat Server][discord-ha]. Use the #add-ons channel, + I am CentralCommand#0913 there. + +You could also [open an issue here][issue] on GitHub. + +## Authors & contributors + +The original setup of this repository is by [Mike Degatano][mdegat01]. + +The amridm2mqtt service ported here was created by [ragingcomputer][ragingcomputer]. + +For a full list of all authors and contributors, +check [the contributor's page][contributors]. + +## License + +MIT License + +Copyright (c) 2021 mdegat01 + +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. + +[add-addon-shield]: https://my.home-assistant.io/badges/supervisor_addon.svg +[add-addon]: https://my.home-assistant.io/redirect/supervisor_addon/?addon=39bd2704_amridm2mqtt +[add-addon-mosquitto]: https://my.home-assistant.io/redirect/supervisor_addon/?addon=core_mosquitto +[add-repo-shield]: https://my.home-assistant.io/badges/supervisor_add_addon_repository.svg +[add-repo]: https://my.home-assistant.io/redirect/supervisor_add_addon_repository/?repository_url=https%3A%2F%2Fgithub.com%2Fmdegat01%2Fhassio-addons +[addon-mosquitto]: https://github.com/home-assistant/addons/tree/master/mosquitto +[amridm2mqtt]: https://github.com/ragingcomputer/amridm2mqtt +[contributors]: https://github.com/mdegat01/addon-hedgedoc/graphs/contributors +[discord-ha]: https://discord.gg/c5DvZ4e +[forum-centralcommand]: https://community.home-assistant.io/u/CentralCommand/?u=CentralCommand +[forum]: https://community.home-assistant.io/t/home-assistant-add-on-hedgedoc/296809?u=CentralCommand +[issue]: https://github.com/mdegat01/addon-hedgedoc/issues +[mdegat01]: https://github.com/mdegat01 +[ragingcomputer]: https://github.com/ragingcomputer +[releases]: https://github.com/mdegat01/addon-hedgedoc/releases +[semver]: http://semver.org/spec/v2.0.0 diff --git a/amridm2mqtt/rootfs/amridm2mqtt/amridm2mqtt.py b/amridm2mqtt/rootfs/amridm2mqtt/amridm2mqtt.py index 0b8d9d8..a90827e 100644 --- a/amridm2mqtt/rootfs/amridm2mqtt/amridm2mqtt.py +++ b/amridm2mqtt/rootfs/amridm2mqtt/amridm2mqtt.py @@ -67,6 +67,7 @@ def send_mqtt( port=settings.MQTT_PORT, auth=AUTH, tls=settings.MQTT_TLS, + client_id=settings.MQTT_CLIENT_ID, ) except Exception as ex: # pylint: disable=broad-except logging.error("MQTT Publish Failed: %s", str(ex)) @@ -130,12 +131,12 @@ def send_mqtt( "Sending meter %s reading: %s", meter_id, current_reading_in_kwh ) send_mqtt( - f"readings/${meter_id}/meter_reading", + f"${settings.MQTT_BASE_TOPIC}/${meter_id}/meter_reading", str(current_reading_in_kwh), ) logging.debug("Sending meter %s rate: %s", meter_id, rate) - send_mqtt(f"readings/${meter_id}/meter_rate", str(rate)) + send_mqtt(f"${settings.MQTT_BASE_TOPIC}/${meter_id}/meter_rate", str(rate)) # store interval ID to avoid duplicating data set_last_interval(meter_id, interval_cur) diff --git a/amridm2mqtt/rootfs/amridm2mqtt/settings.py b/amridm2mqtt/rootfs/amridm2mqtt/settings.py index 111cbfb..9991c79 100644 --- a/amridm2mqtt/rootfs/amridm2mqtt/settings.py +++ b/amridm2mqtt/rootfs/amridm2mqtt/settings.py @@ -7,7 +7,7 @@ # Use empty brackets to read all meters - [] # List may contain only one entry - [12345678] # or multiple entries - [12345678, 98765432, 12340123] -power_meters = os.environ['WATCHED_METERS'].replace(',', ' ').split(' ') +power_meters = os.environ["WATCHED_METERS"].replace(",", " ").split(" ") WATCHED_METERS = [int(meter_id) for meter_id in power_meters] # multiplier to get reading to Watt Hours (Wh) @@ -18,7 +18,7 @@ # with 2 extra digits of precision # MULTIPLIER = 10 # MULTIPLIER needs to be a number -WH_MULTIPLIER = int(os.environ.get('WH_MULTIPLIER', 1000)) +WH_MULTIPLIER = int(os.environ.get("WH_MULTIPLIER", 1000)) # number of IDM intervals per hour reported by the meter # examples: @@ -28,42 +28,51 @@ # for meter providing readings every 15 minutes # or 12 times every hour # READINGS_PER_HOUR = 4 -READINGS_PER_HOUR = int(os.environ.get('READINGS_PER_HOUR', 12)) +READINGS_PER_HOUR = int(os.environ.get("READINGS_PER_HOUR", 12)) # MQTT Server settings # MQTT_HOST needs to be a string # MQTT_PORT needs to be an int # MQTT_USER needs to be a string # MQTT_PASSWORD needs to be a string +# MQTT_CLIENT_ID is a string if provided, library randomly generates one if omitted # If no authentication, leave MQTT_USER and MQTT_PASSWORD empty -MQTT_HOST = os.environ.get('MQTT_HOST', '127.0.0.1') -MQTT_PORT = int(os.environ.get('MQTT_PORT', 1883)) -MQTT_USER = os.environ.get('MQTT_USER', '') -MQTT_PASSWORD = os.environ.get('MQTT_PASSWORD', '') +MQTT_HOST = os.environ.get("MQTT_HOST", "127.0.0.1") +MQTT_PORT = int(os.environ.get("MQTT_PORT", 1883)) +MQTT_USER = os.environ.get("MQTT_USER", "") +MQTT_PASSWORD = os.environ.get("MQTT_PASSWORD", "") +MQTT_CLIENT_ID = os.environ.get("MQTT_CLIENT_ID") # If TLS in use, a CA file must be provided # Client Key+Cert can optionally be provided as well -if os.environ.get('MQTT_CA'): +if os.environ.get("MQTT_CA"): MQTT_TLS = { - 'ca_certs': os.environ.get('MQTT_CA'), - 'certfile': os.environ.get('MQTT_CERT'), - 'keyfile': os.environ.get('MQTT_KEY') + "ca_certs": os.environ.get("MQTT_CA"), + "certfile": os.environ.get("MQTT_CERT"), + "keyfile": os.environ.get("MQTT_KEY"), } else: MQTT_TLS = None +# Set the MQTT base topic with the user's prefix if provided +MQTT_DEFAULT_BASE_TOPIC = "readings" +if os.environ.get("MQTT_BASE_TOPIC"): + MQTT_BASE_TOPIC = f'${os.environ.get("MQTT_BASE_TOPIC")}/${MQTT_DEFAULT_BASE_TOPIC}' +else: + MQTT_BASE_TOPIC = MQTT_DEFAULT_BASE_TOPIC + # Set logging level EV_TO_LOG_LEVEL = { - 'DEBUG': logging.DEBUG, - 'INFO': logging.INFO, - 'WARNING': logging.WARNING, - 'ERROR': logging.ERROR, - 'CRITICAL': logging.CRITICAL + "DEBUG": logging.DEBUG, + "INFO": logging.INFO, + "WARNING": logging.WARNING, + "ERROR": logging.ERROR, + "CRITICAL": logging.CRITICAL, } -LOG_LEVEL = EV_TO_LOG_LEVEL.get(os.environ.get('LOG_LEVEL')) +LOG_LEVEL = EV_TO_LOG_LEVEL.get(os.environ.get("LOG_LEVEL")) # path to rtlamr -RTLAMR = '/root/go/bin/rtlamr' +RTLAMR = "/root/go/bin/rtlamr" # path to rtl_tcp -RTL_TCP = '/usr/bin/rtl_tcp' +RTL_TCP = "/usr/bin/rtl_tcp" diff --git a/amridm2mqtt/rootfs/etc/services.d/amridm2mqtt/run b/amridm2mqtt/rootfs/etc/services.d/amridm2mqtt/run index 16bebee..abbebd1 100644 --- a/amridm2mqtt/rootfs/etc/services.d/amridm2mqtt/run +++ b/amridm2mqtt/rootfs/etc/services.d/amridm2mqtt/run @@ -38,6 +38,13 @@ export "MQTT_PORT=${port}" export "MQTT_USERNAME=${username}" export "MQTT_PASSWORD=${password}" +if bashio::config.exists 'mqtt.client_id'; then + export "MQTT_CLIENT_ID=$(bashio::config 'mqtt.client_id')" +fi +if bashio::config.exists 'mqtt.base_topic'; then + export "MQTT_BASE_TOPIC=$(bashio::config 'mqtt.base_topic')" +fi + if ! bashio::config.is_empty 'mqtt.ca'; then export "MQTT_CA=$(bashio::config 'mqtt.ca')"