diff --git a/.editorconfig b/.editorconfig new file mode 100755 index 0000000..7a12570 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,19 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true +ident_size = 4 + +[*.md] +ident_size = 2 +trim_trailing_whitespace = false + +[*.json] +ident_size = 2 + +[{.gitignore,.gitkeep,.editorconfig}] +ident_size = 2 diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..3fbad49 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,4 @@ +# Require maintainer's :+1: for changes to the .github/ repo-config files +# mainly due to https://github.com/probot/settings privilege escalation +.github/* @frenck +.gitlab-ci.yml @frenck diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100755 index 0000000..544da8f --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,20 @@ +# Problem/Motivation + +> (Why the issue was filed) + +## Expected behavior + +> (What you expected to happen) + +## Actual behavior + +> (What actually happened) + +## Steps to reproduce + +> (How can someone else make/see it happen) + +## Proposed changes + +> (If you have a proposed change, workaround or fix, +> describe the rationale behind it) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100755 index 0000000..cbd529a --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,9 @@ +# Proposed Changes + +> (Describe the changes and rationale behind them) + +## Related Issues + +> ([Github link][autolink-references] to related issues or pull requests) + +[autolink-references]: https://help.github.com/articles/autolinked-references-and-urls/ \ No newline at end of file diff --git a/.github/autolabeler.yml b/.github/autolabeler.yml new file mode 100644 index 0000000..3ce5703 --- /dev/null +++ b/.github/autolabeler.yml @@ -0,0 +1,2 @@ +--- +"Type: Documentation": ["*.md", "*.j2"] diff --git a/.github/config.yml b/.github/config.yml new file mode 100644 index 0000000..71d2f6d --- /dev/null +++ b/.github/config.yml @@ -0,0 +1,50 @@ +--- +# Configuration for request-info - https://github.com/behaviorbot/request-info + +# *OPTIONAL* Comment to reply with +# Can be either a string : +requestInfoReplyComment: + - "We would appreciate it if you could provide us with more info about this issue/pr!" + - "Hmmm... That issue/PR is kinda low on text. Could you please provide some more content?" + +# *OPTIONAL* default titles to check against for lack of descriptiveness +# MUST BE ALL LOWERCASE +requestInfoDefaultTitles: [] + +# *OPTIONAL* Label to be added to Issues and Pull Requests with insufficient information given +requestInfoLabelToAdd: "Incomplete" + +# *OPTIONAL* Require Pull Requests to contain more information than what is provided in the PR template +# Will fail if the pull request's body is equal to the provided template +checkPullRequestTemplate: true + +# *OPTIONAL* Only warn about insufficient information on these events type +# Keys must be lowercase. Valid values are 'issue' and 'pullRequest' +requestInfoOn: + pullRequest: true + issue: true + +# *OPTIONAL* Add a list of people whose Issues/PRs will not be commented on +# keys must be GitHub usernames +requestInfoUserstoExclude: [] + +# Configuration for new-issue-welcome - https://github.com/behaviorbot/new-issue-welcome + +# Comment to be posted to on first time issues +newIssueWelcomeComment: > + :wave: Thanks for opening your first issue here! + If you're reporting a :bug: bug, please make sure you include steps to reproduce it. + Also, logs, error messages and information about your hardware might be usefull. + +# Configuration for new-pr-welcome - https://github.com/behaviorbot/new-pr-welcome + +# Comment to be posted to on PRs from first time contributors in your repository +newPRWelcomeComment: > + :sparkling_heart: Thanks for opening this pull request! :sparkling_heart: + If your PR gets accepted and merged in, we will invite you to the project :tada: + +# Configuration for first-pr-merge - https://github.com/behaviorbot/first-pr-merge + +# Comment to be posted to on pull requests merged by a first time user +firstPRMergeComment: > + Congrats on merging your first pull request! :tada::tada::tada: diff --git a/.github/invite-contributors.yml b/.github/invite-contributors.yml new file mode 100644 index 0000000..25eb31a --- /dev/null +++ b/.github/invite-contributors.yml @@ -0,0 +1,10 @@ +--- +# If true, this will add new contributors as outside collaborators +# to the repo their PR was merged in. Team name is ignored if this +# flag is set to true. +isOutside: false + +# Specify team name to add new contributors to a specific team +# within your organization. +# Use team name or team-name-slug +team: Contributors diff --git a/.github/lock.yml b/.github/lock.yml new file mode 100644 index 0000000..4b1e816 --- /dev/null +++ b/.github/lock.yml @@ -0,0 +1,20 @@ +--- +# Configuration for lock-threads - https://github.com/dessant/lock-threads +# Number of days of inactivity before a closed issue or pull request is locked +daysUntilLock: 30 + +# Comment to post before locking. Set to `false` to disable +lockComment: > + This thread has been automatically locked because it has not had recent + activity. Please open a new issue for related bugs and link to relevant + comments in this thread. + +# Issues or pull requests with these labels will not be locked +# exemptLabels: +# - no-locking + +# Limit to only `issues` or `pulls` +# only: issues + +# Add a label when locking. Set to `false` to disable +lockLabel: false diff --git a/.github/move.yml b/.github/move.yml new file mode 100644 index 0000000..0c2276a --- /dev/null +++ b/.github/move.yml @@ -0,0 +1,20 @@ +--- +# Delete the command comment when it contains no other content +deleteCommand: true + +# Close the source issue after moving +closeSourceIssue: true + +# Lock the source issue after moving +lockSourceIssue: true + +# Mention issue and comment authors +mentionAuthors: true + +# Preserve mentions in the issue content +keepContentMentions: false + +# Set custom aliases for targets +# aliases: +# r: repo +# or: owner/repo diff --git a/.github/no-response.yml b/.github/no-response.yml new file mode 100644 index 0000000..bb9f0f2 --- /dev/null +++ b/.github/no-response.yml @@ -0,0 +1,13 @@ +--- +# Configuration for probot-no-response - https://github.com/probot/no-response +# Number of days of inactivity before an Issue is closed for lack of response +daysUntilClose: 14 +# Label requiring a response +responseRequiredLabel: "Status: Awaiting response" +# Comment to post when closing an Issue for lack of response. Set to `false` to disable +closeComment: > + This issue has been automatically closed because there has been no response + to our request for more information from the original author. With only the + information that is currently in the issue, we don't have enough information + to take action. Please reach out if you have or find the answers we need so + that we can investigate further. diff --git a/.github/potential-duplicates.yml b/.github/potential-duplicates.yml new file mode 100644 index 0000000..00c7c0f --- /dev/null +++ b/.github/potential-duplicates.yml @@ -0,0 +1,14 @@ +--- +# Label name and color to set, when potential duplicates are detected +issueLabel: "Potential duplicate" +labelColor: e6e6e6 + +# If similarity is higher than this threshold, issue will be marked as duplicate +threshold: 0.70 + +# Comment to post when potential duplicates are detected +referenceComment: > + Potential duplicates found: + {{#issues}} + - [#{{ number }}] {{ title }} ({{ accuracy }}%) + {{/issues}} diff --git a/.github/settings.yml b/.github/settings.yml new file mode 100644 index 0000000..669e2cd --- /dev/null +++ b/.github/settings.yml @@ -0,0 +1,150 @@ +--- +repository: + description: "Network UPS Tools - Community Hass.io Add-on for Home Assistant" + homepage: https://addons.community + topics: nut, network-ups-tools, ups, battery-backup, hassio-addons, hassio, hass, home-assistant, homeassistant, home-automation + private: false + has_issues: true + has_projects: false + has_wiki: false + has_downloads: false + default_branch: master + allow_squash_merge: true + allow_merge_commit: false + allow_rebase_merge: true +labels: + # Priority labels + - 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..." + + # Type labels + - name: "Type: Bug" + color: ee0701 + description: "Inconsistencies or issues which will cause a problem for users or implementors." + - name: "Type: Documentation" + color: 0052cc + description: "Solely about the documentation of the project." + - name: "Type: Enhancement" + color: 1d76db + description: "Enhancement of the code, not introducing new features." + - name: "Type: Feature" + color: 0e8a16 + description: "New features or options." + - name: "Type: Support" + color: 5319e7 + description: "Marks an issue as an support ticket." + - name: "Type: Discussion" + color: d4c5f9 + description: "Marks an issue as an generic discussion ticket." + - name: "Type: Maintaince" + color: 2af79e + description: "Generic maintaince tasks, e.g., package updates." + + # Additional markers + - name: "Security" + color: ee0701 + description: "Marks an security issues that needs to be resolved asap." + - name: "Idea" + color: fef2c0 + description: "Marks an idea, which might be excepted and implemented." + - name: "Incomplete" + color: fef2c0 + description: "Marks an PR or issue that is missing information." + - name: "Pull request" + color: fbca04 + description: "There is an PR opened for this issue." + - name: "Accepted" + color: c2e0c6 + description: "This issue or PR has been accepted." + - name: "Declined" + color: f9d0c4 + description: "This issue or PR has been declined." + - name: "Potential duplicate" + color: e6e6e6 + description: "This issue has been automatically marked as a potential duplicate." + + # Ongoing Status labels + - name: "Status: Triage" + color: fbca04 + description: "This issue needs to be triaged." + - name: "Status: On hold" + color: cccccc + description: "Issue or PR that has been placed on hold for now." + - name: "Status: In progress" + color: fbca04 + description: "Issue is currently being resolved by a developer." + - name: "Status: Stale" + color: fef2c0 + description: "There has not been activity on this issue or PR for quite some time." + - name: "Status: Awaiting response" + color: fef2c0 + description: "Issue or PR awaits response from the creator." + - name: "Status: Blocked" + color: fef2c0 + description: "Progress on this issue is currently not possible." + + # Closing status labels + - name: "Closed: Known limitation" + color: e6e6e6 + description: "Issue is closed, it is a known limitation." + - name: "Closed: Expected behavior" + color: e6e6e6 + description: "Issues is closed, it is expected behavior." + - name: "Closed: Duplicate" + color: e6e6e6 + description: "Issue is closed, duplicate of an existing issue." + - name: "Closed: Invalid" + color: e6e6e6 + description: "Issue is closed, marked as not a valid issue (e.g., an user error)." + - name: "Closed: Wrong repository" + color: e6e6e6 + description: "Issue is closed, was created in the wrong repository." + - name: "Closed: Won't Fix" + color: e6e6e6 + description: "Issue is closed, it won't be fixed." + - name: "Closed: Done" + color: c2e0c6 + description: "Issue closed, work on this issue has been marked complete." + + # Others + - 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: "Hacktoberfest" + description: "Issues/PRs are participating in the Hacktoberfest" + color: fbca04 + +branches: + - name: master + protection: + required_pull_request_reviews: + # required_approving_review_count: 1 + dismiss_stale_reviews: true + require_code_owner_reviews: true + dismissal_restrictions: + users: [] + teams: + - Admins + - Masters + required_status_checks: + strict: false + contexts: [] + enforce_admins: false + restrictions: + users: [] + teams: + - Admins + - Masters diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 0000000..36938bb --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,61 @@ +--- +# Configuration for probot-stale - https://github.com/probot/stale + +# Number of days of inactivity before an Issue or Pull Request becomes stale +daysUntilStale: 60 + +# Number of days of inactivity before a stale Issue or Pull Request is closed. +# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. +daysUntilClose: 7 + +# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable +exemptLabels: + - "Status: On hold" + - "Status: In progress" + - "Status: Awaiting response" + - "Status: Blocked" + - "Idea" + - "Security" + +# Set to true to ignore issues in a project (defaults to false) +exemptProjects: false + +# Set to true to ignore issues in a milestone (defaults to false) +exemptMilestones: false + +# Label to use when marking as stale +staleLabel: "Status: Stale" + +# Comment to post when marking as stale. Set to `false` to disable +markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. + +# Comment to post when removing the stale label. +# unmarkComment: > +# Your comment here. +unmarkComment: false + +# Comment to post when closing a stale Issue or Pull Request. +# closeComment: > +# Your comment here. +closeComment: false + +# Limit the number of actions per hour, from 1-30. Default is 30 +limitPerRun: 30 + +# Limit to only `issues` or `pulls` +only: issues + +# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls': +# pulls: +# daysUntilStale: 30 +# markComment: > +# This pull request has been automatically marked as stale because it has not had +# recent activity. It will be closed if no further activity occurs. Thank you +# for your contributions. + +# issues: +# exemptLabels: +# - confirmed diff --git a/.github/support.yml b/.github/support.yml new file mode 100644 index 0000000..8587bbe --- /dev/null +++ b/.github/support.yml @@ -0,0 +1,22 @@ +--- +--- +# Configuration for support-requests - https://github.com/dessant/support-requests + +# Label used to mark issues as support requests +supportLabel: "Type: Support" + +# Comment to post on issues marked as support requests. Add a link +# to a support page, or set to `false` to disable +supportComment: > + :wave: We use the issue tracker exclusively for bug reports and feature requests. + However, this issue appears to be a support request. Please use our + support channels to get help with the project. + + Head over to the [Home Assistant community forum](https://community.home-assistant.io/) + or join our [Discord](https://discord.me/hassioaddons) chat. + +# Close issues marked as support requests +close: true + +# Lock issues marked as support requests +lock: false diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..0c97a4b --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,428 @@ +--- +image: docker:latest + +variables: + ADDON_GITHUB_REPO: hassio-addons/addon-lovelace-migration + ADDON_SLUG: lovelace-migration + ADDON_TARGET: lovelace-migration + DOCKER_DRIVER: overlay2 + DOCKER_HUB_ORG: hassioaddons + +stages: + - preflight + - build + - scan + - deploy + - manifest + - publish + +# Generic DIND template +.dind: &dind + before_script: + - docker info + services: + - name: docker:dind + command: ["--experimental"] + +# Generic preflight template +.preflight: &preflight + stage: preflight + tags: + - preflight + +# Generic build template +.build: &build + <<: *dind + stage: build + before_script: + - docker info + - | + if [ "$(apk --print-arch)" = "amd64" ]; then + docker run --rm --privileged hassioaddons/qemu-user-static:latest + fi + - | + echo "${CI_JOB_TOKEN}" | docker login \ + --username gitlab-ci-token \ + --password-stdin \ + registry.gitlab.com + - docker pull "registry.gitlab.com/${CI_PROJECT_PATH}/${ADDON_ARCH}:cache" || true + script: + - | + docker build \ + --build-arg "BUILD_FROM=${FROM}" \ + --build-arg "BUILD_DATE=$(date +"%Y-%m-%dT%H:%M:%SZ")" \ + --build-arg "BUILD_ARCH=${ADDON_ARCH}" \ + --build-arg "BUILD_REF=${CI_COMMIT_SHA}" \ + --build-arg "BUILD_VERSION=${CI_COMMIT_TAG:-${CI_COMMIT_SHA:0:7}}" \ + --cache-from "registry.gitlab.com/${CI_PROJECT_PATH}/${ADDON_ARCH}:cache" \ + --tag \ + "registry.gitlab.com/${CI_PROJECT_PATH}/${ADDON_ARCH}:${CI_COMMIT_SHA}" \ + "${ADDON_TARGET}" + - | + docker push \ + "registry.gitlab.com/${CI_PROJECT_PATH}/${ADDON_ARCH}:${CI_COMMIT_SHA}" +# Generic scan template +.scan: &scan + <<: *dind + stage: scan + allow_failure: true + before_script: + - docker info + - docker run -d --name db arminc/clair-db:latest + - docker run -p 6060:6060 --link db:postgres -d --name clair arminc/clair-local-scan:v2.0.1 + - apk add -U curl ca-certificates + - | + curl \ + --silent \ + --show-error \ + --location \ + --fail \ + --retry 3 \ + --output /usr/bin/clair-scanner \ + https://github.com/arminc/clair-scanner/releases/download/v8/clair-scanner_linux_amd64 + - chmod +x /usr/bin/clair-scanner + - touch clair-whitelist.yml + - echo "Waiting for Clair to start" + - | + while ! nc -z docker 6060; do + sleep 1 + WAIT=$((${WAIT} + 1)) + if [ "${WAIT}" -gt 30 ]; then + echo "Error > Timeout waiting for Clair to start" + exit 1 + fi + done + - docker pull "registry.gitlab.com/${CI_PROJECT_PATH}/${ADDON_ARCH}:${CI_COMMIT_SHA}" + script: + - | + clair-scanner \ + -c http://docker:6060 \ + --ip $(hostname -i) \ + -w clair-whitelist.yml \ + "registry.gitlab.com/${CI_PROJECT_PATH}/${ADDON_ARCH}:${CI_COMMIT_SHA}" + tags: + - scan + +# Generic deploy template +.deploy: &deploy + <<: *dind + stage: deploy + before_script: + - docker info + - docker pull "registry.gitlab.com/${CI_PROJECT_PATH}/${ADDON_ARCH}:${CI_COMMIT_SHA}" + - | + echo "${CI_JOB_TOKEN}" | docker login \ + --username gitlab-ci-token \ + --password-stdin \ + registry.gitlab.com + - | + echo "${DOCKER_PASSWORD}" | docker login \ + --username "${DOCKER_LOGIN}" \ + --password-stdin + script: + - | + docker tag \ + "registry.gitlab.com/${CI_PROJECT_PATH}/${ADDON_ARCH}:${CI_COMMIT_SHA}" \ + "registry.gitlab.com/${CI_PROJECT_PATH}/${ADDON_ARCH}:cache" + - docker push "registry.gitlab.com/${CI_PROJECT_PATH}/${ADDON_ARCH}:cache" + - TAG="${CI_COMMIT_TAG#v}" + - TAG="${TAG:-${CI_COMMIT_SHA:0:7}}" + - | + docker tag \ + "registry.gitlab.com/${CI_PROJECT_PATH}/${ADDON_ARCH}:${CI_COMMIT_SHA}" \ + "${DOCKER_HUB_ORG}/${ADDON_SLUG}:${ADDON_ARCH}-${TAG}" + - | + docker push \ + "${DOCKER_HUB_ORG}/${ADDON_SLUG}:${ADDON_ARCH}-${TAG}" + tags: + - deploy + only: + - master + - /^v\d+\.\d+\.\d+(?:-(?:beta|rc)(?:(?:(?:\+|\.)?[a-zA-Z0-9]+)*)?)?$/ + except: + - /^(?!master).+@/ + +# Generic manifest template +.manifest: &manifest + <<: *dind + stage: manifest + before_script: + - mkdir -p ~/.docker + - echo '{"experimental":"enabled"}' > ~/.docker/config.json + - docker info + - | + echo "${DOCKER_PASSWORD}" | docker login \ + --username "${DOCKER_LOGIN}" \ + --password-stdin + script: + - TAG="${TAG#v}" + - TAG="${TAG:-${CI_COMMIT_SHA:0:7}}" + - REF="${CI_COMMIT_TAG#v}" + - REF="${REF:-${CI_COMMIT_SHA:0:7}}" + - | + docker manifest create \ + "${DOCKER_HUB_ORG}/${ADDON_SLUG}:${TAG}" \ + "${DOCKER_HUB_ORG}/${ADDON_SLUG}:aarch64-${REF}" \ + "${DOCKER_HUB_ORG}/${ADDON_SLUG}:amd64-${REF}" \ + "${DOCKER_HUB_ORG}/${ADDON_SLUG}:armhf-${REF}" \ + "${DOCKER_HUB_ORG}/${ADDON_SLUG}:i386-${REF}" + - | + docker manifest annotate \ + "${DOCKER_HUB_ORG}/${ADDON_SLUG}:${TAG}" \ + "${DOCKER_HUB_ORG}/${ADDON_SLUG}:aarch64-${REF}" \ + --os=linux \ + --arch=arm64 \ + --variant=v8 + - | + docker manifest annotate \ + "${DOCKER_HUB_ORG}/${ADDON_SLUG}:${TAG}" \ + "${DOCKER_HUB_ORG}/${ADDON_SLUG}:amd64-${REF}" \ + --os=linux \ + --arch=amd64 + - | + docker manifest annotate \ + "${DOCKER_HUB_ORG}/${ADDON_SLUG}:${TAG}" \ + "${DOCKER_HUB_ORG}/${ADDON_SLUG}:armhf-${REF}" \ + --os=linux \ + --arch=arm \ + --variant=v6 + - | + docker manifest annotate \ + "${DOCKER_HUB_ORG}/${ADDON_SLUG}:${TAG}" \ + "${DOCKER_HUB_ORG}/${ADDON_SLUG}:i386-${REF}" \ + --os=linux \ + --arch=386 + - | + docker manifest push \ + "${DOCKER_HUB_ORG}/${ADDON_SLUG}:${TAG}" + tags: + - manifest + except: + - /^(?!master).+@/ + +# Generic publish template +.publish: &publish + stage: publish + image: + name: hassioaddons/repository-updater:latest + entrypoint: [""] + script: + - | + repository-updater \ + --token "${GITHUB_TOKEN}" \ + --repository "${REPOSITORY}" \ + --addon "${ADDON_GITHUB_REPO}" + tags: + - publish + except: + - /^(?!master).+@/ + +# Preflight jobs +hadolint: + <<: *preflight + image: hadolint/hadolint:latest-debian + before_script: + - hadolint --version + script: + - hadolint "${ADDON_TARGET}/Dockerfile" + +shellcheck: + <<: *preflight + image: + name: koalaman/shellcheck-alpine:stable + entrypoint: [""] + before_script: + - shellcheck --version + - apk --no-cache add grep + - | + find . -type f -print0 | \ + xargs -0 sed -i 's:#!/usr/bin/with-contenv bash:#!/bin/bash:g' + script: + - | + for file in $(grep -IRl "#\!\(/usr/bin/env \|/bin/\)" --exclude-dir ".git" "${ADDON_TARGET}"); do + if ! shellcheck $file; then + export FAILED=1 + else + echo "$file OK" + fi + done + if [ "${FAILED}" = "1" ]; then + exit 1 + fi + +yamllint: + <<: *preflight + image: sdesbure/yamllint + before_script: + - yamllint --version + script: + - yamllint . + +jsonlint: + <<: *preflight + image: sahsu/docker-jsonlint + before_script: + - jsonlint --version || true + script: + - | + for file in $(find . -type f -name "*.json"); do + if ! jsonlint -q $file; then + export FAILED=1 + else + echo "$file OK" + fi + done + if [ "${FAILED}" = "1" ]; then + exit 1 + fi +markdownlint: + <<: *preflight + image: + name: ruby:alpine + entrypoint: [""] + before_script: + - gem install mdl + - mdl --version + script: + - mdl --style all --warnings . + +# Build Jobs +build:armhf: + <<: *build + variables: + ADDON_ARCH: armhf + FROM: hassioaddons/base-armhf:2.1.2 + tags: + - build + - armhf + +build:aarch64: + <<: *build + variables: + ADDON_ARCH: aarch64 + FROM: hassioaddons/base-aarch64:2.1.2 + tags: + - build + - aarch64 + +build:i386: + <<: *build + variables: + ADDON_ARCH: i386 + FROM: hassioaddons/base-i386:2.1.2 + tags: + - build + - i386 + +build:amd64: + <<: *build + variables: + ADDON_ARCH: amd64 + FROM: hassioaddons/base-amd64:2.1.2 + tags: + - build + - amd64 + +# Scan jobs +clair:armhf: + <<: *scan + variables: + ADDON_ARCH: armhf + +clair:aarch64: + <<: *scan + variables: + ADDON_ARCH: aarch64 + +clair:i386: + <<: *scan + variables: + ADDON_ARCH: i386 + +clair:amd64: + <<: *scan + variables: + ADDON_ARCH: amd64 + +# Deploy jobs +deploy:armhf: + <<: *deploy + variables: + ADDON_ARCH: armhf + +deploy:aarch64: + <<: *deploy + variables: + ADDON_ARCH: aarch64 + +deploy:i386: + <<: *deploy + variables: + ADDON_ARCH: i386 + +deploy:amd64: + <<: *deploy + variables: + ADDON_ARCH: amd64 + +# Manifest jobs +manifest:sha: + <<: *manifest + only: + - master + +manifest:version: + <<: *manifest + variables: + TAG: "${CI_COMMIT_TAG}" + only: + - /^v\d+\.\d+\.\d+(?:-(?:beta|rc)(?:(?:(?:\+|\.)?[a-zA-Z0-9]+)*)?)?$/ + +manifest:stable: + <<: *manifest + variables: + TAG: latest + only: + - /^v\d+\.\d+\.\d+(?:(?:(?:\+|\.)?[a-zA-Z0-9]+)*)?$/ + +manifest:beta: + <<: *manifest + variables: + TAG: beta + only: + - /^v\d+\.\d+\.\d+(?:-(?:beta|rc)(?:(?:(?:\+|\.)?[a-zA-Z0-9]+)*)?)?$/ + +manifest:edge: + <<: *manifest + variables: + TAG: edge + only: + - master + +# Publish jobs +publish:stable: + <<: *publish + variables: + REPOSITORY: hassio-addons/repository + only: + - /^v\d+\.\d+\.\d+(?:(?:(?:\+|\.)?[a-zA-Z0-9]+)*)?$/ + environment: + name: stable + +publish:beta: + <<: *publish + variables: + REPOSITORY: hassio-addons/repository-beta + only: + - /^v\d+\.\d+\.\d+(?:-(?:beta|rc)(?:(?:(?:\+|\.)?[a-zA-Z0-9]+)*)?)?$/ + environment: + name: beta + +publish:edge: + <<: *publish + variables: + REPOSITORY: hassio-addons/repository-edge + only: + - master + environment: + name: edge diff --git a/.mdlrc b/.mdlrc new file mode 100644 index 0000000..2b0128d --- /dev/null +++ b/.mdlrc @@ -0,0 +1 @@ +rules "~MD024" \ No newline at end of file diff --git a/.yamllint b/.yamllint new file mode 100644 index 0000000..0a2a889 --- /dev/null +++ b/.yamllint @@ -0,0 +1,64 @@ +--- +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: + 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/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..0ac232b --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,74 @@ +# Code of conduct + +## Our pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our standards + +Examples of behavior that contributes to creating a positive environment +include: + +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +- The use of sexualized language or imagery and unwelcome sexual attention + or advances +- Trolling, insulting/derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or + electronic address, without explicit permission +- Other conduct which could reasonably be considered inappropriate + in a professional setting + +## Our responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project lead at frenck@addons.community. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project lead is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 1.4, available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..074d634 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,29 @@ +# Contributing + +When contributing to this repository, please first discuss the change you wish +to make via issue, email, or any other method with the owners of this repository +before making a change. + +Please note we have a code of conduct, please follow it in all your interactions +with the project. + +## Issues and feature requests + +You've found a bug in the source code, a mistake in the documentation or maybe +you'd like a new feature? You can help us by submitting an issue to our +[GitHub Repository][github]. Before you create an issue, make sure you search +the archive, maybe your question was already answered. + +Even better: You could submit a pull request with a fix / new feature! + +## Pull request process + +1. Search our repository for open or closed [pull requests][prs] that relates + to your submission. You don't want to duplicate effort. + +1. You may merge the pull request in once you have the sign-off of two other + developers, or if you do not have permission to do that, you may request + the second reviewer to merge it for you. + +[github]: https://github.com/hassio-addons/addon-nut/issues +[prs]: https://github.com/hassio-addons/addon-nut/pulls diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..7ee4e68 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +# MIT License + +Copyright (c) 2018 Dale Higgs + +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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..08d4bb7 --- /dev/null +++ b/README.md @@ -0,0 +1,420 @@ +# Community Hass.io Add-ons: Network UPS Tools + +[![GitHub Release][releases-shield]][releases] +![Project Stage][project-stage-shield] +[![License][license-shield]](LICENSE.md) + +[![GitLab CI][gitlabci-shield]][gitlabci] +![Project Maintenance][maintenance-shield] +[![GitHub Activity][commits-shield]][commits] + +[![Bountysource][bountysource-shield]][bountysource] +[![Discord][discord-shield]][discord] +[![Community Forum][forum-shield]][forum] + +[![Buy me a coffee][buymeacoffee-shield]][buymeacoffee] + +A Network UPS Tools daemon to allow you to easily manage battery backup (UPS) +devices connected to your Hass.io machine. + +## About + +The primary goal of the Network UPS Tools (NUT) project is to provide support +for Power Devices, such as Uninterruptible Power Supplies, Power Distribution +Units, Automatic Transfer Switch, Power Supply Units and Solar Controllers. + +NUT provides many control and monitoring [features][nut-features], with a +uniform control and management interface. + +More than 140 different manufacturers, and several thousands models +are [compatible][nut-compatible]. + +The Network UPS Tools (NUT) project is the combined effort of +many [individuals and companies][nut-acknowledgements]. + +## Installation + +The installation of this add-on is pretty straightforward 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. +1. Install the "Network UPS Tools" add-on. +1. Configure the `users` and `devices` options. +1. Start the "Network UPS Tools" add-on. +1. Check the logs of the "Network UPS Tools" add-on to see if everything went well. +1. Configure [NUT Sensor][nut-sensor-docs] in your `configuration.yaml` file. + +**NOTE**: Do not add this repository to Hass.io, please use: +`https://github.com/hassio-addons/repository`. + +## Docker status + +![Supports armhf Architecture][armhf-shield] +![Supports aarch64 Architecture][aarch64-shield] +![Supports amd64 Architecture][amd64-shield] +![Supports i386 Architecture][i386-shield] + +[![Docker Version][version-shield]][microbadger] +[![Docker Layers][layers-shield]][microbadger] +[![Docker Pulls][pulls-shield]][dockerhub] + +## Configuration + +Even though this add-on is just a basic add-on, it does come with some +configuration options to play around with. + +**Note**: _Remember to restart the add-on when the configuration is changed._ + +Network UPS Tools add-on configuration: + +```json +{ + "log_level": "info", + "users": [ + { + "username": "nutty", + "password": "changeme", + "instcmds": [ + "all" + ], + "actions": [] + } + ], + "devices": [ + { + "name": "myups", + "driver": "usbhid-ups", + "port": "auto", + "config": [] + } + ], + "upsd": [ + "LISTEN 0.0.0.0" + ], + "nut": { + "mode": "netserver" + } +} +``` + +### Option: `log_level` + +The `log_level` option controls the level of log output by the add-on and can +be changed to be more or less verbose, which might be useful when you are +dealing with an unknown issue. Possible values are: + +- `trace`: Show every detail, like all called internal functions. +- `debug`: Shows detailed debug information. +- `info`: Normal (usually) interesting events. +- `warning`: Exceptional occurrences that are not errors. +- `error`: Runtime errors that do not require immediate action. +- `fatal`: Something went terribly wrong. Add-on becomes unusable. + +Please note that each level automatically includes log messages from a +more severe level, e.g., `debug` also shows `info` messages. By default, +the `log_level` is set to `info`, which is the recommended setting unless +you are troubleshooting. + +### Option: `users` + +This option allows you to specify a list of one or more users. Each user can +have its own privileges like defined in the sub-options below. + +_Refer to the [`upsd.users(5)`][upsd-users] documentation for more information._ + +#### Sub-option: `username` + +The username the user needs to use to login to the NUT server. A valid username +contains only `a-z`, `A-Z`, `0-9` and underscore characters (`_`). + +#### Sub-option: `password` + +Set the password for this user. + +#### Sub-option: `instcmds` + +A list of instant commands that a user is allowed to initiate. Use `all` to +grant all commands automatically. + +#### Sub-option: `actions` + +A list of actions that a user is allowed to perform. Valid actions are: + +- `set`: change the value of certain variables in the UPS. +- `fsd`: set the forced shutdown flag in the UPS. This is equivalent to an + "on battery + low battery" situation for the purposes of monitoring. + +The list of actions is expected to grow in the future. + +#### Sub-option: `upsmon` + +Add the necessary actions for a `upsmon` process to work. This is either set to +`master` or `slave`. + +### Option: `devices` + +This option allows you to specify a list of UPS devices attached to your +system. + +_Refer to the [`ups.conf(5)`][ups-conf] documentation for more information._ + +#### Sub-option: `name` + +The name of the UPS. The name `default` is used internally, so you can’t use +it in this file. + +#### Sub-option: `driver` + +This specifies which program will be monitoring this UPS. You need to specify +the one that is compatible with your hardware. See [`nutupsdrv(8)`][nutupsdrv] +for more information on drivers in general and pointers to the man pages of +specific drivers. + +#### Sub-option: `port` + +This is the serial port where the UPS is connected. The first serial port +usually is `/dev/ttyS0`. Use `auto` to automatically detect the port. + +#### Sub-option: `config` + +A list of additional [options][ups-fields] to configure for this UPS. The common +[`usbhid-ups`][usbhid-ups] driver allows you to distinguish between devices by +using a combination of the `vendor`, `product`, `serial`, `vendorid`, and +`productid` options: + +```json +{ + ... + "devices": [ + { + "name": "mge", + "driver": "usbhid-ups", + "port": "auto", + "config": [ + "vendorid = 0463" + ] + }, + { + "name": "apcups", + "driver": "usbhid-ups", + "port": "auto", + "config": [ + "vendorid = 051d*" + ] + }, + { + "name": "foocorp", + "driver": "usbhid-ups", + "port": "auto", + "config": [ + "vendor = \"Foo.Corporation.*\"" + ] + }, + { + "name": "smartups", + "driver": "usbhid-ups", + "port": "auto", + "config": [ + "product = \".*(Smart|Back)-?UPS.*\"" + ] + } + ], + ... +``` + +### Option: `upsd` + +A list of configuration options for the `upsd` server. Most users will only +need to use the `LISTEN ` option. + +_Refer to the [`upsd.conf(5)`][upsd-conf] documentation for more information._ + +### Option: `nut` + +A set of configuration options for NUT. It is recommended that you do not +change these options unless you know what you are doing. + +_Refer to the [`nut.conf(5)`][nut-conf] documentation for more information._ + +#### Sub-option: `mode` + +Recognized values are `none`, `standalone`, `netserver` and `netclient`. + +- `none`: Indicates that NUT should not get started automatically, possibly + because it is not configured or that an Integrated Power Management or some + external system, is used to startup the NUT components. +- `standalone`: Addresses a local only configuration, with 1 UPS protecting + the local system. This implies to start the 3 NUT layers (driver, `upsd` + and `upsmon`), with the related configuration files. This mode can also + address UPS redundancy. +- `netserver`: Like the `standalone` configuration, but also possibly need one + or more specific `LISTEN` options in `upsd` section. Since this mode is open + to the network, a special care should be applied to security concerns. +- `netclient`: When only `upsmon` is required, possibly because there are + other hosts that are more closely attached to the UPS, the mode should be + set to `netclient`. + +#### Sub-option: `upsd_options` + +Set `upsd` specific options. See [`upsd(8)`][upsd] for more details. It is +ignored when `mode` above indicates that no `upsd` should be running. + +#### Sub-option: `upsmon_options` + +Set `upsmon` specific options. See [`upsmon(8)`][upsmon] for more details. It +is ignored when `mode` above indicates that no `upsmon` should be running. + +#### Sub-option: `poweroff_wait` + +At the end of an emergency system halt, the `upsmon` master will signal the UPS +to switch off. This may fail for a number of reasons. Most notably is the case +that mains power returns during the shutdown process. The system will wait this +long for the UPS to cut power, and then reboot. It should be long enough to +exhaust the batteries, in case line power continues to be unavailable. On the +other hand, it should not be so long that the system remains offline for an +unreasonable amount of time if line power has returned. See [`sleep(1)`][sleep] +for compatible time syntax. If you specify the time in seconds, use the `s` +suffix. + +### Option: `i_like_to_be_pwned` + +Adding this option to the add-on configuration allows to you bypass the +HaveIBeenPwned password requirement by setting it to `true`. + +**Note**: _We STRONGLY suggest picking a stronger/safer password instead of +using this option! USE AT YOUR OWN RISK!_ + +### Option: `leave_front_door_open` + +Adding this option to the add-on configuration allows you to disable +authentication on the NUT server by setting it to `true` and leaving the +username and password empty. + +**Note**: _We STRONGLY suggest, not to use this, even if this add-on is +only exposed to your internal network. USE AT YOUR OWN RISK!_ + +## Changelog & Releases + +This repository keeps a change log using [GitHub's releases][releases] +functionality. The format of the log is based on +[Keep a Changelog][keepchangelog]. + +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 options to get them answered: + +- The [Community Hass.io Add-ons Discord chat server][discord] for add-on + support and feature requests. +- The [Home Assistant Discord chat server][discord-ha] for general Home + Assistant discussions and questions. +- The Home Assistant [Community Forum][forum]. +- Join the [Reddit subreddit][reddit] in [/r/homeassistant][reddit] + +You could also [open an issue here][issue] GitHub. + +## Contributing + +This is an active open-source project. We are always open to people who want to +use the code or contribute to it. + +We have set up a separate document containing our +[contribution guidelines](CONTRIBUTING.md). + +Thank you for being involved! :heart_eyes: + +## Authors & contributors + +The original setup of this repository is by [Dale Higgs][dale3h]. + +For a full list of all authors and contributors, +check [the contributor's page][contributors]. + +## We have got some Hass.io add-ons for you + +Want some more functionality to your Hass.io Home Assistant instance? + +We have created multiple add-ons for Hass.io. For a full list, check out +our [GitHub Repository][repository]. + +## License + +MIT License + +Copyright (c) 2018 Dale Higgs + +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 +[amd64-shield]: https://img.shields.io/badge/amd64-yes-green.svg +[armhf-shield]: https://img.shields.io/badge/armhf-yes-green.svg +[bountysource-shield]: https://img.shields.io/bountysource/team/hassio-addons/activity.svg +[bountysource]: https://www.bountysource.com/teams/hassio-addons/issues +[buymeacoffee-shield]: https://www.buymeacoffee.com/assets/img/guidelines/download-assets-sm-2.svg +[buymeacoffee]: https://www.buymeacoffee.com/dale3h +[commits-shield]: https://img.shields.io/github/commit-activity/y/hassio-addons/addon-nut.svg +[commits]: https://github.com/hassio-addons/addon-nut/commits/master +[contributors]: https://github.com/hassio-addons/addon-nut/graphs/contributors +[dale3h]: https://github.com/dale3h +[discord-ha]: https://discord.gg/c5DvZ4e +[discord-shield]: https://img.shields.io/discord/478094546522079232.svg +[discord]: https://discord.me/hassioaddons +[dockerhub]: https://hub.docker.com/r/hassioaddons/nut +[forum-shield]: https://img.shields.io/badge/community-forum-brightgreen.svg +[forum]: https://community.home-assistant.io/t/community-hass-io-add-on-network-ups-tools/68516 +[gitlabci-shield]: https://gitlab.com/hassio-addons/addon-nut/badges/master/pipeline.svg +[gitlabci]: https://gitlab.com/hassio-addons/addon-nut/pipelines +[i386-shield]: https://img.shields.io/badge/i386-yes-green.svg +[issue]: https://github.com/hassio-addons/addon-nut/issues +[keepchangelog]: https://keepachangelog.com/en/1.0.0/ +[layers-shield]: https://images.microbadger.com/badges/image/hassioaddons/nut.svg +[license-shield]: https://img.shields.io/github/license/hassio-addons/addon-nut.svg +[maintenance-shield]: https://img.shields.io/maintenance/yes/2018.svg +[microbadger]: https://microbadger.com/images/hassioaddons/nut +[nut-acknowledgements]: https://networkupstools.org/acknowledgements.html +[nut-compatible]: https://networkupstools.org/stable-hcl.html +[nut-conf]: https://networkupstools.org/docs/man/nut.conf.html +[nut-features]: https://networkupstools.org/features.html +[nut-sensor-docs]: https://www.home-assistant.io/components/sensor.nut/ +[nutupsdrv]: https://networkupstools.org/docs/man/nutupsdrv.html +[project-stage-shield]: https://img.shields.io/badge/project%20stage-experimental-yellow.svg +[pulls-shield]: https://img.shields.io/docker/pulls/hassioaddons/nut.svg +[reddit]: https://reddit.com/r/homeassistant +[releases-shield]: https://img.shields.io/github/release/hassio-addons/addon-nut.svg +[releases]: https://github.com/hassio-addons/addon-nut/releases +[repository]: https://github.com/hassio-addons/repository +[semver]: https://semver.org/spec/v2.0.0.html +[sleep]: https://linux.die.net/man/1/sleep +[ups-conf]: https://networkupstools.org/docs/man/ups.conf.html +[ups-fields]: https://networkupstools.org/docs/man/ups.conf.html#_ups_fields +[upsd-conf]: https://networkupstools.org/docs/man/upsd.conf.html +[upsd-users]: https://networkupstools.org/docs/man/upsd.users.html +[upsd]: https://networkupstools.org/docs/man/upsd.html +[upsmon]: https://networkupstools.org/docs/man/upsmon.html +[usbhid-ups]: https://networkupstools.org/docs/man/usbhid-ups.html +[version-shield]: https://images.microbadger.com/badges/version/hassioaddons/nut.svg diff --git a/nut/.README.j2 b/nut/.README.j2 new file mode 100644 index 0000000..7427c67 --- /dev/null +++ b/nut/.README.j2 @@ -0,0 +1,78 @@ +# Community Hass.io Add-ons: Network UPS Tools + +[![Release][release-shield]][release] ![Project Stage][project-stage-shield] ![Project Maintenance][maintenance-shield] + +[![Discord][discord-shield]][discord] [![Community Forum][forum-shield]][forum] + +[![Buy me a coffee][buymeacoffee-shield]][buymeacoffee] + +A Network UPS Tools daemon to allow you to easily manage battery backup (UPS) +devices connected to your Hass.io machine. + +## About + +The primary goal of the Network UPS Tools (NUT) project is to provide support +for Power Devices, such as Uninterruptible Power Supplies, Power Distribution +Units, Automatic Transfer Switch, Power Supply Units and Solar Controllers. + +NUT provides many control and monitoring [features][nut-features], with a +uniform control and management interface. + +More than 140 different manufacturers, and several thousands models +are [compatible][nut-compatible]. + +The Network UPS Tools (NUT) project is the combined effort of +many [individuals and companies][nut-acknowledgements]. + +[Click here for the full documentation][docs] + +{% if channel == "edge" %} +## WARNING! THIS IS AN EDGE VERSION! + +This Hass.io Add-ons repository contains edge builds of add-ons. Edge builds +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 our add-ons: + + + +{% endif %} +{% if channel == "beta" %} +## WARNING! THIS IS A BETA VERSION! + +This Hass.io Add-ons repository contains beta releases of add-ons. + +- 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 our add-ons: + + + +{% endif %} +[project-stage-shield]: https://img.shields.io/badge/project%20stage-experimental-yellow.svg +[forum-shield]: https://img.shields.io/badge/community-forum-brightgreen.svg +[forum]: https://community.home-assistant.io/t/community-hass-io-add-on-network-ups-tools/68516 +[discord-shield]: https://img.shields.io/discord/478094546522079232.svg +[discord]: https://discord.me/hassioaddons +[maintenance-shield]: https://img.shields.io/maintenance/yes/2018.svg +[release-shield]: https://img.shields.io/badge/version-{{ version }}-blue.svg +[release]: {{ repo }}/tree/{{ version }} +[docs]: {{ repo }}/blob/{{ version }}/README.md +[buymeacoffee-shield]: https://www.buymeacoffee.com/assets/img/guidelines/download-assets-sm-2.svg +[buymeacoffee]: https://www.buymeacoffee.com/dale3h diff --git a/nut/Dockerfile b/nut/Dockerfile new file mode 100644 index 0000000..a7d8aca --- /dev/null +++ b/nut/Dockerfile @@ -0,0 +1,33 @@ +ARG BUILD_FROM=hassioaddons/base:2.1.2 +# hadolint ignore=DL3006 +FROM ${BUILD_FROM} + +# Copy root filesystem +COPY rootfs / + +# Setup base +RUN apk add --no-cache nut@edge=2.7.4-r5 + +# Build arugments +ARG BUILD_ARCH +ARG BUILD_DATE +ARG BUILD_REF +ARG BUILD_VERSION + +# Labels +LABEL \ + io.hass.name="Network UPS Tools" \ + io.hass.description="Manage battery backup (UPS) devices" \ + io.hass.arch="${BUILD_ARCH}" \ + io.hass.type="addon" \ + io.hass.version=${BUILD_VERSION} \ + maintainer="Dale Higgs " \ + org.label-schema.description="Manage battery backup (UPS) devices" \ + org.label-schema.build-date=${BUILD_DATE} \ + org.label-schema.name="Network UPS Tools" \ + org.label-schema.schema-version="1.0" \ + org.label-schema.url="https://community.home-assistant.io/" \ + org.label-schema.usage="https://github.com/hassio-addons/addon-nut/tree/master/README.md" \ + org.label-schema.vcs-ref=${BUILD_REF} \ + org.label-schema.vcs-url="https://github.com/hassio-addons/addon-nut" \ + org.label-schema.vendor="Community Hass.io Addons" diff --git a/nut/build.json b/nut/build.json new file mode 100644 index 0000000..6aafc4f --- /dev/null +++ b/nut/build.json @@ -0,0 +1,10 @@ +{ + "squash": false, + "build_from": { + "aarch64": "hassioaddons/base-aarch64:2.1.2", + "amd64": "hassioaddons/base-amd64:2.1.2", + "armhf": "hassioaddons/base-armhf:2.1.2", + "i386": "hassioaddons/base-i386:2.1.2" + }, + "args": {} +} diff --git a/nut/config.json b/nut/config.json new file mode 100644 index 0000000..9dad58e --- /dev/null +++ b/nut/config.json @@ -0,0 +1,78 @@ +{ + "name": "Network UPS Tools", + "version": "dev", + "slug": "nut", + "description": "Manage battery backup (UPS) devices", + "url": "https://github.com/hassio-addons/addon-nut", + "startup": "system", + "arch": [ + "aarch64", + "amd64", + "armhf", + "i386" + ], + "boot": "auto", + "ports": { + "3493/tcp": 3493 + }, + "devices": [ + "/dev/bus/usb:/dev/bus/usb:rwm" + ], + "options": { + "log_level": "info", + "users": [ + { + "username": "", + "password": "", + "instcmds": ["all"], + "actions": [] + } + ], + "devices": [ + { + "name": "myups", + "driver": "usbhid-ups", + "port": "auto", + "config": [] + } + ], + "upsd": [ + "LISTEN 0.0.0.0" + ], + "nut": { + "mode": "netserver" + } + }, + "schema": { + "log_level": "match(^(trace|debug|info|notice|warning|error|fatal)$)", + "users": [ + { + "username": "str", + "password": "str", + "instcmds": ["str"], + "actions": ["str"], + "upsmon": "match(^(master|slave)$)?" + } + ], + "devices": [ + { + "name": "str", + "driver": "str", + "port": "str", + "config": ["str"] + } + ], + "upsd": ["match(^(MAXAGE|STATEPATH|LISTEN|MAXCONN|CERTFILE|CERTPATH|CERTIDENT|CERTREQUEST) )"], + "nut": { + "mode": "match(^(none|standalone|netserver|netclient)$)", + "upsd_options": "str?", + "upsmon_options": "str?", + "poweroff_wait": "str?" + }, + "i_like_to_be_pwned": "bool?", + "leave_front_door_open": "bool?" + }, + "environment": { + "LOG_FORMAT": "{LEVEL}: {MESSAGE}" + } +} diff --git a/nut/icon.png b/nut/icon.png new file mode 100644 index 0000000..ee15fec Binary files /dev/null and b/nut/icon.png differ diff --git a/nut/logo.png b/nut/logo.png new file mode 100644 index 0000000..75fa6ed Binary files /dev/null and b/nut/logo.png differ diff --git a/nut/rootfs/etc/cont-init.d/50-upsd b/nut/rootfs/etc/cont-init.d/50-upsd new file mode 100644 index 0000000..fee41fd --- /dev/null +++ b/nut/rootfs/etc/cont-init.d/50-upsd @@ -0,0 +1,94 @@ +#!/usr/bin/with-contenv bash +# ============================================================================== +# Community Hass.io Add-ons: FTP +# Configures Network UPS Tools +# ============================================================================== +# shellcheck disable=SC1091 +source /usr/lib/hassio-addons/base.sh + +readonly UPS_CONF=/etc/nut/ups.conf +readonly UPSD_CONF=/etc/nut/upsd.conf +readonly NUT_CONF=/etc/nut/nut.conf + +gen_ups_conf() { + local name + local driver + local port + local config + + echo -n > "${UPS_CONF}" + echo "# This file was automatically generated by Hass.io Network" >> "${UPS_CONF}" + echo "# UPS Tools add-on. Changes to this file will be overwritten." >> "${UPS_CONF}" + + for device in $(hass.config.get "devices|keys[]"); do + name=$(hass.config.get "devices[${device}].name") + driver=$(hass.config.get "devices[${device}].driver") + port=$(hass.config.get "devices[${device}].port") + + echo >> "${UPS_CONF}" + echo "[${name}]" >> "${UPS_CONF}" + echo " driver = ${driver}" >> "${UPS_CONF}" + echo " port = ${port}" >> "${UPS_CONF}" + + for key in $(hass.config.get "devices[${device}].config|keys[]"); do + config=$(hass.config.get "devices[${device}].config[${key}]") + echo " ${config}" >> "${UPS_CONF}" + done + done +} + +gen_upsd_conf() { + echo -n > "${UPSD_CONF}" + echo "# This file was automatically generated by Hass.io Network" >> "${UPSD_CONF}" + echo "# UPS Tools add-on. Changes to this file will be overwritten." >> "${UPSD_CONF}" + echo >> "${UPSD_CONF}" + + for key in $(hass.config.get "upsd|keys[]"); do + echo $(hass.config.get "upsd[${key}]") >> "${UPSD_CONF}" + done +} + +gen_nut_conf() { + local mode + local upsd_options + local upsmon_options + local poweroff_wait + + echo -n > "${NUT_CONF}" + echo "# This file was automatically generated by Hass.io Network" >> "${NUT_CONF}" + echo "# UPS Tools add-on. Changes to this file will be overwritten." >> "${NUT_CONF}" + echo >> "${NUT_CONF}" + + mode=$(hass.config.get "nut.mode") + echo "MODE=${mode}" >> "${NUT_CONF}" + + if hass.config.has_value "nut.upsd_options"; then + upsd_options=$(hass.config.get "nut.upsd_options") + echo "UPSD_OPTIONS=\"${upsd_options}\"" >> "${NUT_CONF}" + fi + + if hass.config.has_value "nut.upsmon_options"; then + upsmon_options=$(hass.config.get "nut.upsmon_options") + echo "UPSMON_OPTIONS=\"${upsmon_options}\"" >> "${NUT_CONF}" + fi + + if hass.config.has_value "nut.poweroff_wait"; then + poweroff_wait=$(hass.config.get "nut.poweroff_wait") + echo "POWEROFF_WAIT=${poweroff_wait}" >> "${NUT_CONF}" + fi +} + +# ============================================================================== +# RUN LOGIC +# ------------------------------------------------------------------------------ +main() { + hass.log.info "Generating ${UPS_CONF}..." + gen_ups_conf + + hass.log.info "Generating ${UPSD_CONF}..." + gen_upsd_conf + + hass.log.info "Generating ${NUT_CONF}..." + gen_nut_conf +} +main "$@" diff --git a/nut/rootfs/etc/cont-init.d/51-users b/nut/rootfs/etc/cont-init.d/51-users new file mode 100644 index 0000000..72b3c9f --- /dev/null +++ b/nut/rootfs/etc/cont-init.d/51-users @@ -0,0 +1,76 @@ +#!/usr/bin/with-contenv bash +# ============================================================================== +# Community Hass.io Add-ons: FTP +# Configures Network UPS Tools +# ============================================================================== +# shellcheck disable=SC1091 +source /usr/lib/hassio-addons/base.sh + +readonly USERS_CONF=/etc/nut/upsd.users + +gen_upsd_users() { + local username + local password + + echo -n > ${USERS_CONF} + echo "# This file was automatically generated by Hass.io Network" >> ${USERS_CONF} + echo "# UPS Tools add-on. Changes to this file will be overwritten." >> ${USERS_CONF} + + for user in $(hass.config.get "users|keys[]"); do + # Require username / password + if ! hass.config.has_value "users[${user}].username" \ + && ! ( \ + hass.config.exists "leave_front_door_open" \ + && hass.config.true "leave_front_door_open" \ + ); + then + hass.die "You need to set a username!" + fi + + if ! hass.config.has_value "users[${user}].password" \ + && ! ( \ + hass.config.exists "leave_front_door_open" \ + && hass.config.true "leave_front_door_open" \ + ); + then + hass.die "You need to set a password for ${username}!"; + fi + + username=$(hass.config.get "users[${user}].username") + + # Require a secure password + if hass.config.has_value "users[${user}].password" \ + && ! hass.config.is_safe_password "users[${user}].password"; then + hass.die \ + "Please choose a different pass for ${username}, this one is unsafe!" + fi + + password=$(hass.config.get "users[${user}].password") + + echo >> ${USERS_CONF} + echo "[${username}]" >> ${USERS_CONF} + echo " password = ${password}" >> ${USERS_CONF} + + for instcmd in $(hass.config.get "users[${user}].instcmds"); do + echo " instcmds = ${instcmd}" >> ${USERS_CONF} + done + + for action in $(hass.config.get "users[${user}].actions"); do + echo " actions = ${action}" >> ${USERS_CONF} + done + + if hass.config.has_value "users[${user}].upsmon"; then + upsmon=$(hass.config.get "users[${user}].upsmon") + echo " upsmon ${upsmon}" >> ${USERS_CONF} + fi + done +} + +# ============================================================================== +# RUN LOGIC +# ------------------------------------------------------------------------------ +main() { + hass.log.info "Generating ${USERS_CONF}..." + gen_upsd_users +} +main "$@" diff --git a/nut/rootfs/etc/cont-init.d/55-upsdrvctl b/nut/rootfs/etc/cont-init.d/55-upsdrvctl new file mode 100644 index 0000000..cdf6baa --- /dev/null +++ b/nut/rootfs/etc/cont-init.d/55-upsdrvctl @@ -0,0 +1,16 @@ +#!/usr/bin/with-contenv bash +# ============================================================================== +# Community Hass.io Add-ons: FTP +# Starts UPS drivers +# ============================================================================== +# shellcheck disable=SC1091 +source /usr/lib/hassio-addons/base.sh + +hass.log.info "Starting the UPS drivers" + +# Run upsdrvctl +if hass.debug; then + exec upsdrvctl -D start +else + exec upsdrvctl start +fi diff --git a/nut/rootfs/etc/fix-attrs.d/10-nut b/nut/rootfs/etc/fix-attrs.d/10-nut new file mode 100644 index 0000000..beba7dd --- /dev/null +++ b/nut/rootfs/etc/fix-attrs.d/10-nut @@ -0,0 +1,3 @@ +/var/run/nut false root:nut 0770 0770 +/etc/nut/* false root:nut 0660 0660 +/dev/bus/usb/*/* false root 0777 0777 diff --git a/nut/rootfs/etc/services.d/upsd/finish b/nut/rootfs/etc/services.d/upsd/finish new file mode 100644 index 0000000..b74ba79 --- /dev/null +++ b/nut/rootfs/etc/services.d/upsd/finish @@ -0,0 +1,9 @@ +#!/usr/bin/execlineb -S0 +# ============================================================================== +# Community Hass.io Add-ons: FTP +# Take down the S6 supervision tree when upsd fails +# ============================================================================== +if -n { s6-test $# -ne 0 } +if -n { s6-test ${1} -eq 256 } + +s6-svscanctl -t /var/run/s6/services diff --git a/nut/rootfs/etc/services.d/upsd/run b/nut/rootfs/etc/services.d/upsd/run new file mode 100644 index 0000000..1f9e549 --- /dev/null +++ b/nut/rootfs/etc/services.d/upsd/run @@ -0,0 +1,14 @@ +#!/usr/bin/with-contenv bash +# ============================================================================== +# Community Hass.io Add-ons: Network UPS Tools +# Run upsd +# ============================================================================== +# shellcheck disable=SC1091 +source /usr/lib/hassio-addons/base.sh + +# hass.log.trace "Disabling errexit" +# set +o errexit + +# Run upsd +hass.log.info "Starting the UPS information server" +exec upsd -D