diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..0776d0f --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,92 @@ +version: 2 +defaults: &defaults + machine: + image: circleci/classic:edge + steps: + - run: + name: Check docker is running + command: docker info + - run: + name: Pull Community Hass.io Add-ons build environment + command: docker pull hassioaddons/build-env:latest + - run: + name: Log in to Docker Hub + command: | + if [[ "${CIRCLE_BRANCH}" = "master" || ! -z "${CIRCLE_TAG:-}" ]]; + then + docker login -u ${DOCKER_LOGIN} -p ${DOCKER_PASSWORD} + fi + - checkout + - deploy: + name: Build and (maybe) deploy + command: | + if [[ "${CIRCLE_BRANCH}" = "master" || ! -z "${CIRCLE_TAG:-}" ]]; + then + docker run \ + --privileged \ + -v ~/.docker:/root/.docker \ + -v "$PWD":/docker \ + hassioaddons/build-env:latest \ + --target debian-base \ + --${ARCH} \ + --git \ + --push + else + docker run \ + --privileged \ + -v "$PWD":/docker \ + hassioaddons/build-env:latest \ + --target debian-base \ + --git \ + --${ARCH} + fi + - deploy: + name: Send notification to Microbadger + command: | + if [[ "${CIRCLE_BRANCH}" = "master" || ! -z "${CIRCLE_TAG:-}" ]]; + then + curl -X POST https://hooks.microbadger.com/images/${MICROBADGER_WEBHOOK} + fi + +jobs: + aarch64: + <<: *defaults + environment: + ARCH: aarch64 + MICROBADGER_WEBHOOK: hassioaddons/debian-base-aarch64/xPXzVNfmlHE-TbnO6_v2_G5XVt4= + amd64: + <<: *defaults + environment: + ARCH: amd64 + MICROBADGER_WEBHOOK: hassioaddons/debian-base-amd64/9VpSGXcF3-G8pL-mKXq-0WWvG3g= + armhf: + <<: *defaults + environment: + ARCH: armhf + MICROBADGER_WEBHOOK: hassioaddons/debian-base-armhf/PzdnuORNZ7a76EmFKI1mrby2p74= + i386: + <<: *defaults + environment: + ARCH: i386 + MICROBADGER_WEBHOOK: hassioaddons/debian-base-i386/te8Hu20AiumjGJbSK0Bf99iivhI= + +workflows: + version: 2 + build_and_maybe_deploy: + jobs: + - aarch64: + filters: + tags: + only: /.*/ + - amd64: + filters: + tags: + only: /.*/ + - armhf: + filters: + tags: + only: /.*/ + - i386: + filters: + tags: + only: /.*/ diff --git a/.codeclimate.yml b/.codeclimate.yml new file mode 100644 index 0000000..b541f14 --- /dev/null +++ b/.codeclimate.yml @@ -0,0 +1,13 @@ +--- +engines: + fixme: + enabled: true + shellcheck: + enabled: true + markdownlint: + enabled: true +ratings: + paths: + - "**.sh" + - "**.md" +exclude_paths: [] 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/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/.gitignore b/.gitignore new file mode 100644 index 0000000..e69de29 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/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..8e8702e --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,13 @@ +# Community Hass.io Add-ons: Debian Base Images + +All notable changes to this add-on will be documented in this file. + +The format is based on [Keep a Changelog][keep-a-changelog] +and this project adheres to [Semantic Versioning][semantic-versioning]. + +## Unreleased + +- Initial version + +[keep-a-changelog]: http://keepachangelog.com/en/1.0.0/ +[semantic-versioning]: http://semver.org/spec/v2.0.0.html 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..d60db71 --- /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-debian-base/issues +[prs]: https://github.com/hassio-addons/addon-debian-base/pulls \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..e8635ed --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Franck Nijhof + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..0ba522b --- /dev/null +++ b/README.md @@ -0,0 +1,168 @@ +# Community Hass.io Add-ons: Debian Base Images + +![Project Stage][project-stage-shield] +![Maintenance][maintenance-shield] +![Awesome][awesome-shield] +[![License][license-shield]](LICENSE.md) + +[![Code Climate][codeclimate-shield]][codeclimate] +[![CircleCI][circleci-shield]][circleci] + +Docker Debian base images used by Community Hass.io Add-ons. + +## Docker status + +[![Docker Architecture][armhf-arch-shield]][armhf-dockerhub] +[![Docker Version][armhf-version-shield]][armhf-microbadger] +[![Docker Layers][armhf-layers-shield]][armhf-microbadger] +[![Docker Pulls][armhf-pulls-shield]][armhf-dockerhub] + +[![Docker Architecture][aarch64-arch-shield]][aarch64-dockerhub] +[![Docker Version][aarch64-version-shield]][aarch64-microbadger] +[![Docker Layers][aarch64-layers-shield]][aarch64-microbadger] +[![Docker Pulls][aarch64-pulls-shield]][aarch64-dockerhub] + +[![Docker Architecture][amd64-arch-shield]][amd64-dockerhub] +[![Docker Version][amd64-version-shield]][amd64-microbadger] +[![Docker Layers][amd64-layers-shield]][amd64-microbadger] +[![Docker Pulls][amd64-pulls-shield]][amd64-dockerhub] + +[![Docker Architecture][i386-arch-shield]][i386-dockerhub] +[![Docker Version][i386-version-shield]][i386-microbadger] +[![Docker Layers][i386-layers-shield]][i386-microbadger] +[![Docker Pulls][i386-pulls-shield]][i386-dockerhub] + +## About + +These are the base images used by add-ons created by the Community Hass.io +Add-ons. + +While Home Assistant provides base images, the images provided by this +repository contain some extras: + +- Based on Debian Stretch (slim) +- Adds [s6] as a process supervisor. +- Adds `jq` & `curl`, since every add-on uses them. +- Adds Docker [Label Schema][label-schema] support. +- Adds a large Bash function library for use with an add-on. +- Handles logs, add-on startup banners and update notifications. +- Several small adjustments and improvements. + +## Changelog & Releases + +This repository keeps a [change log](CHANGELOG.md). 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 Home Assistant [Community Forums][forums], we have a + [dedicated topic][forums] on that forum regarding this repository. +- The Home Assistant [Discord Chat Server][discord] for general Home Assistant + discussions and questions. +- 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've set up a separate document for our +[contribution guidelines](CONTRIBUTING.md). + +Thank you for being involved! :heart_eyes: + +## Authors & contributors + +The original setup of this repository is by [Franck Nijhof][frenck]. + +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) 2017 Franck Nijhof + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +[aarch64-arch-shield]: https://img.shields.io/badge/architecture-aarch64-blue.svg +[aarch64-dockerhub]: https://hub.docker.com/r/hassioaddons/debian-base-aarch64 +[aarch64-layers-shield]: https://images.microbadger.com/badges/image/hassioaddons/debian-base-aarch64.svg +[aarch64-microbadger]: https://microbadger.com/images/hassioaddons/debian-base-aarch64 +[aarch64-pulls-shield]: https://img.shields.io/docker/pulls/hassioaddons/debian-base-aarch64.svg +[aarch64-version-shield]: https://images.microbadger.com/badges/version/hassioaddons/debian-base-aarch64.svg +[amd64-arch-shield]: https://img.shields.io/badge/architecture-amd64-blue.svg +[amd64-dockerhub]: https://hub.docker.com/r/hassioaddons/debian-base-amd64 +[amd64-layers-shield]: https://images.microbadger.com/badges/image/hassioaddons/debian-base-amd64.svg +[amd64-microbadger]: https://microbadger.com/images/hassioaddons/debian-base-amd64 +[amd64-pulls-shield]: https://img.shields.io/docker/pulls/hassioaddons/debian-base-amd64.svg +[amd64-version-shield]: https://images.microbadger.com/badges/version/hassioaddons/debian-base-amd64.svg +[armhf-arch-shield]: https://img.shields.io/badge/architecture-armhf-blue.svg +[armhf-dockerhub]: https://hub.docker.com/r/hassioaddons/debian-base-armhf +[armhf-layers-shield]: https://images.microbadger.com/badges/image/hassioaddons/debian-base-armhf.svg +[armhf-microbadger]: https://microbadger.com/images/hassioaddons/debian-base-armhf +[armhf-pulls-shield]: https://img.shields.io/docker/pulls/hassioaddons/debian-base-armhf.svg +[armhf-version-shield]: https://images.microbadger.com/badges/version/hassioaddons/debian-base-armhf.svg +[awesome-shield]: https://img.shields.io/badge/awesome%3F-yes-brightgreen.svg +[circleci-shield]: https://img.shields.io/circleci/project/github/hassio-addons/addon-debian-base.svg +[circleci]: https://circleci.com/gh/hassio-addons/addon-debian-base +[codeclimate-shield]: https://img.shields.io/codeclimate/github/hassio-addons/addon-debian-base.svg +[codeclimate]: https://codeclimate.com/github/hassio-addons/addon-debian-base +[contributors]: https://github.com/hassio-addons/addon-debian-base/graphs/contributors +[discord]: https://discord.gg/c5DvZ4e +[forums]: https://community.home-assistant.io/t/repository-community-hass-io-add-ons/24705?u=frenck +[frenck]: https://github.com/frenck +[i386-arch-shield]: https://img.shields.io/badge/architecture-i386-blue.svg +[i386-dockerhub]: https://hub.docker.com/r/hassioaddons/debian-base-i386 +[i386-layers-shield]: https://images.microbadger.com/badges/image/hassioaddons/debian-base-i386.svg +[i386-microbadger]: https://microbadger.com/images/hassioaddons/debian-base-i386 +[i386-pulls-shield]: https://img.shields.io/docker/pulls/hassioaddons/debian-base-i386.svg +[i386-version-shield]: https://images.microbadger.com/badges/version/hassioaddons/debian-base-i386.svg +[issue]: https://github.com/hassio-addons/addon-debian-base/issues +[keepchangelog]: http://keepachangelog.com/en/1.0.0/ +[label-schema]: http://label-schema.org/ +[license-shield]: https://img.shields.io/github/license/hassio-addons/addon-debian-base.svg +[maintenance-shield]: https://img.shields.io/maintenance/yes/2017.svg +[project-stage-shield]: https://img.shields.io/badge/Project%20Stage-Production%20Ready-brightgreen.svg +[reddit]: https://reddit.com/r/homeassistant +[repository]: https://github.com/hassio-addons/repository +[s6]: http://skarnet.org/software/s6/overview.html +[semver]: http://semver.org/spec/v2.0.0.html diff --git a/debian-base/Dockerfile b/debian-base/Dockerfile new file mode 100644 index 0000000..102f2c2 --- /dev/null +++ b/debian-base/Dockerfile @@ -0,0 +1,76 @@ +ARG BUILD_FROM=debian:stretch-slim +FROM ${BUILD_FROM} + +# Environment variables +ENV \ + HOME="/root" \ + LANG="C.UTF-8" \ + PS1="$(whoami)@$(hostname):$(pwd)$ " \ + S6_BEHAVIOUR_IF_STAGE2_FAILS=2 \ + S6_CMD_WAIT_FOR_SERVICES=1 \ + TERM="xterm" + +# Copy root filesystem +COPY rootfs / + +# Install base system +ARG BUILD_ARCH=amd64 +RUN \ + apt-get update \ + \ + && apt-get install -y --no-install-recommends \ + bash \ + ca-certificates \ + curl \ + udev \ + jq \ + tzdata \ + \ + && if [ "${BUILD_ARCH}" = "i386" ]; then S6_ARCH="x86"; else S6_ARCH="${BUILD_ARCH}"; fi \ + \ + && curl -L -s "https://github.com/just-containers/s6-overlay/releases/download/v1.20.0.0/s6-overlay-${S6_ARCH}.tar.gz" \ + | tar zxvf - -C / \ + \ + && mkdir -p \ + /etc/cont-finish.d \ + /etc/fix-attrs.d \ + \ + && \ + if [ "${BUILD_ARCH}" != "armhf" ]; then \ + rm -f /usr/bin/qemu-arm-static; \ + fi \ + \ + && \ + if [ "${BUILD_ARCH}" != "aarch64" ]; then \ + rm -f /usr/bin/qemu-aarch64-static; \ + fi \ + \ + && rm -f -r \ + /tmp/* \ + /var/lib/apt/lists/* + +# Entrypoint & CMD +ENTRYPOINT [ "/init" ] + +# Build arugments +ARG BUILD_DATE +ARG BUILD_REF +ARG BUILD_VERSION + +# Labels +LABEL \ + io.hass.name="Addon Debian base for ${BUILD_ARCH}" \ + io.hass.description="Community Hass.io Add-ons: ${BUILD_ARCH} Debian base image" \ + io.hass.arch="${BUILD_ARCH}" \ + io.hass.type="addon" \ + io.hass.version=${BUILD_VERSION} \ + maintainer="Franck Nijhof " \ + org.label-schema.description="Community Hass.io Add-ons: ${BUILD_ARCH} Debian base image" \ + org.label-schema.build-date=${BUILD_DATE} \ + org.label-schema.name="Addon Debian base for ${BUILD_ARCH}" \ + org.label-schema.schema-version="1.0" \ + org.label-schema.url="http://addons.community" \ + org.label-schema.usage="https://github.com/hassio-addons/addon-debian-base/blob/master/README.md" \ + org.label-schema.vcs-ref=${REF} \ + org.label-schema.vcs-url="https://github.com/hassio-addons/addon-debian-base" \ + org.label-schema.vendor="Franck Nijhof" diff --git a/debian-base/build.json b/debian-base/build.json new file mode 100644 index 0000000..21c007f --- /dev/null +++ b/debian-base/build.json @@ -0,0 +1,11 @@ +{ + "image": "hassioaddons/debian-base-{arch}", + "squash": true, + "build_from": { + "aarch64": "arm64v8/debian:stretch-slim", + "amd64": "debian:stretch-slim", + "armhf": "arm32v7/debian:stretch-slim", + "i386": "i386/debian:stretch-slim" + }, + "args": {} +} \ No newline at end of file diff --git a/debian-base/rootfs/etc/cont-init.d/00-banner.sh b/debian-base/rootfs/etc/cont-init.d/00-banner.sh new file mode 100644 index 0000000..a45fad3 --- /dev/null +++ b/debian-base/rootfs/etc/cont-init.d/00-banner.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +# ============================================================================== +# Community Hass.io Add-ons: Base Images +# Displays a simple add-on banner on startup +# ============================================================================== +# shellcheck source=base/rootfs/usr/lib/hassio-addons/base.sh +source /usr/lib/hassio-addons/base.sh + +if hass.api.supervisor.ping; then + echo '-----------------------------------------------------------' + echo " Hass.io Add-on: $(hass.addon.name) v$(hass.addon.version)" + echo '' + echo " $(hass.addon.description)" + echo '' + echo " From: $(hass.addon.repository)" + echo " By: $(hass.addon.maintainer)" + echo '-----------------------------------------------------------' +fi \ No newline at end of file diff --git a/debian-base/rootfs/etc/cont-init.d/01-log-level.sh b/debian-base/rootfs/etc/cont-init.d/01-log-level.sh new file mode 100644 index 0000000..2c8f5b6 --- /dev/null +++ b/debian-base/rootfs/etc/cont-init.d/01-log-level.sh @@ -0,0 +1,50 @@ +#!/usr/bin/with-contenv bash +# ============================================================================== +# Community Hass.io Add-ons: Base Images +# Sets the log level correctly +# ============================================================================== +# shellcheck source=base/rootfs/usr/lib/hassio-addons/base.sh +source /usr/lib/hassio-addons/base.sh + +declare log_level + +# Check if the log level configuration option exists +if hass.config.exists "log_level"; then + + # Find the matching LOG_LEVEL + case "$(hass.string.lower "$(hass.config.get 'log_level')")" in + all) + log_level="${LOG_LEVEL_ALL}" + ;; + trace) + log_level="${LOG_LEVEL_TRACE}" + ;; + debug) + log_level="${LOG_LEVEL_DEBUG}" + ;; + info) + log_level="${LOG_LEVEL_INFO}" + ;; + notice) + log_level="${LOG_LEVEL_NOTICE}" + ;; + warning) + log_level="${LOG_LEVEL_WARNING}" + ;; + error) + log_level="${LOG_LEVEL_ERROR}" + ;; + fatal) + log_level="${LOG_LEVEL_FATAL}" + ;; + off) + log_level="${LOG_LEVEL_OFF}" + ;; + *) + hass.die "Unknown log_level: ${log_level}" + esac + + # Save determined log level so S6 can pick it up later + echo "${log_level}" > /var/run/s6/container_environment/LOG_LEVEL + echo "Log level is set to ${LOG_LEVELS[$log_level]}" +fi diff --git a/debian-base/rootfs/etc/cont-init.d/02-updates.sh b/debian-base/rootfs/etc/cont-init.d/02-updates.sh new file mode 100644 index 0000000..e3b7452 --- /dev/null +++ b/debian-base/rootfs/etc/cont-init.d/02-updates.sh @@ -0,0 +1,18 @@ +#!/usr/bin/with-contenv bash +# ============================================================================== +# Community Hass.io Add-ons: Base Images +# Displays a notice when there is an update available for this add-on +# ============================================================================== +# shellcheck source=base/rootfs/usr/lib/hassio-addons/base.sh +source /usr/lib/hassio-addons/base.sh + +if hass.api.supervisor.ping; then + if hass.addon.update_available; then + hass.log.warning 'There is an update available for this add-on!' + hass.log.notice "Current installed version: $(hass.addon.version)" + hass.log.notice "Latest version: $(hass.addon.last_version)" + hass.log.info 'Please consider updating this add-on.' + else + hass.log.info 'You are running the latest version of this add-on' + fi +fi \ No newline at end of file diff --git a/debian-base/rootfs/etc/services.d/udevd/run b/debian-base/rootfs/etc/services.d/udevd/run new file mode 100644 index 0000000..c012267 --- /dev/null +++ b/debian-base/rootfs/etc/services.d/udevd/run @@ -0,0 +1,7 @@ +#!/usr/bin/execlineb -P +with-contenv +background -d { + s6-sleep 3 + udevadm trigger +} +/lib/systemd/systemd-udevd diff --git a/debian-base/rootfs/usr/bin/qemu-aarch64-static b/debian-base/rootfs/usr/bin/qemu-aarch64-static new file mode 100755 index 0000000..6dfae1c Binary files /dev/null and b/debian-base/rootfs/usr/bin/qemu-aarch64-static differ diff --git a/debian-base/rootfs/usr/bin/qemu-arm-static b/debian-base/rootfs/usr/bin/qemu-arm-static new file mode 100755 index 0000000..d6748f7 Binary files /dev/null and b/debian-base/rootfs/usr/bin/qemu-arm-static differ diff --git a/debian-base/rootfs/usr/lib/hassio-addons/base.sh b/debian-base/rootfs/usr/lib/hassio-addons/base.sh new file mode 100644 index 0000000..15b533e --- /dev/null +++ b/debian-base/rootfs/usr/lib/hassio-addons/base.sh @@ -0,0 +1,269 @@ +#!/usr/bin/env bash +# ============================================================================== +# Community Hass.io Add-ons: Bash functions library +# +# This is an bash function library for Hass.io add-ons. +# It contains a set of commonly used operations and can be used +# to be included in add-on scripts to reduce code duplication across add-ons. +# ============================================================================== +set -o errexit # Exit script when a command exits with non-zero status +set -o errtrace # Exit on error inside any functions or sub-shells +set -o nounset # Exit script on use of an undefined variable +set -o pipefail # Return exit status of the last command in the pipe that failed + +# ============================================================================== +# GLOBALS +# ============================================================================== +readonly EX_OK=0 # Successful termination +readonly EX_NOK=1 # Termination with errors + +# Stores the location of this library +readonly __LIB_DIR=$(dirname "${BASH_SOURCE[0]}") + +# ============================================================================== +# MODULES +# ============================================================================== +#shellcheck source=base/rootfs/usr/lib/hassio-addons/modules/addon.sh +source "${__LIB_DIR}/modules/addon.sh" +#shellcheck source=base/rootfs/usr/lib/hassio-addons/modules/api.sh +source "${__LIB_DIR}/modules/api.sh" +#shellcheck source=base/rootfs/usr/lib/hassio-addons/modules/config.sh +source "${__LIB_DIR}/modules/config.sh" +#shellcheck source=base/rootfs/usr/lib/hassio-addons/modules/jq.sh +source "${__LIB_DIR}/modules/jq.sh" +#shellcheck source=base/rootfs/usr/lib/hassio-addons/modules/log.sh +source "${__LIB_DIR}/modules/log.sh" +#shellcheck source=base/rootfs/usr/lib/hassio-addons/modules/string.sh +source "${__LIB_DIR}/modules/string.sh" + +# ============================================================================== +# MISC FUNCTIONS +# ============================================================================== + +# ------------------------------------------------------------------------------ +# Exit the script with an optional error messge +# +# Arguments: +# $1 Error message (optional) +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.die() { + local message=${1:-} + + hass.log.trace "${FUNCNAME[0]}:" "$@" + + if hass.has_value "${message}"; then + hass.log.fatal "${message}" + fi + + exit "${EX_NOK}" +} + +# ------------------------------------------------------------------------------ +# Exit the script when given value is false, with an optional error messge +# +# Arguments: +# $1 Value to check if false +# $2 Error message (optional) +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.die_if_false() { + local value=${1:-} + local message=${2:-} + + hass.log.trace "${FUNCNAME[0]}:" "$@" + + if hass.false "${value}"; then + hass.die "${message}" + fi +} + +# ------------------------------------------------------------------------------ +# Exit the script when given value is true, with an optional error messge +# +# Arguments: +# $1 Value to check if true +# $2 Error message (optional) +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.die_if_true() { + local value=${1:-} + local message=${2:-} + + hass.log.trace "${FUNCNAME[0]}:" "$@" + + if hass.true "${value}"; then + hass.die "${message}" + fi +} + +# ------------------------------------------------------------------------------ +# Check whether or not a directory exists +# +# Arguments: +# $1 Path to directory +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.directory_exists() { + local directory=${1} + + hass.log.trace "${FUNCNAME[0]}:" "$@" + + if [[ -d "${directory}" ]]; then + return "${EX_OK}" + fi + + return "${EX_NOK}" +} + +# ------------------------------------------------------------------------------ +# Check whether or not a file exists +# +# Arguments: +# $1 Path to file +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.file_exists() { + local file=${1} + + hass.log.trace "${FUNCNAME[0]}:" "$@" + + if [[ -f "${file}" ]]; then + return "${EX_OK}" + fi + + return "${EX_NOK}" +} + +# ------------------------------------------------------------------------------ +# Check whether or not a device exists +# +# Arguments: +# $1 Path to device +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.device_exists() { + local device=${1} + + hass.log.trace "${FUNCNAME[0]}:" "$@" + + if [[ -d "${device}" ]]; then + return "${EX_OK}" + fi + + return "${EX_NOK}" +} + +# ------------------------------------------------------------------------------ +# Checks if a give value is true +# +# Arguments: +# $1 value +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.true() { + local value=${1:-null} + + hass.log.trace "${FUNCNAME[0]}:" "$@" + + if [[ "${value}" = "true" ]]; then + return "${EX_OK}" + fi + + return "${EX_NOK}" +} + +# ------------------------------------------------------------------------------ +# Checks if a give value is false +# +# Arguments: +# $1 value +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.false() { + local value=${1:-null} + + hass.log.trace "${FUNCNAME[0]}:" "$@" + + if [[ "${value}" = "false" ]]; then + return "${EX_OK}" + fi + + return "${EX_NOK}" +} + +# ------------------------------------------------------------------------------ +# Runs a command, while supressing all output +# +# Arguments: +# $1 command +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.quietly() { + local command=$* + + hass.log.trace "${FUNCNAME[0]}:" "$@" + + ${command} > /dev/null 2>&1 +} + +# ------------------------------------------------------------------------------ +# Checks if a global variable is defined +# +# Arguments: +# $1 Name of the variable +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.defined() { + local variable=${1} + + hass.log.trace "${FUNCNAME[0]}:" "$@" + + [[ "${!variable-X}" = "${!variable-Y}" ]] +} + +# ------------------------------------------------------------------------------ +# Checks if a value has actual value +# +# Arguments: +# $1 Value +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.has_value() { + local value=${1} + + hass.log.trace "${FUNCNAME[0]}:" "$@" + + if [[ -n "${value}" ]]; then + return "${EX_OK}" + fi + + return "${EX_NOK}" +} + +# ------------------------------------------------------------------------------ +# Checks if we are currently running in debug mode, based on the log module +# +# Arguments: +# None +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.debug() { + if [[ "${LOG_LEVEL_DEBUG}" -gt "${LOG_LEVEL}" ]]; then + return "${EX_NOK}" + fi + + return "${EX_OK}" +} diff --git a/debian-base/rootfs/usr/lib/hassio-addons/modules/addon.sh b/debian-base/rootfs/usr/lib/hassio-addons/modules/addon.sh new file mode 100644 index 0000000..f3a5dee --- /dev/null +++ b/debian-base/rootfs/usr/lib/hassio-addons/modules/addon.sh @@ -0,0 +1,165 @@ +#!/usr/bin/env bash +# ============================================================================== +# Community Hass.io Add-ons: Bash functions library +# +# Provides functions for reading the add-on configuration +# ============================================================================== + +# ============================================================================== +# FUNCTIONS +# ============================================================================== + +# ------------------------------------------------------------------------------ +# Returns the name of the current running add-on +# +# Arguments: +# None +# Returns: +# Name of the current Add-on running +# ------------------------------------------------------------------------------ +hass.addon.name() { + hass.log.trace "${FUNCNAME[0]}" + hass.api.addons.info.name "$(hass.addon.slug)" +} + +# ------------------------------------------------------------------------------ +# Returns the slug of the current running add-on +# +# Arguments: +# None +# Returns: +# Slug of the current Add-on running +# ------------------------------------------------------------------------------ +hass.addon.slug() { + local hostname + hass.log.trace "${FUNCNAME[0]}" + hostname=$(hostname) + echo "${hostname/-/_}" +} + +# ------------------------------------------------------------------------------ +# Returns the description of the current running add-on +# +# Arguments: +# None +# Returns: +# Description of the current Add-on running +# ------------------------------------------------------------------------------ +hass.addon.description() { + hass.log.trace "${FUNCNAME[0]}" + hass.api.addons.info.description "$(hass.addon.slug)" +} + +# ------------------------------------------------------------------------------ +# Returns the name of the repository of the current running add-on +# +# Arguments: +# None +# Returns: +# Name of the repository +# ------------------------------------------------------------------------------ +hass.addon.repository() { + local slug + hass.log.trace "${FUNCNAME[0]}" + slug=$(hass.api.addons.info.repository "$(hass.addon.slug)") + hass.api.addons.repositories.info.name "${slug}" +} + +# ------------------------------------------------------------------------------ +# Returns the maintainer of the current running add-on +# +# Arguments: +# None +# Returns: +# Maintainer of the current Add-on running +# ------------------------------------------------------------------------------ +hass.addon.maintainer() { + local slug + hass.log.trace "${FUNCNAME[0]}" + slug=$(hass.api.addons.info.repository "$(hass.addon.slug)") + hass.api.addons.repositories.info.maintainer "${slug}" +} + +# ------------------------------------------------------------------------------ +# Returns the version of the current running add-on +# +# Arguments: +# None +# Returns: +# Version of the current Add-on running +# ------------------------------------------------------------------------------ +hass.addon.version() { + hass.api.addons.info.version "$(hass.addon.slug)" +} + +# ------------------------------------------------------------------------------ +# Returns the latest version of the current running add-on +# +# Arguments: +# None +# Returns: +# Latest version of the current Add-on running +# ------------------------------------------------------------------------------ +hass.addon.last_version() { + hass.api.addons.info.last_version "$(hass.addon.slug)" +} + +# ------------------------------------------------------------------------------ +# Restarts the current running add-on +# +# Arguments: +# None +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.addon.restart() { + hass.api.addons.restart "$(hass.addon.slug)" +} + +# ------------------------------------------------------------------------------ +# Stops the current running add-on +# +# Arguments: +# None +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.addon.stop() { + hass.api.addons.stop "$(hass.addon.slug)" +} + +# ------------------------------------------------------------------------------ +# Updates the current running add-on +# +# Arguments: +# None +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.addon.update() { + hass.api.addons.update "$(hass.addon.slug)" +} + +# ------------------------------------------------------------------------------ +# Rebuilds the current running add-on +# +# Arguments: +# None +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.addon.rebuild() { + hass.api.addons.rebuild "$(hass.addon.slug)" +} + +# ------------------------------------------------------------------------------ +# Check if there is an update available for the running add-on +# +# Arguments: +# None +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.addon.update_available() { + hass.api.addons.update_available "$(hass.addon.slug)" +} diff --git a/debian-base/rootfs/usr/lib/hassio-addons/modules/api.sh b/debian-base/rootfs/usr/lib/hassio-addons/modules/api.sh new file mode 100644 index 0000000..a4480e9 --- /dev/null +++ b/debian-base/rootfs/usr/lib/hassio-addons/modules/api.sh @@ -0,0 +1,112 @@ +#!/usr/bin/env bash +# ============================================================================== +# Community Hass.io Add-ons: Bash functions library +# +# Provides access to the API functions of Hass.io +# ============================================================================== + +# ============================================================================== +# GLOBALS +# ============================================================================== +readonly HASS_API_ENDPOINT='http://hassio' + +# ============================================================================== +# MODULES +# ============================================================================== +# shellcheck source=base/rootfs/usr/lib/hassio-addons/modules/api/addons.sh +source "${__LIB_DIR}/modules/api/addons.sh" +# shellcheck source=base/rootfs/usr/lib/hassio-addons/modules/api/homeassistant.sh +source "${__LIB_DIR}/modules/api/homeassistant.sh" +# shellcheck source=base/rootfs/usr/lib/hassio-addons/modules/api/host.sh +source "${__LIB_DIR}/modules/api/host.sh" +# shellcheck source=base/rootfs/usr/lib/hassio-addons/modules/api/network.sh +source "${__LIB_DIR}/modules/api/network.sh" +# shellcheck source=base/rootfs/usr/lib/hassio-addons/modules/api/snapshots.sh +source "${__LIB_DIR}/modules/api/snapshots.sh" +# shellcheck source=base/rootfs/usr/lib/hassio-addons/modules/api/supervisor.sh +source "${__LIB_DIR}/modules/api/supervisor.sh" + +# ============================================================================== +# FUNCTIONS +# ============================================================================== + +# ------------------------------------------------------------------------------ +# Makes a call to the Hass.io API +# +# Arguments: +# $1 HTTP Method (GET/POST) +# $2 API Resource requested +# $3 Whether or not this resource returns raw data instead of json (optional) +# $4 jq filter command (optional) +# Returns: +# Mixed content +# ------------------------------------------------------------------------------ +hass.api.call() { + local method=${1} + local resource=${2} + local raw=${3:-false} + local filter=${4:-} + local response + local status + local result + + hass.log.trace "${FUNCNAME[0]}" "$@" + + if ! response=$(curl --silent --show-error \ + --write-out '\n%{http_code}' --request "${method}" \ + "${HASS_API_ENDPOINT}${resource}" + ); then + hass.log.debug "${response}" + hass.log.error "Something went wrong contacting the API" + return "${EX_NOK}" + fi + + status=${response##*$'\n'} + response=${response%$status} + + hass.log.debug "Requested API resource: ${HASS_API_ENDPOINT}${resource}" + hass.log.debug "API HTTP Response code: ${status}" + hass.log.debug "API Response: ${response}" + + if [[ "${status}" -eq 401 ]]; then + hass.log.error "Unable to authenticate with the API, permission denied" + return "${EX_NOK}" + fi + + if [[ "${status}" -eq 404 ]]; then + hass.log.error "Requested resource ${resource} was not found" + return "${EX_NOK}" + fi + + if [[ "${status}" -eq 405 ]]; then + hass.log.error "Requested resource ${resource} was called using an" \ + "unallowed method." + return "${EX_NOK}" + fi + + if [[ $(hass.jq "${response}" ".result") = "error" ]]; then + hass.log.error "Got unexpected response from the API:" \ + "$(hass.jq "${response}" '.message // empty')" + return "${EX_NOK}" + fi + + if [[ "${status}" -ne 200 ]]; then + hass.log.error "Unknown HTTP error occured" + return "${EX_NOK}" + fi + + if [[ "${raw}" = "true" ]]; then + echo "${response}" + return "${EX_OK}" + fi + + result=$(hass.jq "${response}" 'if .data == {} then empty else .data end') + + if hass.has_value "${filter}"; then + hass.log.debug "Filtering response using: ${filter}" + result=$(hass.jq "${result}" "${filter}") + fi + + echo "${result}" + return "${EX_OK}" +} diff --git a/debian-base/rootfs/usr/lib/hassio-addons/modules/api/addons.sh b/debian-base/rootfs/usr/lib/hassio-addons/modules/api/addons.sh new file mode 100644 index 0000000..808df9d --- /dev/null +++ b/debian-base/rootfs/usr/lib/hassio-addons/modules/api/addons.sh @@ -0,0 +1,622 @@ +#!/usr/bin/env bash +# ============================================================================== +# Community Hass.io Add-ons: Bash functions library +# +# Provides access to the API functions of Hass.io: Addons +# ============================================================================== + +# ============================================================================== +# FUNCTIONS +# ============================================================================== + +# ------------------------------------------------------------------------------ +# List all available Hass.io addons +# +# Arguments: +# None +# Returns: +# JSON addon objects +# ------------------------------------------------------------------------------ +hass.api.addons.list() { + hass.log.trace "${FUNCNAME[0]}" "$@" + hass.api.call GET /addons false ".addons" +} + +# ------------------------------------------------------------------------------ +# List all installed Hass.io add-on repositories +# +# Arguments: +# None +# Returns: +# JSON repository objects +# ------------------------------------------------------------------------------ +hass.api.addons.repositories() { + hass.log.trace "${FUNCNAME[0]}" + hass.api.call GET /addons false ".repositories" +} + +# ------------------------------------------------------------------------------ +# Gives all available information for a repository +# +# Arguments: +# $1 Repository slug +# $2 jq Filter to apply on the result (optional) +# Returns: +# JSON repository object or the filtered result +# ------------------------------------------------------------------------------ +hass.api.addons.repositories.info() { + local repository=${1} + local filter=${2:-} + local repositories + local result + hass.log.trace "${FUNCNAME[0]}" "$@" + repositories=$(hass.api.addons.repositories) + result=$(hass.jq "${repositories}" \ + ".[] | select(.slug==\"${repository}\")") + + if hass.has_value "${filter}"; then + hass.log.debug "Filtering response using: ${filter}" + result=$(hass.jq "${result}" "${filter}") + fi + + echo "${result}" + return "${EX_OK}" +} + +# ------------------------------------------------------------------------------ +# Returns the name of a repository +# +# Arguments: +# $1 Repository slug +# Returns: +# Name of the repository +# ------------------------------------------------------------------------------ +hass.api.addons.repositories.info.name() { + local repository=${1} + hass.log.trace "${FUNCNAME[0]}" "$@" + hass.api.addons.repositories.info "${repository}" ".name" +} + +# ------------------------------------------------------------------------------ +# Returns the name of a repository +# +# Arguments: +# $1 Repository slug +# Returns: +# Name of the repository +# ------------------------------------------------------------------------------ +hass.api.addons.repositories.info.source() { + local repository=${1} + hass.log.trace "${FUNCNAME[0]}" "$@" + hass.api.addons.repositories.info "${repository}" ".source" +} + +# ------------------------------------------------------------------------------ +# Returns the URL of a repository +# +# Arguments: +# $1 Repository slug +# Returns: +# URL to the source of the repository +# ------------------------------------------------------------------------------ +hass.api.addons.repositories.info.url() { + local repository=${1} + hass.log.trace "${FUNCNAME[0]}" "$@" + hass.api.addons.repositories.info "${repository}" ".url" +} + +# ------------------------------------------------------------------------------ +# Returns the maintainer of a repository +# +# Arguments: +# $1 Repository slug +# Returns: +# Maintainer of the repository +# ------------------------------------------------------------------------------ +hass.api.addons.repositories.info.maintainer() { + local repository=${1} + hass.log.trace "${FUNCNAME[0]}" "$@" + hass.api.addons.repositories.info "${repository}" ".maintainer" +} + +# ------------------------------------------------------------------------------ +# Reload the list of addons and version from remote repositories +# +# Arguments: +# None +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.api.addons.reload() { + hass.log.trace "${FUNCNAME[0]}" + hass.api.call POST /addons/reload +} + +# ------------------------------------------------------------------------------ +# Gives all available information for an add-on +# +# Arguments: +# $1 Add-on slug +# $2 jq Filter to apply on the result (optional) +# Returns: +# JSON repository object or the filtered result +# ------------------------------------------------------------------------------ +hass.api.addons.info() { + local addon=${1} + local filter=${2:-} + hass.log.trace "${FUNCNAME[0]}" "$@" + hass.api.call GET "/addons/${addon}/info" false "${filter}" +} + +# ------------------------------------------------------------------------------ +# Returns the name of an add-on +# +# Arguments: +# $1 Add-on slug +# Returns: +# Name of the add-on +# ------------------------------------------------------------------------------ +hass.api.addons.info.name() { + local addon=${1} + hass.log.trace "${FUNCNAME[0]}" "$@" + hass.api.addons.info "${addon}" ".name" +} + +# ------------------------------------------------------------------------------ +# Returns the description of an add-on +# +# Arguments: +# $1 Add-on slug +# Returns: +# Description of the add-on +# ------------------------------------------------------------------------------ +hass.api.addons.info.description() { + local addon=${1} + hass.log.trace "${FUNCNAME[0]}" "$@" + hass.api.addons.info "${addon}" ".description" +} + +# ------------------------------------------------------------------------------ +# Returns the version of an add-on +# +# Arguments: +# $1 Add-on slug +# Returns: +# Version of the add-on +# ------------------------------------------------------------------------------ +hass.api.addons.info.version() { + local addon=${1} + hass.log.trace "${FUNCNAME[0]}" "$@" + hass.api.addons.info "${addon}" ".version" +} + +# ------------------------------------------------------------------------------ +# Returns whether or not auto update is enabled for this add-on +# +# Arguments: +# $1 Add-on slug +# Returns: +# Whether or not auto update is enabled for this add-on +# ------------------------------------------------------------------------------ +hass.api.addons.info.auto_update() { + local addon=${1} + hass.log.trace "${FUNCNAME[0]}" "$@" + hass.api.addons.info "${addon}" ".auto_update // false" +} + +# ------------------------------------------------------------------------------ +# Returns the repository slug of an add-on +# +# Arguments: +# $1 Add-on slug +# Returns: +# Respository slug of the add-on +# ------------------------------------------------------------------------------ +hass.api.addons.info.repository() { + local addon=${1} + hass.log.trace "${FUNCNAME[0]}" "$@" + hass.api.addons.info "${addon}" ".repository" +} + +# ------------------------------------------------------------------------------ +# Returns the latest version of an add-on +# +# Arguments: +# $1 Add-on slug +# Returns: +# Latest version of the add-on +# ------------------------------------------------------------------------------ +hass.api.addons.info.last_version() { + local addon=${1} + hass.log.trace "${FUNCNAME[0]}" "$@" + hass.api.addons.info "${addon}" ".last_version" +} + +# ------------------------------------------------------------------------------ +# Returns the current state of an add-on +# +# Arguments: +# $1 Add-on slug +# Returns: +# The current state of the add-on +# ------------------------------------------------------------------------------ +hass.api.addons.info.state() { + local addon=${1} + hass.log.trace "${FUNCNAME[0]}" "$@" + hass.api.addons.info "${addon}" ".state" +} + +# ------------------------------------------------------------------------------ +# Returns the current boot setting of this add-on +# +# Arguments: +# $1 Add-on slug +# Returns: +# Boot setting of the add-on +# ------------------------------------------------------------------------------ +hass.api.addons.info.boot() { + local addon=${1} + hass.log.trace "${FUNCNAME[0]}" "$@" + hass.api.addons.info "${addon}" ".boot" +} + +# ------------------------------------------------------------------------------ +# Returns the URL of an add-on +# +# Arguments: +# $1 Add-on slug +# Returns: +# URL of the add-on +# ------------------------------------------------------------------------------ +hass.api.addons.info.url() { + local addon=${1} + hass.log.trace "${FUNCNAME[0]}" "$@" + hass.api.addons.info "${addon}" ".url" +} + +# ------------------------------------------------------------------------------ +# Returns whether or not the add-on is running deattached +# +# Arguments: +# $1 Add-on slug +# Returns: +# Whether or not the add-on is running deattached +# ------------------------------------------------------------------------------ +hass.api.addons.info.detached() { + local addon=${1} + hass.log.trace "${FUNCNAME[0]}" "$@" + hass.api.addons.info "${addon}" ".detached // false" +} + +# ------------------------------------------------------------------------------ +# Returns whether or not this add-on is being build locally +# +# Arguments: +# $1 Add-on slug +# Returns: +# Whether or not this add-on is being build locally +# ------------------------------------------------------------------------------ +hass.api.addons.info.build() { + local addon=${1} + hass.log.trace "${FUNCNAME[0]}" "$@" + hass.api.addons.info "${addon}" ".build // false" +} + +# ------------------------------------------------------------------------------ +# Returns a list of ports which are exposed on the host network for this add-on +# +# Arguments: +# $1 Add-on slug +# Returns: +# List of network ports +# ------------------------------------------------------------------------------ +hass.api.addons.info.network() { + local addon=${1} + hass.log.trace "${FUNCNAME[0]}" "$@" + hass.api.addons.info "${addon}" ".network" +} + +# ------------------------------------------------------------------------------ +# Returns whether or not this add-on runs on the host network +# +# Arguments: +# $1 Add-on slug +# Returns: +# Whether or not this add-on is running on the host network +# ------------------------------------------------------------------------------ +hass.api.addons.info.host_network() { + local addon=${1} + hass.log.trace "${FUNCNAME[0]}" "$@" + hass.api.addons.info "${addon}" ".host_network // false" +} + +# ------------------------------------------------------------------------------ +# Returns the privileges the add-on has on to the hardware / system. +# +# Arguments: +# $1 Add-on slug +# Returns: +# List of privileges +# ------------------------------------------------------------------------------ +hass.api.addons.info.privileged() { + local addon=${1} + hass.log.trace "${FUNCNAME[0]}" "$@" + hass.api.addons.info "${addon}" ".privileged // empty" +} + +# ------------------------------------------------------------------------------ +# Returns a list devices made available to the add-on +# +# Arguments: +# $1 Add-on slug +# Returns: +# List of devices +# ------------------------------------------------------------------------------ +hass.api.addons.info.devices() { + local addon=${1} + hass.log.trace "${FUNCNAME[0]}" "$@" + hass.api.addons.info "${addon}" ".devices" +} + +# ------------------------------------------------------------------------------ +# Returns whether or not this add-on has a logo available +# +# Arguments: +# $1 Add-on slug +# Returns: +# Whether or not this add-on has a logo available +# ------------------------------------------------------------------------------ +hass.api.addons.info.logo() { + local addon=${1} + hass.log.trace "${FUNCNAME[0]}" "$@" + hass.api.addons.info "${addon}" ".logo // false" +} + +# ------------------------------------------------------------------------------ +# A URL for web interface of this add-on +# +# Arguments: +# $1 Add-on slug +# Returns: +# The webui URL of the add-on +# ------------------------------------------------------------------------------ +hass.api.addons.info.webui() { + local addon=${1} + hass.log.trace "${FUNCNAME[0]}" "$@" + hass.api.addons.info "${addon}" ".webui // empty" +} + +# ------------------------------------------------------------------------------ +# Returns whether or not this add-on can use the STDIN on the Hass.io API +# +# Arguments: +# $1 Add-on slug +# Returns: +# Whether or not this add-on can use the STDIN +# ------------------------------------------------------------------------------ +hass.api.addons.info.stdin() { + local addon=${1} + hass.log.trace "${FUNCNAME[0]}" "$@" + hass.api.addons.info "${addon}" ".stdin // false" +} + +# ------------------------------------------------------------------------------ +# Returns whether or not this add-on can access the Hass.io API +# +# Arguments: +# $1 Add-on slug +# Returns: +# Whether or not this add-on can access the Hass.io API +# ------------------------------------------------------------------------------ +hass.api.addons.info.hassio_api() { + local addon=${1} + hass.log.trace "${FUNCNAME[0]}" "$@" + hass.api.addons.info "${addon}" ".hassio_api // false" +} + +# ------------------------------------------------------------------------------ +# Returns whether or not this add-on can access the Home Assistant API +# +# Arguments: +# $1 Add-on slug +# Returns: +# Whether or not this add-on can access the Home Assistant API +# ------------------------------------------------------------------------------ +hass.api.addons.info.homeassistant_api() { + local addon=${1} + hass.log.trace "${FUNCNAME[0]}" "$@" + hass.api.addons.info "${addon}" ".homeassistant_api // false" +} + +# ------------------------------------------------------------------------------ +# Returns whether or not this add-on can access GPIO +# +# Arguments: +# $1 Add-on slug +# Returns: +# Whether or not this add-on can access GPIO +# ------------------------------------------------------------------------------ +hass.api.addons.info.gpio() { + local addon=${1} + hass.log.trace "${FUNCNAME[0]}" "$@" + hass.api.addons.info "${addon}" ".gpio // false" +} + +# ------------------------------------------------------------------------------ +# Returns whether or not this add-on can access an audio device +# +# Arguments: +# $1 Add-on slug +# Returns: +# Whether or not this add-on can access an audio device +# ------------------------------------------------------------------------------ +hass.api.addons.info.audio() { + local addon=${1} + hass.log.trace "${FUNCNAME[0]}" "$@" + hass.api.addons.info "${addon}" ".audio // false" +} + +# ------------------------------------------------------------------------------ +# Returns the available audio input device for an add-on +# +# Arguments: +# $1 Add-on slug +# Returns: +# the available audio input device for an add-on +# ------------------------------------------------------------------------------ +hass.api.addons.info.audio_input() { + local addon=${1} + hass.log.trace "${FUNCNAME[0]}" "$@" + hass.api.addons.info "${addon}" ".audio_input" +} + +# ------------------------------------------------------------------------------ +# Returns the available audio output device for an add-on +# +# Arguments: +# $1 Add-on slug +# Returns: +# the available audio output device for an add-on +# ------------------------------------------------------------------------------ +hass.api.addons.info.audio_output() { + local addon=${1} + hass.log.trace "${FUNCNAME[0]}" "$@" + hass.api.addons.info "${addon}" ".audio_output" +} + +# ------------------------------------------------------------------------------ +# Install an add-on onto your Hass.io instance +# +# Arguments: +# $1 Add-on slug +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.api.addons.install() { + local addon=${1} + hass.log.trace "${FUNCNAME[0]}" "$@" + hass.api.call POST "/addons/${addon}/install" +} + +# ------------------------------------------------------------------------------ +# Uninstall an add-on from your Hass.io instance +# +# Arguments: +# $1 Add-on slug +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.api.addons.uninstall() { + local addon=${1} + hass.log.trace "${FUNCNAME[0]}" "$@" + hass.api.call POST "/addons/${addon}/uninstall" +} + +# ------------------------------------------------------------------------------ +# Starts an add-on +# +# Arguments: +# $1 Add-on slug +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.api.addons.start() { + local addon=${1} + hass.log.trace "${FUNCNAME[0]}" "$@" + hass.api.call POST "/addons/${addon}/start" +} + +# ------------------------------------------------------------------------------ +# Stops an add-on +# +# Arguments: +# $1 Add-on slug +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.api.addons.stop() { + local addon=${1} + hass.log.trace "${FUNCNAME[0]}" "$@" + hass.api.call POST "/addons/${addon}/stop" +} + +# ------------------------------------------------------------------------------ +# Restarts an add-on +# +# Arguments: +# $1 Add-on slug +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.api.addons.restart() { + local addon=${1} + hass.log.trace "${FUNCNAME[0]}" "$@" + hass.api.call POST "/addons/${addon}/restart" +} + +# ------------------------------------------------------------------------------ +# Updates an add-on +# +# Arguments: +# $1 Add-on slug +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.api.addons.update() { + local addon=${1} + hass.log.trace "${FUNCNAME[0]}" "$@" + hass.api.call POST "/addons/${addon}/update" +} + +# ------------------------------------------------------------------------------ +# Rebuilds an add-on locally +# +# Arguments: +# $1 Add-on slug +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.api.addons.rebuild() { + local addon=${1} + hass.log.trace "${FUNCNAME[0]}" "$@" + hass.api.call POST "/addons/${addon}/rebuild" +} + +# ------------------------------------------------------------------------------ +# Returns the logs created by an add-on +# +# Arguments: +# $1 Add-on slug +# Returns: +# The logs in text format +# ------------------------------------------------------------------------------ +hass.api.addons.logs() { + local addon=${1} + hass.log.trace "${FUNCNAME[0]}" + hass.api.call GET "/addons/${addon}/logs" true +} + +# ------------------------------------------------------------------------------ +# Checks if there is an update available for an add-on +# +# Arguments: +# $1 Add-on slug +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.api.addons.update_available() { + local addon=${1} + local version + local last_version + + hass.log.trace "${FUNCNAME[0]}" + + version=$(hass.api.addons.info.version "${addon}") + last_version=$(hass.api.addons.info.last_version "${addon}") + + if [[ "${version}" = "${last_version}" ]]; then + return "${EX_NOK}" + fi + + return "${EX_OK}" +} diff --git a/debian-base/rootfs/usr/lib/hassio-addons/modules/api/homeassistant.sh b/debian-base/rootfs/usr/lib/hassio-addons/modules/api/homeassistant.sh new file mode 100644 index 0000000..d39b35b --- /dev/null +++ b/debian-base/rootfs/usr/lib/hassio-addons/modules/api/homeassistant.sh @@ -0,0 +1,219 @@ +#!/usr/bin/env bash +# ============================================================================== +# Community Hass.io Add-ons: Bash functions library +# +# Provides access to the API functions of Hass.io: Home Assistant +# ============================================================================== + +# ============================================================================== +# FUNCTIONS +# ============================================================================== + +# ------------------------------------------------------------------------------ +# List all available information about the Home Assistant instance +# +# Arguments: +# $1 jq Filter to apply on the result (optional) +# Returns: +# JSON object +# ------------------------------------------------------------------------------ +hass.api.homeassistant.info() { + local filter=${1:-} + hass.log.trace "${FUNCNAME[0]}" "$@" + hass.api.call GET /homeassistant/info false "${filter}" +} + +# ------------------------------------------------------------------------------ +# Returns the version of Home Assistant +# +# Arguments: +# None +# Returns: +# Version +# ------------------------------------------------------------------------------ +hass.api.homeassistant.info.version() { + hass.log.trace "${FUNCNAME[0]}" + hass.api.homeassistant.info ".version" +} + +# ------------------------------------------------------------------------------ +# Returns the latest version of Home Assistant +# +# Arguments: +# None +# Returns: +# Latest version +# ------------------------------------------------------------------------------ +hass.api.homeassistant.info.last_version() { + hass.log.trace "${FUNCNAME[0]}" + hass.api.homeassistant.info ".last_version" +} + +# ------------------------------------------------------------------------------ +# Returns the Docker image of Home Assistant +# +# Arguments: +# None +# Returns: +# Docker image +# ------------------------------------------------------------------------------ +hass.api.homeassistant.info.image() { + hass.log.trace "${FUNCNAME[0]}" + hass.api.homeassistant.info ".image" +} + +# ------------------------------------------------------------------------------ +# Returns a list of devices available to Home Assistant +# +# Arguments: +# None +# Returns: +# List of devices +# ------------------------------------------------------------------------------ +hass.api.homeassistant.info.devices() { + hass.log.trace "${FUNCNAME[0]}" + hass.api.homeassistant.info ".devices[]" +} + +# ------------------------------------------------------------------------------ +# Returns whether or not a custom version of Home Assistant is installed +# +# Arguments: +# None +# Returns: +# Whether or not a custom Home Assistant is installed +# ------------------------------------------------------------------------------ +hass.api.homeassistant.info.custom() { + hass.log.trace "${FUNCNAME[0]}" + hass.api.homeassistant.info ".custom // false" +} + +# ------------------------------------------------------------------------------ +# Returns whether or not Home Assistant starts at device boot +# +# Arguments: +# None +# Returns: +# Whether or not Home Assistant starts at boot +# ------------------------------------------------------------------------------ +hass.api.homeassistant.info.boot() { + hass.log.trace "${FUNCNAME[0]}" + hass.api.homeassistant.info ".boot // false" +} + +# ------------------------------------------------------------------------------ +# Returns the port number on which Home Assistant is running +# +# Arguments: +# None +# Returns: +# Port number +# ------------------------------------------------------------------------------ +hass.api.homeassistant.info.port() { + hass.log.trace "${FUNCNAME[0]}" + hass.api.homeassistant.info ".port" +} + +# ------------------------------------------------------------------------------ +# Returns whether or not Home Assistant is running on SSL +# +# Arguments: +# None +# Returns: +# Whether or not Home Assistant runs on SSL +# ------------------------------------------------------------------------------ +hass.api.homeassistant.info.ssl() { + hass.log.trace "${FUNCNAME[0]}" + hass.api.homeassistant.info ".ssl // false" +} + +# ------------------------------------------------------------------------------ +# Returns whether or not Home Assistant is monitored by Watchdog +# +# Arguments: +# None +# Returns: +# Whether or not Home Assistant is monitored by Watchdog +# ------------------------------------------------------------------------------ +hass.api.homeassistant.info.watchdog() { + hass.log.trace "${FUNCNAME[0]}" + hass.api.homeassistant.info ".watchdog // false" +} + +# ------------------------------------------------------------------------------ +# Updates Home Assistant to the latest version +# +# Arguments: +# None +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.api.homeassistant.update() { + hass.log.trace "${FUNCNAME[0]}" + hass.api.call POST /homeassistant/update +} + +# ------------------------------------------------------------------------------ +# Restarts Home Assistant +# +# Arguments: +# None +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.api.homeassistant.restart() { + hass.log.trace "${FUNCNAME[0]}" + hass.api.call POST /homeassistant/restart +} + +# ------------------------------------------------------------------------------ +# Stops Home Assistant +# +# Arguments: +# None +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.api.homeassistant.stop() { + hass.log.trace "${FUNCNAME[0]}" + hass.api.call POST /homeassistant/stop +} + +# ------------------------------------------------------------------------------ +# Starts Home Assistant +# +# Arguments: +# None +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.api.homeassistant.start() { + hass.log.trace "${FUNCNAME[0]}" + hass.api.call POST /homeassistant/start +} + +# ------------------------------------------------------------------------------ +# Checks/validates your Home Assistant configuration +# +# Arguments: +# None +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.api.homeassistant.check() { + hass.log.trace "${FUNCNAME[0]}" + hass.api.call POST /homeassistant/check +} + +# ------------------------------------------------------------------------------ +# Returns the logs created by Home Assistant +# +# Arguments: +# None +# Returns: +# The logs in text format +# ------------------------------------------------------------------------------ +hass.api.homeassistant.logs() { + hass.log.trace "${FUNCNAME[0]}" + hass.api.call GET /homeassistant/logs true +} diff --git a/debian-base/rootfs/usr/lib/hassio-addons/modules/api/host.sh b/debian-base/rootfs/usr/lib/hassio-addons/modules/api/host.sh new file mode 100644 index 0000000..2e9356a --- /dev/null +++ b/debian-base/rootfs/usr/lib/hassio-addons/modules/api/host.sh @@ -0,0 +1,207 @@ +#!/usr/bin/env bash +# ============================================================================== +# Community Hass.io Add-ons: Bash functions library +# +# Provides access to the API functions of Hass.io: Host +# ============================================================================== + +# ============================================================================== +# FUNCTIONS +# ============================================================================== + +# ------------------------------------------------------------------------------ +# List all available information about the host system +# +# Arguments: +# $1 jq Filter to apply on the result (optional) +# Returns: +# JSON object +# ------------------------------------------------------------------------------ +hass.api.host.info() { + local filter=${1:-} + hass.log.trace "${FUNCNAME[0]}" "$@" + hass.api.call GET /host/info false "${filter}" +} + +# ------------------------------------------------------------------------------ +# Returns the type of the host +# +# Arguments: +# None +# Returns: +# Type of host +# ------------------------------------------------------------------------------ +hass.api.host.info.type() { + hass.log.trace "${FUNCNAME[0]}" + hass.api.host.info ".type" +} + +# ------------------------------------------------------------------------------ +# Returns the version of the software running on the host +# +# Arguments: +# None +# Returns: +# Version +# ------------------------------------------------------------------------------ +hass.api.host.info.version() { + hass.log.trace "${FUNCNAME[0]}" + hass.api.host.info ".version" +} + +# ------------------------------------------------------------------------------ +# Returns the latest version of the software for the host +# +# Arguments: +# None +# Returns: +# Latest version +# ------------------------------------------------------------------------------ +hass.api.host.info.last_version() { + hass.log.trace "${FUNCNAME[0]}" + hass.api.host.info ".last_version" +} + +# ------------------------------------------------------------------------------ +# Returns a list of exposed features by the host +# +# Arguments: +# None +# Returns: +# List of features +# ------------------------------------------------------------------------------ +hass.api.host.info.features() { + hass.log.trace "${FUNCNAME[0]}" + hass.api.host.info ".features[]" +} + +# ------------------------------------------------------------------------------ +# Returns the hostname of the host system +# +# Arguments: +# None +# Returns: +# Hostname +# ------------------------------------------------------------------------------ +hass.api.host.info.hostname() { + hass.log.trace "${FUNCNAME[0]}" + hass.api.host.info ".hostname" +} + +# ------------------------------------------------------------------------------ +# Returns the OS of the host system +# +# Arguments: +# None +# Returns: +# OS +# ------------------------------------------------------------------------------ +hass.api.host.info.os() { + hass.log.trace "${FUNCNAME[0]}" + hass.api.host.info ".os" +} + +# ------------------------------------------------------------------------------ +# Returns a list of available hardware on the host system +# +# Arguments: +# None +# Returns: +# JSON object with hardware +# ------------------------------------------------------------------------------ +hass.api.host.hardware() { + local filter=${1:-} + hass.log.trace "${FUNCNAME[0]}" "$@" + hass.api.call GET /host/hardware false "${filter}" +} + +# ------------------------------------------------------------------------------ +# Returns a list of available serial devices on the host system +# +# Arguments: +# None +# Returns: +# JSON object +# ------------------------------------------------------------------------------ +hass.api.host.hardware.serial() { + hass.log.trace "${FUNCNAME[0]}" + hass.api.host.hardware ".serial[]" +} + +# ------------------------------------------------------------------------------ +# Returns a list of available input devices on the host system +# +# Arguments: +# None +# Returns: +# JSON object +# ------------------------------------------------------------------------------ +hass.api.host.hardware.input() { + hass.log.trace "${FUNCNAME[0]}" + hass.api.host.hardware ".input[]" +} + +# ------------------------------------------------------------------------------ +# Returns a list of available disk devices on the host system +# +# Arguments: +# None +# Returns: +# JSON object +# ------------------------------------------------------------------------------ +hass.api.host.hardware.disk() { + hass.log.trace "${FUNCNAME[0]}" + hass.api.host.hardware ".disk[]" +} + +# ------------------------------------------------------------------------------ +# Returns a list of available audio devices on the host system +# +# Arguments: +# None +# Returns: +# JSON object +# ------------------------------------------------------------------------------ +hass.api.host.hardware.audio() { + hass.log.trace "${FUNCNAME[0]}" + hass.api.host.hardware ".audio[]" +} + +# ------------------------------------------------------------------------------ +# Reboots the host system +# +# Arguments: +# None +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.api.host.reboot() { + hass.log.trace "${FUNCNAME[0]}" + hass.api.call POST /host/reboot +} + +# ------------------------------------------------------------------------------ +# Shuts down the host system +# +# Arguments: +# None +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.api.host.shutdown() { + hass.log.trace "${FUNCNAME[0]}" + hass.api.call POST /host/shutdown +} + +# ------------------------------------------------------------------------------ +# Updates the host system to the latest version +# +# Arguments: +# None +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.api.host.update() { + hass.log.trace "${FUNCNAME[0]}" + hass.api.call POST /host/update +} diff --git a/debian-base/rootfs/usr/lib/hassio-addons/modules/api/network.sh b/debian-base/rootfs/usr/lib/hassio-addons/modules/api/network.sh new file mode 100644 index 0000000..1849dd3 --- /dev/null +++ b/debian-base/rootfs/usr/lib/hassio-addons/modules/api/network.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash +# ============================================================================== +# Community Hass.io Add-ons: Bash functions library +# +# Provides access to the API functions of Hass.io: Network +# ============================================================================== + +# ============================================================================== +# FUNCTIONS +# ============================================================================== + +# ------------------------------------------------------------------------------ +# List all available information about the network +# +# Arguments: +# $1 jq Filter to apply on the result (optional) +# Returns: +# JSON object +# ------------------------------------------------------------------------------ +hass.api.network.info() { + local filter=${1:-} + hass.log.trace "${FUNCNAME[0]}" "$@" + hass.api.call GET /network/info false "${filter}" +} + +# ------------------------------------------------------------------------------ +# Returns the hostname +# +# Arguments: +# None +# Returns: +# Hostname +# ------------------------------------------------------------------------------ +hass.api.network.info.hostname() { + hass.log.trace "${FUNCNAME[0]}" + hass.api.host.info ".hostname" +} diff --git a/debian-base/rootfs/usr/lib/hassio-addons/modules/api/snapshots.sh b/debian-base/rootfs/usr/lib/hassio-addons/modules/api/snapshots.sh new file mode 100644 index 0000000..926b3f9 --- /dev/null +++ b/debian-base/rootfs/usr/lib/hassio-addons/modules/api/snapshots.sh @@ -0,0 +1,148 @@ +#!/usr/bin/env bash +# ============================================================================== +# Community Hass.io Add-ons: Bash functions library +# +# Provides access to the API functions of Hass.io: Snapshots +# ============================================================================== + +# ============================================================================== +# FUNCTIONS +# ============================================================================== + +# ------------------------------------------------------------------------------ +# List all available snapshots +# +# Arguments: +# None +# Returns: +# JSON object +# ------------------------------------------------------------------------------ +hass.api.snapshots.list() { + hass.log.trace "${FUNCNAME[0]}" + hass.api.call GET /snapshots false ".snapshots[]" +} + +# ------------------------------------------------------------------------------ +# Reload the lists of available snapshots +# +# Arguments: +# None +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.api.snapshots.reload() { + hass.log.trace "${FUNCNAME[0]}" + hass.api.call POST /snapshots/reload +} + +# ------------------------------------------------------------------------------ +# Create a new full snapshot +# +# Arguments: +# None +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.api.snapshots.new.full() { + hass.log.trace "${FUNCNAME[0]}" + hass.api.call POST /snapshots/new/full +} + +# ------------------------------------------------------------------------------ +# Create a new partial snapshot +# +# Arguments: +# None +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.api.snapshots.new.partial() { + hass.log.trace "${FUNCNAME[0]}" + hass.api.call POST /snapshots/new/partial +} + +# ------------------------------------------------------------------------------ +# List all available information about a snapshot +# +# Arguments: +# $1 Slug of the snapshot to get detailed information for +# $1 jq Filter to apply on the result (optional) +# Returns: +# JSON object +# ------------------------------------------------------------------------------ +hass.api.snapshots.info() { + local snapshot=${1} + local filter=${2:-} + hass.log.trace "${FUNCNAME[0]}" "$@" + hass.api.call GET "/snapshots/${snapshot}/info" false "${filter}" +} + +# ------------------------------------------------------------------------------ +# Returns the name of a snapshot +# +# Arguments: +# $1 Snapshot slug +# Returns: +# Name +# ------------------------------------------------------------------------------ +hass.api.snapshots.info.name() { + local snapshot=${1} + hass.log.trace "${FUNCNAME[0]}" "$@" + hass.api.snapshots.info "${snapshot}" ".name" +} + +# ------------------------------------------------------------------------------ +# Returns the date of a snapshot +# +# Arguments: +# $1 Snapshot slug +# Returns: +# Date +# ------------------------------------------------------------------------------ +hass.api.snapshots.info.date() { + local snapshot=${1} + hass.log.trace "${FUNCNAME[0]}" "$@" + hass.api.snapshots.info "${snapshot}" ".date" +} + +# ------------------------------------------------------------------------------ +# Removes a snapshot +# +# Arguments: +# $1 Snapshot slug +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.api.snapshots.remove() { + local snapshot=${1} + hass.log.trace "${FUNCNAME[0]}" "$@" + hass.api.call POST "/snapshots/${snapshot}/remove" +} + +# ------------------------------------------------------------------------------ +# Restores a full snapshot +# +# Arguments: +# $1 Snapshot slug +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.api.snapshots.restore.full() { + local snapshot=${1} + hass.log.trace "${FUNCNAME[0]}" "$@" + hass.api.call POST "/snapshots/${snapshot}/restore/full" +} + +# ------------------------------------------------------------------------------ +# Restores a partial snapshot +# +# Arguments: +# $1 Snapshot slug +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.api.snapshots.restore.partial() { + local snapshot=${1} + hass.log.trace "${FUNCNAME[0]}" "$@" + hass.api.call POST "/snapshots/${snapshot}/restore/partial" +} diff --git a/debian-base/rootfs/usr/lib/hassio-addons/modules/api/supervisor.sh b/debian-base/rootfs/usr/lib/hassio-addons/modules/api/supervisor.sh new file mode 100644 index 0000000..2428981 --- /dev/null +++ b/debian-base/rootfs/usr/lib/hassio-addons/modules/api/supervisor.sh @@ -0,0 +1,163 @@ +#!/usr/bin/env bash +# ============================================================================== +# Community Hass.io Add-ons: Bash functions library +# +# Provides access to the API functions of Hass.io: Supervisor +# ============================================================================== + +# ------------------------------------------------------------------------------ +# Check to see if the Supervisor is still alive +# +# Arguments: +# None +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.api.supervisor.ping() { + hass.log.trace "${FUNCNAME[0]}" + hass.api.call GET /supervisor/ping +} + +# ------------------------------------------------------------------------------ +# List all available information about the Supervisor +# +# Arguments: +# $1 jq Filter to apply on the result (optional) +# Returns: +# JSON object +# ------------------------------------------------------------------------------ +hass.api.supervisor.info() { + local filter=${1:-} + hass.log.trace "${FUNCNAME[0]}" "$@" + hass.api.call GET /supervisor/info false "${filter}" +} + +# ------------------------------------------------------------------------------ +# Returns the version of the Supervisor +# +# Arguments: +# None +# Returns: +# Version +# ------------------------------------------------------------------------------ +hass.api.supervisor.info.version() { + hass.log.trace "${FUNCNAME[0]}" + hass.api.supervisor.info ".version" +} + +# ------------------------------------------------------------------------------ +# Returns the latest version of the Supervisor +# +# Arguments: +# None +# Returns: +# Latest version +# ------------------------------------------------------------------------------ +hass.api.supervisor.info.last_version() { + hass.log.trace "${FUNCNAME[0]}" + hass.api.supervisor.info ".last_version" +} + +# ------------------------------------------------------------------------------ +# Returns whether or not the Supervisor is on the beta channel +# +# Arguments: +# None +# Returns: +# Whether or not the Supervisor is on the beta channel +# ------------------------------------------------------------------------------ +hass.api.supervisor.info.beta_channel() { + hass.log.trace "${FUNCNAME[0]}" + hass.api.supervisor.info ".beta_channel // false" +} + +# ------------------------------------------------------------------------------ +# Returns the architecture of the system +# +# Arguments: +# None +# Returns: +# Architecture +# ------------------------------------------------------------------------------ +hass.api.supervisor.info.arch() { + hass.log.trace "${FUNCNAME[0]}" + hass.api.supervisor.info ".arch" +} + +# ------------------------------------------------------------------------------ +# Returns the current timezone of the system +# +# Arguments: +# None +# Returns: +# Architecture +# ------------------------------------------------------------------------------ +hass.api.supervisor.info.timezone() { + hass.log.trace "${FUNCNAME[0]}" + hass.api.supervisor.info ".timezone" +} + +# ------------------------------------------------------------------------------ +# Returns a list of all current installed add-ons +# +# Arguments: +# None +# Returns: +# JSON object +# ------------------------------------------------------------------------------ +hass.api.supervisor.info.addons() { + hass.log.trace "${FUNCNAME[0]}" + hass.api.supervisor.info ".addons[]" +} + +# ------------------------------------------------------------------------------ +# Returns a list of all activated repositories +# +# Arguments: +# None +# Returns: +# List of URL's +# ------------------------------------------------------------------------------ +hass.api.supervisor.info.addons_repositories() { + hass.log.trace "${FUNCNAME[0]}" + hass.api.supervisor.info ".addons_repositories[]" +} + +# ------------------------------------------------------------------------------ +# Updates the Supervisor to the latest version +# +# Arguments: +# None +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.api.supervisor.update() { + hass.log.trace "${FUNCNAME[0]}" + hass.api.call POST /supervisor/update +} + +# ------------------------------------------------------------------------------ +# Reloads the Supervisor +# +# Arguments: +# None +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.api.supervisor.reload() { + hass.log.trace "${FUNCNAME[0]}" + hass.api.call POST /supervisor/reload +} + +# ------------------------------------------------------------------------------ +# Returns the logs created by the Supervisor +# +# Arguments: +# None +# Returns: +# The logs in text format +# ------------------------------------------------------------------------------ +hass.api.supervisor.logs() { + hass.log.trace "${FUNCNAME[0]}" + hass.api.call GET /supervisor/logs true +} diff --git a/debian-base/rootfs/usr/lib/hassio-addons/modules/config.sh b/debian-base/rootfs/usr/lib/hassio-addons/modules/config.sh new file mode 100644 index 0000000..612a8fa --- /dev/null +++ b/debian-base/rootfs/usr/lib/hassio-addons/modules/config.sh @@ -0,0 +1,147 @@ +#!/usr/bin/env bash +# ============================================================================== +# Community Hass.io Add-ons: Bash functions library +# +# Provides functions for reading the add-on configuration +# ============================================================================== + +# ============================================================================== +# GLOBALS +# ============================================================================== +readonly ADDON_CONFIG_PATH=/data/options.json + +# ============================================================================== +# FUNCTIONS +# ============================================================================== + +# ------------------------------------------------------------------------------ +# Fetches a configuration value from the add-on config file +# +# Arguments: +# $1 Key of the config option +# Returns: +# Value of the key in the configuration file +# ------------------------------------------------------------------------------ +hass.config.get() { + local key=${1} + hass.log.trace "${FUNCNAME[0]}:" "$@" + + if ! hass.config.exists "${key}"; then + return "${EX_OK}" + fi + + if hass.jq.is_string "${ADDON_CONFIG_PATH}" ".${key}"; then + hass.jq "${ADDON_CONFIG_PATH}" ".${key} // empty" + return "${EX_OK}" + fi + + if hass.jq.is_boolean "${ADDON_CONFIG_PATH}" ".${key}"; then + hass.jq "${ADDON_CONFIG_PATH}" ".${key} // false" + return "${EX_OK}" + fi + + if hass.jq.is_array "${ADDON_CONFIG_PATH}" ".${key}"; then + if hass.jq.has_value "${ADDON_CONFIG_PATH}" ".${key}"; then + hass.jq "${ADDON_CONFIG_PATH}" ".${key}[]" + fi + return "${EX_OK}" + fi + + if hass.jq.is_object "${ADDON_CONFIG_PATH}" ".${key}"; then + if hass.jq.has_value "${ADDON_CONFIG_PATH}" ".${key}"; then + hass.jq "${ADDON_CONFIG_PATH}" ".${key}{}" + fi + return "${EX_OK}" + fi + + if hass.jq.is_number "${ADDON_CONFIG_PATH}" ".${key}"; then + hass.jq "${ADDON_CONFIG_PATH}" ".${key}" + return "${EX_OK}" + fi + + return "${EX_NOK}" +} + +# ------------------------------------------------------------------------------ +# Checks if a configuration option exists in the config file +# +# Arguments: +# $1 Key of the config option +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.config.exists() { + local key=${1} + hass.log.trace "${FUNCNAME[0]}:" "$@" + + if ! hass.jq.exists "${ADDON_CONFIG_PATH}" ".${key}"; then + return "${EX_NOK}" + fi + + return "${EX_OK}" +} + +# ------------------------------------------------------------------------------ +# Checks if a configuration option has an actual value +# +# Arguments: +# $1 Key of the config option +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.config.has_value() { + local key=${1} + hass.log.trace "${FUNCNAME[0]}:" "$@" + + if ! hass.jq.has_value "${ADDON_CONFIG_PATH}" ".${key}"; then + return "${EX_NOK}" + fi + + return "${EX_OK}" +} + +# ------------------------------------------------------------------------------ +# Checks if a configuration option is true +# +# Arguments: +# $1 Key of the config option +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.config.true() { + local key=${1} + hass.log.trace "${FUNCNAME[0]}:" "$@" + + if ! hass.jq.is_boolean "${ADDON_CONFIG_PATH}" ".${key}"; then + return "${EX_NOK}" + fi + + if [[ $(hass.config.get "${key}") = "true" ]]; then + return "${EX_OK}" + fi + + return "${EX_NOK}" +} + +# ------------------------------------------------------------------------------ +# Checks if a configuration option is false +# +# Arguments: +# $1 Key of the config option +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.config.false() { + local key=${1} + hass.log.trace "${FUNCNAME[0]}:" "$@" + + if ! hass.jq.is_boolean "${ADDON_CONFIG_PATH}" ".${key}"; then + return "${EX_NOK}" + fi + + if [[ $(hass.config.get "${key}") = "false" ]]; then + return "${EX_OK}" + fi + + return "${EX_NOK}" +} diff --git a/debian-base/rootfs/usr/lib/hassio-addons/modules/jq.sh b/debian-base/rootfs/usr/lib/hassio-addons/modules/jq.sh new file mode 100644 index 0000000..6372eb3 --- /dev/null +++ b/debian-base/rootfs/usr/lib/hassio-addons/modules/jq.sh @@ -0,0 +1,193 @@ +#!/usr/bin/env bash +# ============================================================================== +# Community Hass.io Add-ons: Bash functions library +# +# Provides helper function to the jq json query tool +# ============================================================================== + +# ============================================================================== +# FUNCTIONS +# ============================================================================== + +# ------------------------------------------------------------------------------ +# Execute a json query +# +# Arguments: +# $1 JSON string or path to a JSON file +# $2 jq filter (optional) +# Returns: +# jq result +# ------------------------------------------------------------------------------ +hass.jq() { + local data=${1} + local filter=${2:-} + + hass.log.trace "${FUNCNAME[0]}:" "$@" + + if [[ -f "${data}" ]]; then + jq --raw-output "$filter" "${data}" + else + jq --raw-output "$filter" <<< "${data}" + fi +} + +# ------------------------------------------------------------------------------ +# Checks if variable exists (optionally after filtering) +# +# Arguments: +# $1 JSON string or path to a JSON file +# $2 jq filter (optional) +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.jq.exists() { + local data=${1} + local filter=${2:-} + + hass.log.trace "${FUNCNAME[0]}:" "$@" + + if [[ $(hass.jq "${data}" "${filter}") = "null" ]]; then + return "${EX_NOK}" + fi + + return "${EX_OK}" +} + +# ------------------------------------------------------------------------------ +# Checks if data exists (optionally after filtering) +# +# Arguments: +# $1 JSON string or path to a JSON file +# $2 jq filter (optional) +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.jq.has_value() { + local data=${1} + local filter=${2:-} + local value + + hass.log.trace "${FUNCNAME[0]}:" "$@" + + value=$(hass.jq "${data}" \ + "${filter} | if (. == {} or . == []) then empty else . end // empty") + + if ! hass.has_value "${value}"; then + return "${EX_NOK}" + fi + + return "${EX_OK}" +} + +# ------------------------------------------------------------------------------ +# Checks if resulting data is of a specific type +# +# Arguments: +# $1 JSON string or path to a JSON file +# $2 jq filter +# $3 type (boolean, string, number, array, object, null) +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.jq.is() { + local data=${1} + local filter=${2} + local type=${3} + local value + + hass.log.trace "${FUNCNAME[0]}:" "$@" + + value=$(hass.jq "${data}" \ + "${filter} | if type==\"${type}\" then true else false end") + + if [[ "${value}" = "false" ]]; then + return "${EX_NOK}" + fi + + return "${EX_OK}" +} + +# ------------------------------------------------------------------------------ +# Checks if resulting data is a boolean +# +# Arguments: +# $1 JSON string or path to a JSON file +# $2 jq filter (optional) +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.jq.is_boolean() { + local data=${1} + local filter=${2:-} + + hass.log.trace "${FUNCNAME[0]}:" "$@" + hass.jq.is "${data}" "${filter}" "boolean" +} + +# ------------------------------------------------------------------------------ +# Checks if resulting data is a string +# +# Arguments: +# $1 JSON string or path to a JSON file +# $2 jq filter (optional) +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.jq.is_string() { + local data=${1} + local filter=${2:-} + + hass.log.trace "${FUNCNAME[0]}:" "$@" + hass.jq.is "${data}" "${filter}" "string" +} + +# ------------------------------------------------------------------------------ +# Checks if resulting data is an object +# +# Arguments: +# $1 JSON string or path to a JSON file +# $2 jq filter (optional) +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.jq.is_object() { + local data=${1} + local filter=${2:-} + + hass.log.trace "${FUNCNAME[0]}:" "$@" + hass.jq.is "${data}" "${filter}" "object" +} + +# ------------------------------------------------------------------------------ +# Checks if resulting data is a number +# +# Arguments: +# $1 JSON string or path to a JSON file +# $2 jq filter (optional) +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.jq.is_number() { + local data=${1} + local filter=${2:-} + + hass.log.trace "${FUNCNAME[0]}:" "$@" + hass.jq.is "${data}" "${filter}" "number" +} + +# ------------------------------------------------------------------------------ +# Checks if resulting data is an array +# +# Arguments: +# $1 JSON string or path to a JSON file +# $2 jq filter (optional) +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.jq.is_array() { + local data=${1} + local filter=${2:-} + + hass.log.trace "${FUNCNAME[0]}:" "$@" + hass.jq.is "${data}" "${filter}" "array" +} diff --git a/debian-base/rootfs/usr/lib/hassio-addons/modules/log.sh b/debian-base/rootfs/usr/lib/hassio-addons/modules/log.sh new file mode 100644 index 0000000..3f8cf28 --- /dev/null +++ b/debian-base/rootfs/usr/lib/hassio-addons/modules/log.sh @@ -0,0 +1,163 @@ +#!/usr/bin/env bash +# ============================================================================== +# Community Hass.io Add-ons: Bash functions library +# +# Provides functions for simple logging +# ============================================================================== + +# ============================================================================== +# GLOBALS +# ============================================================================== +readonly LOG_LEVEL_ALL=8 +readonly LOG_LEVEL_DEBUG=6 +readonly LOG_LEVEL_ERROR=2 +readonly LOG_LEVEL_FATAL=1 +readonly LOG_LEVEL_INFO=5 +readonly LOG_LEVEL_NOTICE=4 +readonly LOG_LEVEL_OFF=0 +readonly LOG_LEVEL_TRACE=7 +readonly LOG_LEVEL_WARNING=3 +readonly -A LOG_LEVELS=( + [${LOG_LEVEL_OFF}]="OFF" + [${LOG_LEVEL_FATAL}]="FATAL" + [${LOG_LEVEL_ERROR}]="ERROR" + [${LOG_LEVEL_WARNING}]="WARNING" + [${LOG_LEVEL_NOTICE}]="NOTICE" + [${LOG_LEVEL_INFO}]="INFO" + [${LOG_LEVEL_DEBUG}]="DEBUG" + [${LOG_LEVEL_TRACE}]="TRACE" + [${LOG_LEVEL_ALL}]="ALL" +) +readonly LOG_FORMAT_DEFAULT="[{TIMESTAMP}] {LEVEL} ----> {MESSAGE}" +readonly LOG_TIMESTAMP_DEFAULT="%T%z" + +declare LOG_FORMAT=${LOG_FORMAT:-${LOG_FORMAT_DEFAULT}} +declare LOG_LEVEL=${LOG_LEVEL:-${LOG_LEVEL_INFO}} +declare LOG_TIMESTAMP_FORMAT=${LOG_TIMESTAMP_FORMAT:-${LOG_TIMESTAMP_DEFAULT}} + +# ============================================================================== +# FUNCTIONS +# ============================================================================== + +# ------------------------------------------------------------------------------ +# Log a message +# +# Arguments: +# $1 Log level +# $2 Message to display +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.log.log() { + local level=${1} + local message=${2} + local timestamp + local output + + if [[ "${level}" -gt "${LOG_LEVEL}" ]]; then + return "${EX_OK}" + fi + + timestamp=$(date +"${LOG_TIMESTAMP_FORMAT}") + + output="${LOG_FORMAT}" + output="${output//\{TIMESTAMP\}/${timestamp}}" + output="${output//\{MESSAGE\}/${message}}" + output="${output//\{LEVEL\}/${LOG_LEVELS[$level]}}" + + echo "${output}" >&2 + + return "${EX_OK}" +} + + +# ------------------------------------------------------------------------------ +# Log a message @ trace level +# +# Arguments: +# $* Message to display +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.log.trace() { + local message=$* + hass.log.log "${LOG_LEVEL_TRACE}" "${message}" +} + +# ------------------------------------------------------------------------------ +# Log a message @ debug level +# +# Arguments: +# $* Message to display +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.log.debug() { + local message=$* + hass.log.log "${LOG_LEVEL_DEBUG}" "${message}" +} + +# ------------------------------------------------------------------------------ +# Log a message @ info level +# +# Arguments: +# $* Message to display +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.log.info() { + local message=$* + hass.log.log "${LOG_LEVEL_INFO}" "${message}" +} + +# ------------------------------------------------------------------------------ +# Log a message @ notice level +# +# Arguments: +# $* Message to display +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.log.notice() { + local message=$* + hass.log.log "${LOG_LEVEL_NOTICE}" "${message}" +} + +# ------------------------------------------------------------------------------ +# Log a message @ warning level +# +# Arguments: +# $* Message to display +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.log.warning() { + local message=$* + hass.log.log "${LOG_LEVEL_WARNING}" "${message}" +} + +# ------------------------------------------------------------------------------ +# Log a message @ error level +# +# Arguments: +# $* Message to display +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.log.error() { + local message=$* + hass.log.log "${LOG_LEVEL_ERROR}" "${message}" +} + +# ------------------------------------------------------------------------------ +# Log a message @ fatal level +# +# Arguments: +# $* Message to display +# Returns: +# None +# ------------------------------------------------------------------------------ +hass.log.fatal() { + local message=$* + hass.log.log "${LOG_LEVEL_FATAL}" "${message}" +} diff --git a/debian-base/rootfs/usr/lib/hassio-addons/modules/string.sh b/debian-base/rootfs/usr/lib/hassio-addons/modules/string.sh new file mode 100644 index 0000000..bc6a9f5 --- /dev/null +++ b/debian-base/rootfs/usr/lib/hassio-addons/modules/string.sh @@ -0,0 +1,99 @@ +#!/usr/bin/env bash +# ============================================================================== +# Community Hass.io Add-ons: Bash functions library +# +# Provides string manupulation functions +# ============================================================================== + +# ============================================================================== +# FUNCTIONS +# ============================================================================== + +# ------------------------------------------------------------------------------ +# Converts a string to upper case +# +# Arguments: +# $1 String to convert +# Returns: +# Uppercase string +# ------------------------------------------------------------------------------ +hass.string.upper() { + local string="${1}" + hass.log.trace "${FUNCNAME[0]}:" "$@" + echo "${string^^}" +} + +# ------------------------------------------------------------------------------ +# Converts a string to lower case +# +# Arguments: +# $1 String to convert +# Returns: +# Lowercase string +# ------------------------------------------------------------------------------ +hass.string.lower() { + local string="${1}" + hass.log.trace "${FUNCNAME[0]}:" "$@" + echo "${string,,}" +} + +# ------------------------------------------------------------------------------ +# Returns the length of a string +# +# Arguments: +# $1 String to determine the length of +# Returns: +# Length of the string +# ------------------------------------------------------------------------------ +hass.string.length() { + local string="${1}" + hass.log.trace "${FUNCNAME[0]}:" "$@" + echo "${#string}" +} + +# ------------------------------------------------------------------------------ +# Replaces parts of the string with an other string +# +# Arguments: +# $1 String to make replacements in +# $2 String part to replace +# $3 String replacement +# Returns: +# The resulting string +# ------------------------------------------------------------------------------ +hass.string.replace() { + local string="${1}" + local needle="${2}" + local replacement="${3}" + hass.log.trace "${FUNCNAME[0]}:" "$@" + echo "${string//${needle}/${replacement}}" +} + +# ------------------------------------------------------------------------------ +# Returns a substring of a string +# +# stringZ=abcABC123ABCabc +# hass.string.substring "${stringZ}" 0 # abcABC123ABCabc +# hass.string.substring "${stringZ}" 1 # bcABC123ABCabc +# hass.string.substring "${stringZ}" 7 # 23ABCabc +# hass.string.substring "${stringZ}" 7 3 # 23AB +# +# Arguments: +# $1 String to return a substring off +# $2 Position to start +# $3 Length of the substring (optional) +# Returns: +# The resulting string +# ------------------------------------------------------------------------------ +hass.string.substring() { + local string="${1}" + local position="${2}" + local length="${3:-}" + hass.log.trace "${FUNCNAME[0]}:" "$@" + + if hass.has_value "${length}"; then + echo "${string:${position}:${length}}" + else + echo "${string:${position}}" + fi +}