From 92a061b12c1589dcb37dd6ae884bbbd15f6a6a11 Mon Sep 17 00:00:00 2001 From: Ben Kristinsson Date: Tue, 21 Mar 2023 19:58:45 +0100 Subject: [PATCH 01/10] a whole bunch of changes, home audio system (airconnect, audiobookshelf, owntone) lost of hass changes --- roles/airconnect/defaults/main.yml | 18 + roles/airconnect/handlers/main.yml | 11 + .../airconnect-linuxserver.io-image.yaml | 87 ++++ roles/airconnect/tasks/airconnect.yml | 56 +++ roles/airconnect/tasks/main.yml | 3 + roles/airconnect/templates/30-install.j2 | 5 + roles/airconnect/templates/airupnp.xml.j2 | 37 ++ roles/airconnect/templates/run.j2 | 3 + .../airconnect/templates/supervisord.conf.j2 | 25 + roles/audiobookshelf/handlers/main.yml | 6 + roles/audiobookshelf/tasks/audiobookshelf.yml | 81 +++ roles/audiobookshelf/tasks/main.yml | 3 + .../templates/02-audiobookshelf.conf.j2 | 32 ++ roles/hass/defaults/main.yml | 1 + roles/hass/files/agp-debian-gpg.asc | 52 ++ roles/hass/files/agp-debian-key.asc | 52 ++ roles/hass/files/templates.yaml | 124 ++++- roles/hass/handlers/main.yml | 22 +- roles/hass/tasks/hass.yml | 359 +++++++++---- roles/hass/templates/01-hass.conf.j2 | 144 ++++++ roles/hass/templates/01-hass.j2 | 74 --- roles/hass/templates/01-zwavejs.conf.j2 | 47 ++ roles/hass/templates/20-sdr.rules.j2 | 1 + .../automations-ansible-managed.yaml.j2 | 174 +++++++ roles/hass/templates/blackbox.connection.j2 | 38 ++ roles/hass/templates/blink1.yaml.j2 | 71 +++ roles/hass/templates/climate.yaml.j2 | 52 ++ roles/hass/templates/configuration.yaml.j2 | 293 +++++++++-- .../hass/templates/configuration.yaml.j2.save | 410 +++++++++++++++ .../templates/scripts-ansible-managed.yaml.j2 | 0 roles/hass/templates/secrets.yaml.j2 | 17 + roles/hass/templates/templates.yaml.j2 | 126 +++++ roles/home/meta/main.yml | 7 + roles/homeaudio/meta/main.yml | 7 + roles/owntone/handlers/main.yml | 12 + roles/owntone/tasks/main.yml | 4 + roles/owntone/tasks/owntone.yml | 145 ++++++ roles/owntone/templates/01-owntone.conf.j2 | 70 +++ roles/owntone/templates/owntone-cron.j2 | 7 + roles/owntone/templates/owntone-mathom.conf | 452 +++++++++++++++++ roles/owntone/templates/owntone.conf.j2 | 472 ++++++++++++++++++ roles/sensnet/meta/main.yml | 6 - roles/sensors/tasks/sensors.yml | 185 +++++++ roles/sudoisbot/meta/main.yml | 5 + 44 files changed, 3592 insertions(+), 204 deletions(-) create mode 100644 roles/airconnect/defaults/main.yml create mode 100644 roles/airconnect/handlers/main.yml create mode 100644 roles/airconnect/tasks/airconnect-linuxserver.io-image.yaml create mode 100644 roles/airconnect/tasks/airconnect.yml create mode 100644 roles/airconnect/tasks/main.yml create mode 100644 roles/airconnect/templates/30-install.j2 create mode 100644 roles/airconnect/templates/airupnp.xml.j2 create mode 100644 roles/airconnect/templates/run.j2 create mode 100644 roles/airconnect/templates/supervisord.conf.j2 create mode 100644 roles/audiobookshelf/handlers/main.yml create mode 100644 roles/audiobookshelf/tasks/audiobookshelf.yml create mode 100644 roles/audiobookshelf/tasks/main.yml create mode 100644 roles/audiobookshelf/templates/02-audiobookshelf.conf.j2 create mode 100644 roles/hass/files/agp-debian-gpg.asc create mode 100644 roles/hass/files/agp-debian-key.asc create mode 100644 roles/hass/templates/01-hass.conf.j2 delete mode 100644 roles/hass/templates/01-hass.j2 create mode 100644 roles/hass/templates/01-zwavejs.conf.j2 create mode 100644 roles/hass/templates/20-sdr.rules.j2 create mode 100644 roles/hass/templates/automations-ansible-managed.yaml.j2 create mode 100644 roles/hass/templates/blackbox.connection.j2 create mode 100644 roles/hass/templates/blink1.yaml.j2 create mode 100644 roles/hass/templates/climate.yaml.j2 create mode 100644 roles/hass/templates/configuration.yaml.j2.save create mode 100644 roles/hass/templates/scripts-ansible-managed.yaml.j2 create mode 100644 roles/hass/templates/templates.yaml.j2 create mode 100644 roles/home/meta/main.yml create mode 100644 roles/homeaudio/meta/main.yml create mode 100644 roles/owntone/handlers/main.yml create mode 100644 roles/owntone/tasks/main.yml create mode 100644 roles/owntone/tasks/owntone.yml create mode 100644 roles/owntone/templates/01-owntone.conf.j2 create mode 100644 roles/owntone/templates/owntone-cron.j2 create mode 100644 roles/owntone/templates/owntone-mathom.conf create mode 100644 roles/owntone/templates/owntone.conf.j2 create mode 100644 roles/sudoisbot/meta/main.yml diff --git a/roles/airconnect/defaults/main.yml b/roles/airconnect/defaults/main.yml new file mode 100644 index 0000000..d9a2267 --- /dev/null +++ b/roles/airconnect/defaults/main.yml @@ -0,0 +1,18 @@ +--- + +airconnect_dir: "/var/lib/airconnect" +airconnect_user: + name: airconnect + uid: 1337 +airconnect_group: + name: airconnect + gid: 1337 + +airconnect_upnp: [] +airconnect_containers: + # UPnP/Sonos + - prog: airupnp + state: started + # Chromecast + - prog: aircast + state: started diff --git a/roles/airconnect/handlers/main.yml b/roles/airconnect/handlers/main.yml new file mode 100644 index 0000000..9d28fe2 --- /dev/null +++ b/roles/airconnect/handlers/main.yml @@ -0,0 +1,11 @@ +--- + +- name: restart airconnect containers + docker_container: + name: airconnect-{{ item }} + state: "{{ item.state }}" + restart: item.state == 'started' + with_items: "{{ airconnect_containers }}" + when: + - airconnect_containers is not defined or not airconnect_containers.changed + - item.state == 'started' diff --git a/roles/airconnect/tasks/airconnect-linuxserver.io-image.yaml b/roles/airconnect/tasks/airconnect-linuxserver.io-image.yaml new file mode 100644 index 0000000..9a032bb --- /dev/null +++ b/roles/airconnect/tasks/airconnect-linuxserver.io-image.yaml @@ -0,0 +1,87 @@ +--- + +- name: create airconnect dir + file: + path: "{{ airconnect_dir }}" + state: directory + mode: "0755" + owner: hass + group: hass + tags: + - airconnect-dirs + +- name: airconnect config files + template: + src: "{{ item.name }}.j2" + dest: "{{ airconnect_dir }}/{{ item.name }}" + owner: "{{ item.owner | default(systemuserlist.hass.uid) }}" + group: "{{ item.group | default(systemuserlist.hass.gid) }}" + mode: "{{ item.mode }}" + notify: restart airconnect container + with_items: + - name: airupnp.xml + mode: "0644" + - name: run + mode: "0755" + - name: supervisord.conf + mode: "0644" + owner: "root" + group: "root" + # needed to nuke the script that would otherwise + # overwrite our sane supervisord.conf file + - name: 30-install + mode: "0755" + loop_control: + label: "{{ item.name }}" + tags: + - airconnect-config + - airconnect + +- name: start airconnect container + docker_container: + name: airconnect + hostname: airconnect + image: 1activegeek/airconnect + #user: "{{ systemuserlist.hass.uid }}:{{ systemuserlist.hass.gid }}" + detach: true + pull: true + auto_remove: false + restart_policy: "unless-stopped" + state: "{{ airconnect_container_state | default('started') }}" + network_mode: host + env: + # docker image uses the linuxserver base image and supervisor, + # setting 'user:' doesnt work because of all of the custom shit + # that the base image does: https://github.com/linuxserver/docker-baseimage-ubuntu/blob/bionic/root/etc/cont-init.d/10-adduser + # but this image doesnt follow that properly, and supervisor starts + # the airconnect processes as root, so we need to mount a custom + # supervisord.conf file instead + PUID: "{{ systemuserlist.hass.uid }}" + PGID: "{{ systemuserlist.hass.gid }}" + + # the image runs a script + # AIRUPNP_VAR: "-x /etc/airconnect/airupnp.xml" + # will create a reference config file for chromecast when found, + # but the defaults are fine, so no need to maintain a custom file + #AIRCAST_VAR: "-i /etc/airconnect/aircast.xml" + mounts: + # generate the reference config files by setting AIRUPNP_VAR and + # AIRCAST_VAR env to '-i $PATH' + - type: bind + source: "{{ airconnect_dir }}/airupnp.xml" + target: /etc/airupnp.xml + read_only: true + # need this to set uid/gid properly + - type: bind + source: "{{ airconnect_dir }}/supervisord.conf" + target: /etc/supervisord.conf + # needed to nuke the script that would otherwise overwrite our sane + # version of the supervisord.conf file when the container starts + - type: bind + source: "{{ airconnect_dir }}/30-install" + target: /etc/cont-init.d/30-install + tags: + - airconnect + - airconnect-container + - docker-containers + register: airconnect_container diff --git a/roles/airconnect/tasks/airconnect.yml b/roles/airconnect/tasks/airconnect.yml new file mode 100644 index 0000000..7480ec7 --- /dev/null +++ b/roles/airconnect/tasks/airconnect.yml @@ -0,0 +1,56 @@ +--- + +- name: create airconnect dir + file: + path: "{{ airconnect_dir }}" + state: directory + mode: "0755" + owner: "{{ owntone_user.uid }}" + group: "{{ owntone_group.gid }}" + tags: + - airconnect-dirs + +- name: airconnect config files + template: + src: "{{ item.name }}.j2" + dest: "{{ airconnect_dir }}/{{ item.name }}" + owner: "{{ owntone_user.uid }}" + group: "{{ owntone_group.gid }}" + mode: "{{ item.mode | default('0644') }}" + notify: restart airconnect containers + with_items: + - name: airupnp.xml + loop_control: + label: "{{ item.name }}" + tags: + - airconnect-config + - airconnect + +- name: "set up the airconnect containers" + docker_container: + name: airconnect-{{ item.prog }} + hostname: airconnect-{{ item.prog }} + image: git.sudo.is/ben/airconnect + detach: true + pull: true + auto_remove: false + restart_policy: "unless-stopped" + state: "{{ item.state | default('started') }}" + network_mode: host + user: "{{ owntone_user.uid }}:{{ owntone_group.gid }}" + env: + AIRCONNECT_PROG: "{{ item.prog }}" + AIRCONNECT_ARGS: "{{ item.args|default() }}" + mounts: + - type: bind + source: "{{ airconnect_dir }}/airupnp.xml" + target: /etc/airupnp.xml + read_only: true + tags: + - airconnect + - airconnect-container + - docker-containers + register: airconnect_containers + loop_control: + label: airconnect-{{ item.prog }} + with_items: "{{ airconnect_containers }}" diff --git a/roles/airconnect/tasks/main.yml b/roles/airconnect/tasks/main.yml new file mode 100644 index 0000000..bf08e23 --- /dev/null +++ b/roles/airconnect/tasks/main.yml @@ -0,0 +1,3 @@ +--- + - import_tasks: airconnect.yml + tags: airconnect diff --git a/roles/airconnect/templates/30-install.j2 b/roles/airconnect/templates/30-install.j2 new file mode 100644 index 0000000..26f3829 --- /dev/null +++ b/roles/airconnect/templates/30-install.j2 @@ -0,0 +1,5 @@ +#!/usr/bin/with-contenv bash + +echo "this file has been nuked, so our sane supervisord.conf file is not overwritten" + +exit 0 diff --git a/roles/airconnect/templates/airupnp.xml.j2 b/roles/airconnect/templates/airupnp.xml.j2 new file mode 100644 index 0000000..5b2b84e --- /dev/null +++ b/roles/airconnect/templates/airupnp.xml.j2 @@ -0,0 +1,37 @@ + + + + + http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=00;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=0d500000000000000000000000000000 + http-get:*:audio/wav:DLNA.ORG_OP=00;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=0d500000000000000000000000000000 + http-get:*:audio/flac:DLNA.ORG_OP=00;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=0d500000000000000000000000000000 + http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=00;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=0d500000000000000000000000000000 + + 1 + 100 + -1 + 1 + mp3:320 + 1 + 1 + + 0:1000 + 0 + + info + info + warn + info + -1 + 32 + ? + 0:0 + {% for item in airconnect_upnp -%} + + uuid:{{ item.local_uid }} + {{ item.name }} + {{ item.mac }} + {% if item.enabled|default(true) %}1{% else %}0{% endif %} + + {% endfor %} + diff --git a/roles/airconnect/templates/run.j2 b/roles/airconnect/templates/run.j2 new file mode 100644 index 0000000..63ceade --- /dev/null +++ b/roles/airconnect/templates/run.j2 @@ -0,0 +1,3 @@ +#!/usr/bin/with-contenv bash + +supervisord --configuration /etc/supervisord.conf diff --git a/roles/airconnect/templates/supervisord.conf.j2 b/roles/airconnect/templates/supervisord.conf.j2 new file mode 100644 index 0000000..b1abdee --- /dev/null +++ b/roles/airconnect/templates/supervisord.conf.j2 @@ -0,0 +1,25 @@ +[supervisord] +nodaemon=true + +# the base image of the airconnect image uses the env vars $PUID and $PGID +# to create a user, and those are set in the ansible role +# +# supervisord doesnt have an argument for group, uses the default +# group of the user instead. +# +# but supervisor itself needs to run as root because of +# asinine stuff that the linuxserver base image wants to do + +[program:airupnp] +user={{ systemuserlist.hass.uid }} +redirect_stderr=true +# the config file maintained in this ansible role, mounted in +# the docker_container task +command=/bin/airupnp-linux-x86_64 -x /etc/airupnp.xml +process_name = airupnp-linux-x86_64 + +[program:aircast] +user={{ systemuserlist.hass.uid }} +redirect_stderr=true +command=/bin/aircast-linux-x86_64 +process_name = aircast-linux-x86_64 diff --git a/roles/audiobookshelf/handlers/main.yml b/roles/audiobookshelf/handlers/main.yml new file mode 100644 index 0000000..edde81c --- /dev/null +++ b/roles/audiobookshelf/handlers/main.yml @@ -0,0 +1,6 @@ +--- + +- name: reload nginx + service: + name: nginx + state: reloaded diff --git a/roles/audiobookshelf/tasks/audiobookshelf.yml b/roles/audiobookshelf/tasks/audiobookshelf.yml new file mode 100644 index 0000000..72b995f --- /dev/null +++ b/roles/audiobookshelf/tasks/audiobookshelf.yml @@ -0,0 +1,81 @@ +--- + +- name: create dir structure + file: + path: "{{ audiobookshelf_path }}/{{ item.name }}" + mode: "{{ item.mode | default('0750') }}" + owner: "{{ audiobookshelf_user.uid }}" + group: "{{ audiobookshelf_group.gid }}" + tags: + - audiobookshelf-dirs + loop_control: + label: "{{ item.name }}" + with_items: + - name: '' + - name: audiobooks + - name: config + - name: metadata + - name: podcasts + +- name: install certs + copy: + src: "/usr/local/etc/letsencrypt/live/{{ item }}" + dest: "/usr/local/etc/certs/" + owner: root + group: root + mode: 0755 + tags: + - letsencrypt-certs + notify: reload nginx + vars: + prediff_cmd: echo + with_items: + - "{{ audiobookshelf_url }}" + +- name: template nginx vhost + template: + src: 02-audiobookshelf.conf.j2 + dest: /etc/nginx/sites-enabled/02-audiobookshelf.conf + owner: root + group: root + mode: 0644 + tags: + - nginx + - audiobookshelf-nginx + notify: reload nginx + +- name: start audiobookshelf container + docker_container: + name: audiobookshelf + image: ghcr.io/advplyr/audiobookshelf:latest + detach: true + pull: true + restart_policy: no + state: started + container_default_behavior: compatibility + networks_cli_compatible: false + network_mode: bridgewithdns + networks: + - name: bridgewithdns + ipv4_address: "{{ bridgewithdns.audiobookshelf }}" + ports: + - "10.102.47.146:13378:80" + env: + AUDIOBOOKSHELF_UID: "{{ audiobookshelf_user.uid }}" + AUDIOBOOKSHELF_GID: "{{ audiobookshelf_group.gid }}" + mounts: + - type: bind + source: "{{ audiobookshelf_path }}/audiobooks" + target: /audiobooks + - type: bind + source: "{{ audiobookshelf_path }}/podcasts" + target: /podcasts + - type: bind + source: "{{ audiobookshelf_path }}/config" + target: /config + - type: bind + source: "{{ audiobookshelf_path }}/metadata" + target: /metadata + tags: + - audiobookshelf-container + - docker-containers diff --git a/roles/audiobookshelf/tasks/main.yml b/roles/audiobookshelf/tasks/main.yml new file mode 100644 index 0000000..07e93a3 --- /dev/null +++ b/roles/audiobookshelf/tasks/main.yml @@ -0,0 +1,3 @@ +--- + - import_tasks: audiobookshelf.yml + tags: audiobookshelf diff --git a/roles/audiobookshelf/templates/02-audiobookshelf.conf.j2 b/roles/audiobookshelf/templates/02-audiobookshelf.conf.j2 new file mode 100644 index 0000000..ab3f2bc --- /dev/null +++ b/roles/audiobookshelf/templates/02-audiobookshelf.conf.j2 @@ -0,0 +1,32 @@ +server { + listen 443 ssl http2; + include listen-proxy-protocol.conf; + + {% if inventory_hostname in wg_clients -%} + listen {{ wg_clients[inventory_hostname].ip }}:443 ssl http2; + {% endif -%} + + server_name {{ audiobookshelf_url }}; + + access_log /var/log/nginx/access_{{ audiobookshelf_url }}.log main; + error_log /var/log/nginx/error_{{ audiobookshelf_url }}.log warn; + + ssl_certificate /usr/local/etc/certs/{{ audiobookshelf_url }}/fullchain.pem; + ssl_certificate_key /usr/local/etc/certs/{{ audiobookshelf_url }}/privkey.pem; + + include /etc/nginx/authelia_internal.conf; + + location / { + include /etc/nginx/require_auth.conf; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Host $host; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + + proxy_http_version 1.1; + + proxy_pass http://{{ bridgewithdns.audiobookshelf }}:80; + proxy_redirect http:// https://; + } +} diff --git a/roles/hass/defaults/main.yml b/roles/hass/defaults/main.yml index ed97d53..551bba9 100644 --- a/roles/hass/defaults/main.yml +++ b/roles/hass/defaults/main.yml @@ -1 +1,2 @@ --- +blink1_enabled: false diff --git a/roles/hass/files/agp-debian-gpg.asc b/roles/hass/files/agp-debian-gpg.asc new file mode 100644 index 0000000..41903d6 --- /dev/null +++ b/roles/hass/files/agp-debian-gpg.asc @@ -0,0 +1,52 @@ +-----BEGIN PGP ARMORED FILE----- +Comment: Use "gpg --dearmor" for unpacking + +mQINBFfzkl4BEADR7mEkR2iHHBmMbUHdpVkvtmxZCh6Akc8IgHUY9frCVoQl3aLd +CWIK8MHDE0U35LwyFI6VuB8kL1uSuSLmMcmoEWnfJuMzQJlxDXVSKQvc9ECCd9ui +E26n4UKzK3dHJ6pat/MEAhyJ9BeDOYbwU3588izzDZdRbcyNIu9TTmJf+zcU8wYQ +vG5RAKLliS8qKgPYqk1vksQfHF8AZLmMwLC6EFNBGUhB+GDC7RVfGdk14MYZuJ/R +W61FMZOCJYOw8CkFWJZ2J35Q10U0vmfL+OuwE0Q2WpAoZh8XlywMa8EyPBJnwDdO +Z8HJrFJxbOnjpbN+pE+pSxxlYf4IBvCmAjWmbzVlALa2fgjr9YbyPSF7jTRLKTUb +6SzV0/UJqPMgi6l1rvwf/baaxHrEhyNX96JhBxo/M9ZFlAqvJjJo1evZ9cSZwp5v +mb9uM9qDzIWEAMbAcKeUPhg2vnKC44e4s3azElRXJlbxFHdpfxLfAzT8dWjfajzA +iVQVPj+48bFKOUbQBPLBkwOtdd+LsONkLOrqv+A2qoOES9DmXLF4N9hAbBcIfwwe +H56ANy67h5WCHbWMpPot8e0vaMhjM1uMmnPbZ82NoPhiaKjrQIv19BwlNzhQZ4+r +O/uGTP5MKqh8ECELwcYhgjh3QnLP4PVro34hZxnA8YL6AWg4uR/j9MMu+wARAQAB +tEBBRyBQcm9qZWN0cyBEZWJpYW4gUGFja2FnZSBTaWduaW5nIEtleSA8c3VwcG9y +dEBhZy1wcm9qZWN0cy5jb20+iQI3BBMBCAAhBQJX85JeAhsDBQsJCAcCBhUICQoL +AgQWAgMBAh4BAheAAAoJEPdARsMW2Pn1I/kP/RnTLjJcbf+ZvQokdjIo9JQiGOal +xdKLZ4QfDaukgxdUR1zS85ZsHddXQ/CxwvJ0fjdGkrx82Si6CAL5lTqv8X+8UCnx +iqNiOqIuEft29GN7YP2cPU2N7HqDrtOd9ZLm2AObxUOK33MblAuK5Gvp96f7qZ0J +EKdCSVtyM1sK4mwDM3Pjy/UsJcqiVYafitN/KQy8hPmz98xuNKaMjjxV7AYokuqK +64UGn5bLL3YwfgVPokl/57sSpfbKGs0pH6+HyiKu6ouKL3ool3tk5O9tFcPXE52Y +fE8iv6uyH2YCLYH3E7SlM+PEFTRHLXLDF4herwkPMvzRBS0au4FW5QyFWGbpfoDt +JF6qu9+YLe6144fPLnO0T7kKSLZtc/QmjNClM1cOwmHTdcCGveiqxWDZpt6qhzo7 +pnbJ5X4B8e1Gc+8AuXyITIu/uxQBrq1J0dpBdciYySxDr2hmK/paV9+ePofDq/KM +mwtp88M0H54cj/QQXWLhJVfGPxWuozVAhTg+QAwYwdXrY/dvRV+Aw5LTVEpfdyqL +M1mnDbe1HwxcZZxeuvImeV6rWmql4BjyMZkK8jArG/TnQFLKlug4ymestRqqDQxR +Asb9llRmp0UfkN1WheIQGxKA5wIC9cmRNWmj33gE5gc8NI8dCqxbqz6p2exGMotQ +qOO1vqpUbRPAsTnsuQINBFfzkl4BEADJEXKJB/8aBt0w7AVbYPyb4OojcxApwsKS +GyV12OAyWdfI5bq2vMS2KPUq1kE5dHF1c0VNqCqfFeQ7kpq0lakIfyBLyvX9Veka +JlHysGI8KzQQVJdZcCmnARL2ryvIFDbLVViXo/dQ50EtB87D8tYdQiWwrBsVpcqN +bBHkGq/dkivH5TNMUYRZzByRfi0Smis5aVn6nTOYgxb59y+/xoxf2ym9Y/1yINmf +7AJKVa2E8o9oNwnFZAzyBlJtz/i5c6Le+znp4Ubj3bo/PaSmT3gql/JiZ5+uOeHd +L0LCdyjLXHIQ1Jn6Vyu13VI0gLGIsgAjodsc7M4EpkwsOTVvCLwHvLRkdkVfVODa +R3roN8SQXTGgBgsod30gtpYY95UUIPcSUDtrt16YvPK9kv5yWFZuxTP0EdA/yxDz +9D/6z0opYmn1yAzEW763t9axJdfhsaQHCTpHRlzolCGNkosiGJaUkNknpndE+TAL +5oEEuEV8lgsbWQzkWZU9VjkgUXHQkU7OIELsO1UrgqL7lCRFyT2FWRImzZTcTESk +f9dKFYbn9uUDB4Fz1OkzrNS8C8drNiEPyNxID05SF/niBrRN+AgdakW5c1pEkd33 +lwsgZUCn+0hbPkuqsdwuErLcAjiJ5DeyVpqXCUzJPP1S+o3Hy+VgYeIy5TG8k5kn +5G8i7rjtFQARAQABiQIfBBgBCAAJBQJX85JeAhsMAAoJEPdARsMW2Pn1Zp4P/3ck +0a7WnpsN9CDFBIR0pM9i77cYhDfDp6EsMzmWovDlp8rIkLYQW2WANFPZB+lrArqh +fgZcM8a4vK/1iaX/AhoIzGzFoGn3bt3pnYe4OZ5Gxt/MD34F80Y7SVsFiob+ZlRO +QNbsPOrOm5fcXzlEGrQs/4H+WgZkOZqxZ5ydO75Qf2VFUVGpFSA6KQdGUWh8o6Fi +VnnfIgZKYuc1TKqtF+4SHpFgiPqvJSBEaguDy1ty7fm1tLO4zZHIDPW8u1SAWfG/ +SKn6wcZlkrBJ79pKyEYXs7ZQm6qA0L0FzPJsRwWFrtJE4VzNQDIp999qmY0IYCk5 +s34Nwbqtdk8P0G5pl1IufCXv/+6hx+0ahxQUW8cP/wYDc+5MY1Y8m+wHEVaXRVyl +IVZL/UdoHwVbQoIvnUCXY2hWAkcStyyZU5G4Y+x5v3WuERdAexEPmxr7NcCey5rO +jhv7UwyrtqggTzjUryf+y/HNRNgQNyvi6nng2vNe7YS1iA/Qu55jr98IWO5ybP/B +THylTg4m+z2Ni/U0vG35Vbc9yU8ZHkqaHyg1K1JqEC/U8p/YXBny30NUQEf3wFQZ +y3jte5Sm6up2fWZIdeitjKlP8hvuXS/H9KTxj+xJHm6jRnZCrThtwKWDoKBOq1OH +8f9kzGZAZKvkJEV6DtTfsgGjCc+h99ce+A5kjmEl +=P2ir +-----END PGP ARMORED FILE----- diff --git a/roles/hass/files/agp-debian-key.asc b/roles/hass/files/agp-debian-key.asc new file mode 100644 index 0000000..41903d6 --- /dev/null +++ b/roles/hass/files/agp-debian-key.asc @@ -0,0 +1,52 @@ +-----BEGIN PGP ARMORED FILE----- +Comment: Use "gpg --dearmor" for unpacking + +mQINBFfzkl4BEADR7mEkR2iHHBmMbUHdpVkvtmxZCh6Akc8IgHUY9frCVoQl3aLd +CWIK8MHDE0U35LwyFI6VuB8kL1uSuSLmMcmoEWnfJuMzQJlxDXVSKQvc9ECCd9ui +E26n4UKzK3dHJ6pat/MEAhyJ9BeDOYbwU3588izzDZdRbcyNIu9TTmJf+zcU8wYQ +vG5RAKLliS8qKgPYqk1vksQfHF8AZLmMwLC6EFNBGUhB+GDC7RVfGdk14MYZuJ/R +W61FMZOCJYOw8CkFWJZ2J35Q10U0vmfL+OuwE0Q2WpAoZh8XlywMa8EyPBJnwDdO +Z8HJrFJxbOnjpbN+pE+pSxxlYf4IBvCmAjWmbzVlALa2fgjr9YbyPSF7jTRLKTUb +6SzV0/UJqPMgi6l1rvwf/baaxHrEhyNX96JhBxo/M9ZFlAqvJjJo1evZ9cSZwp5v +mb9uM9qDzIWEAMbAcKeUPhg2vnKC44e4s3azElRXJlbxFHdpfxLfAzT8dWjfajzA +iVQVPj+48bFKOUbQBPLBkwOtdd+LsONkLOrqv+A2qoOES9DmXLF4N9hAbBcIfwwe +H56ANy67h5WCHbWMpPot8e0vaMhjM1uMmnPbZ82NoPhiaKjrQIv19BwlNzhQZ4+r +O/uGTP5MKqh8ECELwcYhgjh3QnLP4PVro34hZxnA8YL6AWg4uR/j9MMu+wARAQAB +tEBBRyBQcm9qZWN0cyBEZWJpYW4gUGFja2FnZSBTaWduaW5nIEtleSA8c3VwcG9y +dEBhZy1wcm9qZWN0cy5jb20+iQI3BBMBCAAhBQJX85JeAhsDBQsJCAcCBhUICQoL +AgQWAgMBAh4BAheAAAoJEPdARsMW2Pn1I/kP/RnTLjJcbf+ZvQokdjIo9JQiGOal +xdKLZ4QfDaukgxdUR1zS85ZsHddXQ/CxwvJ0fjdGkrx82Si6CAL5lTqv8X+8UCnx +iqNiOqIuEft29GN7YP2cPU2N7HqDrtOd9ZLm2AObxUOK33MblAuK5Gvp96f7qZ0J +EKdCSVtyM1sK4mwDM3Pjy/UsJcqiVYafitN/KQy8hPmz98xuNKaMjjxV7AYokuqK +64UGn5bLL3YwfgVPokl/57sSpfbKGs0pH6+HyiKu6ouKL3ool3tk5O9tFcPXE52Y +fE8iv6uyH2YCLYH3E7SlM+PEFTRHLXLDF4herwkPMvzRBS0au4FW5QyFWGbpfoDt +JF6qu9+YLe6144fPLnO0T7kKSLZtc/QmjNClM1cOwmHTdcCGveiqxWDZpt6qhzo7 +pnbJ5X4B8e1Gc+8AuXyITIu/uxQBrq1J0dpBdciYySxDr2hmK/paV9+ePofDq/KM +mwtp88M0H54cj/QQXWLhJVfGPxWuozVAhTg+QAwYwdXrY/dvRV+Aw5LTVEpfdyqL +M1mnDbe1HwxcZZxeuvImeV6rWmql4BjyMZkK8jArG/TnQFLKlug4ymestRqqDQxR +Asb9llRmp0UfkN1WheIQGxKA5wIC9cmRNWmj33gE5gc8NI8dCqxbqz6p2exGMotQ +qOO1vqpUbRPAsTnsuQINBFfzkl4BEADJEXKJB/8aBt0w7AVbYPyb4OojcxApwsKS +GyV12OAyWdfI5bq2vMS2KPUq1kE5dHF1c0VNqCqfFeQ7kpq0lakIfyBLyvX9Veka +JlHysGI8KzQQVJdZcCmnARL2ryvIFDbLVViXo/dQ50EtB87D8tYdQiWwrBsVpcqN +bBHkGq/dkivH5TNMUYRZzByRfi0Smis5aVn6nTOYgxb59y+/xoxf2ym9Y/1yINmf +7AJKVa2E8o9oNwnFZAzyBlJtz/i5c6Le+znp4Ubj3bo/PaSmT3gql/JiZ5+uOeHd +L0LCdyjLXHIQ1Jn6Vyu13VI0gLGIsgAjodsc7M4EpkwsOTVvCLwHvLRkdkVfVODa +R3roN8SQXTGgBgsod30gtpYY95UUIPcSUDtrt16YvPK9kv5yWFZuxTP0EdA/yxDz +9D/6z0opYmn1yAzEW763t9axJdfhsaQHCTpHRlzolCGNkosiGJaUkNknpndE+TAL +5oEEuEV8lgsbWQzkWZU9VjkgUXHQkU7OIELsO1UrgqL7lCRFyT2FWRImzZTcTESk +f9dKFYbn9uUDB4Fz1OkzrNS8C8drNiEPyNxID05SF/niBrRN+AgdakW5c1pEkd33 +lwsgZUCn+0hbPkuqsdwuErLcAjiJ5DeyVpqXCUzJPP1S+o3Hy+VgYeIy5TG8k5kn +5G8i7rjtFQARAQABiQIfBBgBCAAJBQJX85JeAhsMAAoJEPdARsMW2Pn1Zp4P/3ck +0a7WnpsN9CDFBIR0pM9i77cYhDfDp6EsMzmWovDlp8rIkLYQW2WANFPZB+lrArqh +fgZcM8a4vK/1iaX/AhoIzGzFoGn3bt3pnYe4OZ5Gxt/MD34F80Y7SVsFiob+ZlRO +QNbsPOrOm5fcXzlEGrQs/4H+WgZkOZqxZ5ydO75Qf2VFUVGpFSA6KQdGUWh8o6Fi +VnnfIgZKYuc1TKqtF+4SHpFgiPqvJSBEaguDy1ty7fm1tLO4zZHIDPW8u1SAWfG/ +SKn6wcZlkrBJ79pKyEYXs7ZQm6qA0L0FzPJsRwWFrtJE4VzNQDIp999qmY0IYCk5 +s34Nwbqtdk8P0G5pl1IufCXv/+6hx+0ahxQUW8cP/wYDc+5MY1Y8m+wHEVaXRVyl +IVZL/UdoHwVbQoIvnUCXY2hWAkcStyyZU5G4Y+x5v3WuERdAexEPmxr7NcCey5rO +jhv7UwyrtqggTzjUryf+y/HNRNgQNyvi6nng2vNe7YS1iA/Qu55jr98IWO5ybP/B +THylTg4m+z2Ni/U0vG35Vbc9yU8ZHkqaHyg1K1JqEC/U8p/YXBny30NUQEf3wFQZ +y3jte5Sm6up2fWZIdeitjKlP8hvuXS/H9KTxj+xJHm6jRnZCrThtwKWDoKBOq1OH +8f9kzGZAZKvkJEV6DtTfsgGjCc+h99ce+A5kjmEl +=P2ir +-----END PGP ARMORED FILE----- diff --git a/roles/hass/files/templates.yaml b/roles/hass/files/templates.yaml index 4d5f219..b4c7f0e 100644 --- a/roles/hass/files/templates.yaml +++ b/roles/hass/files/templates.yaml @@ -1,6 +1,126 @@ - - sensor: - name: "chance_of_rain" unit_of_measurement: "%" icon: "mdi:weather-pouring" - state: "{{ state_attr('weather.hourly', 'forecast')[:2] | map(attribute='precipitation_probability') | list | max | float }}" + state: >- + {% raw -%} + {% set ipma_2h = state_attr('weather.ipma_hourly_home', 'forecast')[:2] | map(attribute='precipitation_probability') %} + {% set ipma = state_attr('weather.ipma_hourly_home', 'forecast')[0]['precipitation_probability'] | int %} + {% set owm = states('sensor.owm_home_forecast_precipitation_probability') | int %} + {{ [owm, ipma, 0] | max | int }} + {% endraw %} + + {% for radiator in hass_radiators %} + {% if 'status' in radiator %} + + - name: "radiator_{{ radiator.name }}_last_updated" + unit_of_measurement: "minutes" + icon: "mdi:update" + device_class: duration + state: >- + {% raw %} {% {% endraw -%} + set since_last_update = now() - states.{{ radiator.status }}.last_updated + {%- raw %} %} {% endraw %} + + {% raw %} {{ {% endraw -%} + since_last_update.seconds|int // 60 + {%- raw %} }} {% endraw -%} + {% endif %} + {% endfor %} + {% for linux_tracker in hass_linux_presence_trackers -%} + {% endfor %} + +- binary_sensor: + {% if blink1_enabled -%} + - name: "blink1_on" + device_class: light + state: >- + {% raw -%} + {{ state_attr('sensor.blink1', 'rgb') != "#000000" }} + {% endraw %} + {% endif %} + + - name: "heating_on" + icon: "mdi:home-thermometer" + device_class: heat + state: >- + {% raw -%} + {% set all_radiators = states.climate | selectattr("attributes", "defined") | map(attribute="attributes") | selectattr("temperature", "defined") | map(attribute="temperature") | default([]) | list %} + {% set max_radiator_temp = all_radiators | max | default(0.0) | float %} + {% set target_temp = states('input_number.target_temp_heat') | default(0.0) | float %} + {{ max_radiator_temp >= target_temp }} + {% endraw %} + + - name: s21_anyone_home + icon: "mdi:home-account" + attributes: + friendly_name: "Anyone home" + state: >- + {% raw -%} + {{ state_attr("zone.home", "persons") | default([]) | length > 0 }} + {% endraw %} + + - name: doorbell_buzzer + state: >- + {% raw %} {{ is_state("switch.doorbell_buzzer", "on") }} {% endraw +%} + icon: >- + {% raw -%} + {% if is_state("switch.doorbell_buzzer", "on") %} + mdi:electric-switch-closed + {% else %} + mdi:electric-switch + {% endif %} + {% endraw %} + + - name: washing_machine_on + icon: "mdi:washing-machine" + device_class: running + delay_on: "00:00:05" + delay_off: "00:00:05" + state: >- + {% raw -%} + {% set current = states('sensor.washing_machine_electric_a') %} + {% set power = states('sensor.washing_machine_electric_w') %} + {% if current == "unavailable" or power == "unavailable" %} + {{ false }} + {% else %} + {{ current|default(0.0)|float > 0.14 or power|default(0.0)|float > 2.8 }} + {% endif %} + {% endraw %} + + {% for linux_tracker in hass_linux_presence_trackers -%} + - name: {{ linux_tracker.name }}_active + icon: "mdi:laptop" + state: >- + {% raw -%} {% {% endraw %} set state_text = states('input_text.webhook_{{ linux_tracker.name }}') {%raw%} %} {%endraw%} + {% raw %} + {{ state_text == "active" }} + {% endraw %} + + - name: "{{ linux_tracker.name }}_webhook_triggering" + state: >- + {% raw %} {% {% endraw -%} + set since_last_triggered = now() - state_attr('automation.webhook_presence_trackers_{{ linux_tracker.name }}', 'last_triggered') + {%- raw %} %} {% endraw %} + + {% raw %} {{ {% endraw -%} + since_last_triggered.seconds|int < 666 + {%- raw %} }} {% endraw -%} + + {% endfor %} + +- button: + name: doorbell_buzzer + icon: >- + {% raw -%} + {% if is_state("switch.doorbell_buzzer", "on") %} + mdi:electric-switch-closed + {% else %} + mdi:electric-switch + {% endif %} + {% endraw +%} + press: + - service: script.toggle_switch_like_button + data: + target_switch: switch.doorbell_buzzer + press_for_ms: 200 diff --git a/roles/hass/handlers/main.yml b/roles/hass/handlers/main.yml index 1d26331..5fc0b27 100644 --- a/roles/hass/handlers/main.yml +++ b/roles/hass/handlers/main.yml @@ -15,4 +15,24 @@ name: hass state: started restart: true - when: hass_container is not defined or not hass_container.changed + when: + - hass_container is not defined or not hass_container.changed + - hass_container_state|default("stopped") == "started" + +- name: restart zwavejs container + docker_container: + name: zwavejs + state: started + restart: true + when: + - zwavejs_container is not defined or not zwavejs_container.changed + - hass_container_state|default("stopped") == "started" + +- name: udevadm reload rules + command: udevadm control --reload-rules + +- name: nmcli conn reload + command: nmcli conn reload + +- name: nmcli device wifi hotspot + command: nmcli device wifi hotspot diff --git a/roles/hass/tasks/hass.yml b/roles/hass/tasks/hass.yml index 289ad36..b8e1d9a 100644 --- a/roles/hass/tasks/hass.yml +++ b/roles/hass/tasks/hass.yml @@ -1,52 +1,103 @@ --- -- name: allow ssh - ufw: - rule: allow - to_port: "22" - direction: in - state: enabled - tags: - - ufw +# - name: allow ssh +# ufw: +# rule: allow +# to_port: "22" +# direction: in +# state: enabled +# tags: +# - ufw -- name: allow loopback - ufw: - rule: allow - interface: lo - direction: in - state: enabled - tags: - - ufw +# - name: allow loopback +# ufw: +# rule: allow +# interface: lo +# direction: in +# state: enabled +# tags: +# - ufw -- name: default policy - ufw: - policy: allow - state: enabled - tags: - - ufw +# - name: default policy +# ufw: +# policy: allow +# state: enabled +# tags: +# - ufw -- name: deny hass cloud port stuff - ufw: - # drops packets - rule: deny - to_port: '42161' - direction: in - state: enabled - tags: - - ufw +# - name: deny hass cloud port stuff +# ufw: +# # drops packets +# rule: deny +# to_port: '42161' +# direction: in +# state: enabled +# tags: +# - ufw -- name: reject zwavejs ws and hass ports (loopback only) - ufw: - # connection refused - rule: reject - to_port: "{{ item }}" - direction: in - state: enabled - with_items: - - "8091" - - "8123" +# - name: reject zwavejs ws and hass ports (loopback only) +# ufw: +# # connection refused +# rule: reject +# to_port: "{{ item }}" +# direction: in +# state: enabled +# with_items: +# - "8091" +# - "8123" +# tags: +# - ufw + +- name: get current timestamp line + command: grep "timestamp=" /etc/NetworkManager/system-connections/blackbox.connection + check_mode: false + ignore_errors: true + changed_when: false + register: timestamp tags: - - ufw + - hass-wifi + - hass-blackbox + +# nmcli device wifi blackbox ifname wlo1 ssid {{ hass_wifi_blackbox.ssid }} password {{ hass_wifi_blackbox.pass }} +- name: config for blackbox wifi ap with NetworkManager + template: + src: blackbox.connection.j2 + dest: /etc/NetworkManager/system-connections/blackbox.connection + owner: root + group: root + mode: 0600 + tags: + - hass-wifi + - hass-blackbox + notify: + - nmcli conn reload + - nmcli device wifi hotspot + +# # routing/forwarding should not be enabled, but block it to be sure +# - name: allow local traffic on blackbox wifi +# ufw: +# rule: allow +# interface: "{{ hass_wifi_blackbox.iface }}" +# direction: out +# dest: "{{ hass_wifi_blackbox.ip }}/{{ hass_wifi_blackbox.cidr_prefix }}" +# state: enabled +# tags: +# - hass-wifi +# - hass-blackbox +# - ufw + +# - name: reject everything else on the blackbox wifi +# ufw: +# # connection refused +# rule: reject +# interface: "{{ hass_wifi_blackbox.iface }}" +# direction: out +# dest: any +# state: enabled +# tags: +# - hass-wifi +# - hass-blackbox +# - ufw - name: copy ssh keys for {{ hass_config_repo_name }} template: @@ -66,22 +117,30 @@ - name: create dir structure file: - path: "{{ systemuserlist.hass.home }}/{{ item }}" + path: "{{ systemuserlist.hass.home }}/{{ item.name }}" state: directory - mode: 0755 + mode: "{{ item.mode | default('0755') }}" owner: hass group: hass tags: - hass-dirs + loop_control: + label: "{{ item.name }}" with_items: - - home-assistant - - home-assistant/config - - home-assistant/.config - - home-assistant/media - - zwavejs - - zwavejs/app - - zwavejs/app/store - - git + - name: .local + mode: "0775" + - name: home-assistant + - name: home-assistant/config + - name: home-assistant/config/python_scripts + - name: home-assistant/config/bvg + - name: home-assistant/.config # might not be needed, misread 'cache' as 'config' + - name: home-assistant/.cache + - name: home-assistant/media + mode: "0775" + - name: zwavejs + - name: zwavejs/app + - name: zwavejs/app/store + - name: git - name: template gitconfig template: @@ -108,36 +167,38 @@ - hass-git - hass-git-clone -- name: home assistant main configuration.yaml + +- name: home assistant config files template: - src: configuration.yaml.j2 - dest: "{{ systemuserlist.hass.home }}/home-assistant/config/configuration.yaml" + src: "{{ item }}.j2" + dest: "{{ systemuserlist.hass.home }}/home-assistant/config/{{ item }}" owner: "{{ systemuserlist.hass.uid }}" group: "{{ systemuserlist.hass.gid }}" mode: 0644 notify: restart hass container + with_items: + - secrets.yaml + - configuration.yaml + - templates.yaml + - climate.yaml + - automations-ansible-managed.yaml + - scripts-ansible-managed.yaml + - blink1.yaml tags: - hass-config -- name: home assistant secrets file - template: - src: secrets.yaml.j2 - dest: "{{ systemuserlist.hass.home }}/home-assistant/config/secrets.yaml" - owner: "{{ systemuserlist.hass.uid }}" - group: "{{ systemuserlist.hass.gid }}" - mode: 0644 - notify: restart hass container - tags: - - hass-config - -- name: copy home assistant templates file +- name: copy dashboards copy: - src: templates.yaml - dest: "{{ systemuserlist.hass.home }}/home-assistant/config/templates.yaml" - owner: "{{ systemuserlist.hass.uid }}" - group: "{{ systemuserlist.hass.gid }}" - mode: 0644 + src: "private/hass/{{ item }}" + dest: "{{ systemuserlist.hass.home }}/home-assistant/config/{{ item }}" + mode: 0755 + owner: hass + group: hass notify: restart hass container + with_items: + - mini.yaml + - ui-test.yaml + - card_room.yaml tags: - hass-config @@ -163,15 +224,106 @@ - hass-cron - hass-git +- name: udev rules + template: + src: "{{ item }}.j2" + dest: /etc/udev/rules.d/{{ item }} + owner: root + group: root + mode: 0644 + notify: udevadm reload rules + with_items: + - 20-sdr.rules + tags: + - hass-udev + +# key source: +# - http://download.ag-projects.com/agp-debian-gpg.key +# - http://download.ag-projects.com/agp-debian-key.key +# gpg --enarmor roles/hass/files/agp-debian-gpg.key +# binary keys: .gpg +# ascii armor: .asc (or .key?) +- name: add apt key for sip tools + copy: + src: "{{ item }}" + dest: /etc/apt/trusted.gpg.d/{{ item }} + owner: root + group: root + mode: "0644" + with_items: + - agp-debian-gpg.asc + - agp-debian-key.asc + tags: + - packages + - hass-sip + - sip + +# - debug: +# msg: "deb [signed-by=/usr/share/keyrings/agp-debian-gpg.key] http://ag-projects.com/{{ ansible_lsb.id | lower }} {{ ansible_lsb.codename }} main" +# tags: hass-sip + +# [signed-by=/usr/share/keyrings/agp-debian-gpg.gpg] +# [signed-by=/etc/apt/trusted.gpg.d/agp-debian-gpg.asc] +- name: add repo for sip tools + apt_repository: + #repo: "{{ item }} [signed-by=/etc/apt/trusted.gpg.d/agp-debian-key.asc] http://ag-projects.com/{{ ansible_lsb.id | lower }} {{ ansible_lsb.codename }} main" + repo: "{{ item }} [signed-by=/etc/apt/trusted.gpg.d/agp-debian-key.asc] http://ag-projects.com/{{ ansible_lsb.id | lower }} sid main" + state: present + update_cache: false + with_items: + - "deb" + - "deb-src" + register: sip_repo + tags: + - packages + - hass-sip + - sip + when: false + +- name: update apt if new repo was added + apt: + update_cache: true + tags: + - packages + - hass-sip + - sip + when: + - sip_repo.changed + - false + # the host needs to have bluez installed for the container to use bluetooth -- name: install bluetooth packages +- name: install packages apt: name: + - vlc + - mplayer - bluez - bluetooth + - fapg + - podget + - sqlite3 + - rtl-433 + - mosquitto + - python3-paho-mqtt + - blink1 # git.sudo.is/ben/build-blink1 + # # sip tools + # - python3-sipsimple + # - sipclients3 state: latest tags: - packages + - hass-bluetooth + - hass-packages + - hass-sip + - sip + +- name: ensure bluetooth service is started and enabled + service: + name: bluetooth + state: started + enabled: true + tags: + - hass-bluetooth # docker run --run -it -p 8091:8091 -p 3000:3000 --network #bridgewithdns --device /dev/ttyACM0:/dev/zwave -v @@ -186,7 +338,7 @@ detach: true pull: true restart_policy: "unless-stopped" - state: "{{ container_state | default('started') }}" + state: "{{ hass_container_state | default('stopped') }}" container_default_behavior: compatibility user: "{{ systemuserlist.hass.uid }}:dialout" networks_cli_compatible: false @@ -200,18 +352,21 @@ # ws for hass<->zwavejs # hass is configured to use localhost:3000 to talk to zwavejs, but can # also use {{ bridgewithdns.zwavejs }}, but hass is very fragile and - # you have to manually work around it if it cant access zwaevjs because the - # ip/dns changed or the container moved networks. it is not configured in a - # config file either. so using localhost is the least fragile strategy. + # you have to manually work around it if it cant access zwaevjs because + # the ip/dns changed or the container moved networks. it is not + # configured in a config file either. so using localhost is the least + # fragile strategy. - "127.0.0.1:3000:3000" env: #BASE_URL: "/zwavejs/" SESSION_SECRET: "{{ zwavejs_session_secret }}" ZWAVEJS_EXTERNAL_CONFIG: /usr/src/app/store/.config-db + SERVER_SSL: "true" mounts: - type: bind source: "{{ systemuserlist.hass.home }}/zwavejs/app/store" target: /usr/src/app/store + register: zwavejs_container tags: - zwavejs - zwavejs-container @@ -219,42 +374,58 @@ - docker-containers # docker run --rm it --name hass -p 8123:8123 -e TZ=Etc/UTC -v -# /home/ben/hass:/config --network-bridgewithdns +# /home/ben/hass:/config --network=bridgewithdns # ghcr.io/home-assistant/home-assistant:stable - name: start home-assistant container docker_container: name: hass - image: ghcr.io/home-assistant/home-assistant:stable + #image: ghcr.io/home-assistant/home-assistant:stable + image: git.sudo.is/ben/hass:latest detach: true pull: true restart_policy: "unless-stopped" - state: "{{ container_state | default('started') }}" + state: "{{ hass_container_state | default('stopped') }}" container_default_behavior: compatibility user: "{{ systemuserlist.hass.uid }}:{{ systemuserlist.hass.gid }}" network_mode: host + privileged: true + capabilities: + - SYS_ADMIN + - NET_ADMIN env: TZ: "Etc/UTC" + devices: + - "/dev/blink1:/dev/blink1:rwm" + # #- "/dev/rtl_sdr:/dev/rtl_sdr:rw" mounts: - type: bind source: "{{ systemuserlist.hass.home }}/home-assistant/config" target: /config - type: bind source: "{{ systemuserlist.hass.home }}/home-assistant/.config" - target: /.config + target: /.config t + - type: bind + source: "{{ systemuserlist.hass.home }}/home-assistant/.cache" + target: /.cache + - type: bind + source: "{{ systemuserlist.hass.home }}/.local" + target: /.local - type: bind source: "{{ systemuserlist.hass.home }}/home-assistant/media" target: /usr/var/media - # for bluetooth, container needs access to the dbus socket - # https://www.home-assistant.io/integrations/bluetooth/ - type: bind - source: /run/dbus/ - target: /run/dbus/ read_only: true + source: "{{ systemuserlist.archives.home }}/podgrab/data" + target: /usr/var/media/podcasts - type: bind - source: /etc/bluetooth/main.conf - target: /etc/bluetooth/main.conf - read_only: true + read_only: false + source: /run/dbus + target: /run/dbus + # - type: bind + # source: /etc/bluetooth/main.conf + # target: /etc/bluetooth/main.conf + # read_only: true # scripts from role: common # only depends on requests, which hass image has - type: bind @@ -284,19 +455,23 @@ prediff_cmd: echo with_items: - "{{ hass_url }}" + - "{{ zwavejs_url }}" -- name: template nginx vhost for hass +- name: template nginx vhosts for hass and friends template: - src: 01-hass.j2 - dest: /etc/nginx/sites-enabled/01-hass + src: "01-{{ item }}.conf.j2" + dest: /etc/nginx/sites-enabled/{{ item }}.conf owner: root group: root mode: 0644 + with_items: + - hass + - zwavejs tags: - nginx - hass-nginx - zwave-nginx - notify: restart nginx + notify: reload nginx # different task because its better for the hass config to restart nginx - name: template nginx vhost for grafana-proxy diff --git a/roles/hass/templates/01-hass.conf.j2 b/roles/hass/templates/01-hass.conf.j2 new file mode 100644 index 0000000..3fabaa1 --- /dev/null +++ b/roles/hass/templates/01-hass.conf.j2 @@ -0,0 +1,144 @@ +map $http_upgrade $connection_upgrade { + default upgrade; + #default $http_connection; + '' close; +} + +server { + listen 443 ssl http2; + {% if inventory_hostname in wg_clients -%} + listen {{ wg_clients[inventory_hostname].ip }}:443 ssl http2; + {% endif -%} + + include /etc/nginx/authelia_internal.conf; + include listen-proxy-protocol.conf; + include /etc/nginx/sudo-known.conf; + + server_name {{ hass_url }}; + + location / { + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + proxy_pass http://127.0.0.1:8123; + } + + # location /media { + # root {{ systemuserlist.hass.home }}/home-assistant/media; + # autoindex on; + # autoindex_exact_size off; + # } + + {% if blink1_enabled -%} + location /blink1/ { + {% for cidr in my_local_cidrs -%} + allow {{ cidr }}; + {% endfor -%} + allow {{ my_public_ips[ansible_control_host] }}/32; + allow 127.0.0.1; + deny all; + + {% if blink1_tiny_html|default(false) -%} + rewrite '^/blink1(/.*)$' $1 break; + sub_filter_once off; + sub_filter '"/' '"./'; + {% else -%} + add_header Content-Type 'application/json' always; + {% endif -%} + proxy_http_version 1.1; + proxy_pass http://localhost:{{ blink1_server_port }}; + + } + {% endif %} + location = {{ nginx_zwavejs_path }} { + # zwavejs needs to be accessed with a trailing / to respond. + # + # temporary redirects dont get remembered by the browser + # and redirect issues are no fun + return 302 https://{{ hass_url }}{{ nginx_zwavejs_path }}/; + } + location {{ nginx_zwavejs_path }} { + #add_header Access-Control-Allow-Origin "*" always; + # kill cache + add_header Last-Modified $date_gmt always; + add_header Cache-Control 'no-store' always; + if_modified_since off; + expires off; + etag off; + + # nuke the service worker cache + # sub_filter '.js' '.js?id=$request_id'; + + include /etc/nginx/require_auth.conf; + + rewrite ^ $request_uri; + rewrite '^{{ nginx_zwavejs_path }}(/.*)$' $1 break; + + proxy_set_header X-External-Path {{ nginx_zwavejs_path }}; + + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + #proxy_socket_keepalive on; + + ## for the special dashboard + ## https://zwave-js.github.io/zwave-js-ui/#/usage/reverse-proxy?id=using-an-http-header + ## proxy_set_header X-External-Path $http_x_ingress_path; + + proxy_pass http://{{ bridgewithdns.zwavejs }}:8091$uri; + #proxy_pass http://{{ bridgewithdns.zwavejs }}:8091/; + } + + location = {{ nginx_podgrab_path }} { + return 302 https://{{ hass_url }}{{ nginx_podgrab_path }}; + } + location {{ nginx_podgrab_path }} { + #include /etc/nginx/require_auth.conf; + + # json for a tag: + # https://hass.sudo.is/podcasts/tags/${tag} + + # add rss type or let podgrab handle it? + # slice out the url prefix + rewrite '^{{ nginx_podgrab_path }}(/.*)$' $1 break; + + # rewrite html responses to add the url prefix + sub_filter_once off; + sub_filter 'href="/' 'href="{{ nginx_podgrab_path }}/'; + sub_filter 'src="/' 'src="{{ nginx_podgrab_path }}/'; + sub_filter '= "/' '= "{{ nginx_podgrab_path }}/'; + sub_filter ':"/' ':"{{ nginx_podgrab_path }}/'; + sub_filter ':href="\'/' ':href="\'{{ nginx_podgrab_path }}/'; + sub_filter 'return "/' 'return "{{ nginx_podgrab_path }}/'; + sub_filter '("/' '("{{ nginx_podgrab_path }}/'; + sub_filter '`/' '`{{ nginx_podgrab_path }}/'; + sub_filter '/ws' '{{ nginx_podgrab_path }}/ws'; + + # nuke the service worker cache + sub_filter '.js' '.js?id=$request_id'; + sub_filter '.css' '.css?id=$request_id'; + + # headers for websockets + #proxy_set_header Upgrade $http_upgrade; + #proxy_set_header Connection $connection_upgrade; + proxy_set_header X-Real-IP $remote_addr; + #proxy_set_header X-Forwarded-Proto $scheme; + #proxy_set_header X-Forwarded-Host $http_host; + #proxy_set_header Host $http_host; + + proxy_pass http://localhost:{{ podgrab_port }}{{ nginx_podgrab_path }}/; + } + + access_log /var/log/nginx/access_{{ hass_url }}.log main; + error_log /var/log/nginx/error_{{ hass_url }}.log warn; + + ssl_session_timeout 5m; + ssl_certificate /usr/local/etc/certs/{{ hass_url }}/fullchain.pem; + ssl_certificate_key /usr/local/etc/certs/{{ hass_url }}/privkey.pem; + + fastcgi_hide_header X-Powered-By; +} diff --git a/roles/hass/templates/01-hass.j2 b/roles/hass/templates/01-hass.j2 deleted file mode 100644 index f30ada5..0000000 --- a/roles/hass/templates/01-hass.j2 +++ /dev/null @@ -1,74 +0,0 @@ -map $http_upgrade $connection_upgrade { - default upgrade; - #default $http_connection; - '' close; -} - -server { - listen 443 ssl http2; - {% if inventory_hostname in wg_clients -%} - listen {{ wg_clients[inventory_hostname].ip }}:443 ssl http2; - {% endif -%} - - include /etc/nginx/authelia_internal.conf; - include listen-proxy-protocol.conf; - include /etc/nginx/sudo-known.conf; - - server_name {{ hass_url }}; - - location / { - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection $connection_upgrade; - - proxy_set_header Host $host; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - - proxy_pass http://127.0.0.1:8123; - } - - location = {{ nginx_zwavejs_path }} { - # zwavejs needs to be accessed with a trailing / to respond. - # - # temporary redirects dont get remembered by the browser - # and redirect issues are no fun - return 302 https://{{ hass_url }}{{ nginx_zwavejs_path }}/; - } - - location {{ nginx_zwavejs_path }} { - #add_header Access-Control-Allow-Origin "*" always; - # kill cache - add_header Last-Modified $date_gmt always; - add_header Cache-Control 'no-store' always; - if_modified_since off; - expires off; - etag off; - - include /etc/nginx/require_auth.conf; - - rewrite ^ $request_uri; - rewrite '^{{ nginx_zwavejs_path }}(/.*)$' $1 break; - - proxy_set_header X-External-Path {{ nginx_zwavejs_path }}; - - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection $connection_upgrade; - #proxy_socket_keepalive on; - - proxy_pass http://{{ bridgewithdns.zwavejs }}:8091$uri; - #proxy_pass http://{{ bridgewithdns.zwavejs }}:8091; - # for the special dashboard - # https://zwave-js.github.io/zwave-js-ui/#/usage/reverse-proxy?id=using-an-http-header - # proxy_set_header X-External-Path $http_x_ingress_path; - } - - access_log /var/log/nginx/access_{{ hass_url }}.log main; - error_log /var/log/nginx/error_{{ hass_url }}.log warn; - - ssl_session_timeout 5m; - ssl_certificate /usr/local/etc/certs/{{ hass_url }}/fullchain.pem; - ssl_certificate_key /usr/local/etc/certs/{{ hass_url }}/privkey.pem; - - fastcgi_hide_header X-Powered-By; -} diff --git a/roles/hass/templates/01-zwavejs.conf.j2 b/roles/hass/templates/01-zwavejs.conf.j2 new file mode 100644 index 0000000..41ed80a --- /dev/null +++ b/roles/hass/templates/01-zwavejs.conf.j2 @@ -0,0 +1,47 @@ +server { + listen 443 ssl http2; + {% if inventory_hostname in wg_clients -%} + listen {{ wg_clients[inventory_hostname].ip }}:443 ssl http2; + {% endif -%} + + include /etc/nginx/authelia_internal.conf; + include listen-proxy-protocol.conf; + include /etc/nginx/sudo-known.conf; + + server_name {{ zwavejs_url }}; + + location / { + include /etc/nginx/require_auth.conf; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + # nuke cache + add_header Last-Modified $date_gmt always; + add_header Cache-Control 'no-store' always; + if_modified_since off; + expires off; + etag off; + + # nuke the service worker cache + sub_filter '.js' '.js?id=$request_id'; + ## for the special dashboard + ## https://zwave-js.github.io/zwave-js-ui/#/usage/reverse-proxy?id=using-an-http-header + #proxy_set_header X-External-Path "/"; + proxy_set_header X-External-Path $http_x_ingress_path; + + proxy_pass http://{{ bridgewithdns.zwavejs }}:8091; + } + + access_log /var/log/nginx/access_{{ zwavejs_url }}.log main; + error_log /var/log/nginx/error_{{ zwavejs_url }}.log warn; + + ssl_session_timeout 5m; + ssl_certificate /usr/local/etc/certs/{{ zwavejs_url }}/fullchain.pem; + ssl_certificate_key /usr/local/etc/certs/{{ zwavejs_url }}/privkey.pem; + + fastcgi_hide_header X-Powered-By; +} diff --git a/roles/hass/templates/20-sdr.rules.j2 b/roles/hass/templates/20-sdr.rules.j2 new file mode 100644 index 0000000..5d3bcf1 --- /dev/null +++ b/roles/hass/templates/20-sdr.rules.j2 @@ -0,0 +1 @@ +SUBSYSTEM=="usb", ATTRS{idVendor}=="0bda", ATTRS{idProduct}=="2838", GROUP="adm", MODE="0666", SYMLINK+="rtl_sdr" diff --git a/roles/hass/templates/automations-ansible-managed.yaml.j2 b/roles/hass/templates/automations-ansible-managed.yaml.j2 new file mode 100644 index 0000000..352282a --- /dev/null +++ b/roles/hass/templates/automations-ansible-managed.yaml.j2 @@ -0,0 +1,174 @@ +- alias: monitor_radiators_reporting + description: monitor that the battery-powered radiator knobs are working + trigger: + {% for radiator in hass_radiators -%} + {% if 'status' in radiator -%} + - platform: state + id: "radiator {{ radiator.name }}" + entity_id: + - binary_sensor.radiator_{{ radiator.name }}_reporting + to: 'off' + {% endif -%} + {% endfor %} + + condition: [] + mode: single + action: + - service: notify.persistent_notification + data: + title: 'device not reporting' + message: 'stopped reporting: {%raw%}{{ trigger.id }}{%endraw%}' + - service: notify.notify + data: + title: 'device not reporting' + message: 'stopped reporting: {%raw%}{{ trigger.id }}{%endraw%}' + +- alias: refresh_light_switches_state + description: the tkbhome switches dont automatically report their state + trigger: + - platform: time_pattern + minutes: "/1" + condition: [] + mode: single + action: + - service: zwave_js.refresh_value + data: + entity_id: + {% for item in hass_light_switches -%} + {% if item.automation_refresh|default(false) -%} + - {{ item.entity_id }} + {% endif -%} + {% endfor %} + +{% for item in hass_light_switches -%} +{% set domain = item.entity_id.split('.')[0] %} +{% set name = item.entity_id.split('.')[1] %} +{% if 'auto_off' in item %} +- alias: {{ name }}_turn_off + description: automatically turn off {{ name }} {{ domain}} + trigger: + - platform: state + entity_id: + - {{ item.entity_id }} + to: "on" + for: + minutes: {{ item.auto_off }} + condition: + - condition: state + entity_id: {{ item.entity_id }} + state: "on" + action: + - service: {{ domain }}.turn_off + data: {} + target: + entity_id: {{ item.entity_id }} + mode: single +{% endif -%} +{% endfor %} + +{% for linux_tracker in hass_linux_presence_trackers -%} +- alias: "webhook_presence_trackers_{{ linux_tracker.name }}" + description: "" + trigger: + - platform: webhook + webhook_id: {{ linux_tracker.name }}-{{ linux_tracker.webhook_key }} + id: webhook + mode: single + action: + - if: + - condition: template + value_template: >- + {%raw -%} {% {%endraw%} set current_value = states('input_text.webhook_{{ linux_tracker.name }}') {%raw%} %} {%endraw%} + {% raw %} + {{ current_value != trigger.json['state'] }} + {% endraw %} + + then: + - service: input_text.set_value + target: + entity_id: input_text.webhook_{{ linux_tracker.name }} + data: + value: "{% raw %}{{ trigger.json.state }}{% endraw %}" + +- alias: "inactive_webhook_presence_trackers_{{ linux_tracker.name }}" + description: "" + trigger: + - platform: state + entity_id: binary_sensor.{{ linux_tracker.name }}_webhook_triggering + to: "off" + action: + - service: input_text.set_value + target: + entity_id: input_text.webhook_{{ linux_tracker.name }} + data: + value: "inactive" + +{% endfor %} + +- alias: flood_sensor_washing_machine + description: "" + trigger: + - platform: state + entity_id: + - binary_sensor.flood_sensor_water_leak_detected + to: "on" + condition: [] + mode: single + action: + - service: switch.turn_off + data: {} + target: + entity_id: switch.washing_machine + - service: notify.notify + data: + title: "FLOOD SENSOR BATHROOM" + message: "WATER DETECTED! power to washing machine was cut" + - if: + - condition: zone + entity_id: person.ben + zone: zone.home + then: + - service: switch.turn_on + data: {} + target: + entity_id: switch.nad_c370 + - service: media_player.play_media + data: + media_content_type: video/webm + media_content_id: media-source://media_source/media/flood_alert.mp3 + target: + entity_id: media_player.den_tv + +- alias: buzzer_normally_closed + description: "keep the buzzer switch closed" + mode: single + trigger: + - platform: state + entity_id: + - switch.doorbell_buzzer + to: "on" + for: + hours: 0 + minutes: 0 + seconds: 1 + condition: [] + action: + - service: switch.turn_off + data: {} + target: + entity_id: switch.doorbell_buzzer + +{% for item in hass_feedreader -%} +- alias: "podcast_parse_feed_{{ item.short_name }}" + trigger: + platform: event + event_type: feedreader + event_data: + feed_url: "{{ item.url }}" + action: + service: persistent_notification.create + data: + title: "Podcast parsed" + message: {% raw %}"{{ trigger.event.data }}"{% endraw %} + +{% endfor %} diff --git a/roles/hass/templates/blackbox.connection.j2 b/roles/hass/templates/blackbox.connection.j2 new file mode 100644 index 0000000..bf78b90 --- /dev/null +++ b/roles/hass/templates/blackbox.connection.j2 @@ -0,0 +1,38 @@ +{# + # nmcli device wifi blackbox ifname wlo1 ssid {{ hass_wifi_blackbox.ssid }} password {{ hass_wifi_blackbox.pass }} + #} +[connection] +id=blackbox +uuid={{ hass_wifi_blackbox.uuid }} +type=wifi +autoconnect=false +interface-name={{ hass_wifi_blackbox.iface }} +{% if timestamp.stdout %} +{{ timestamp.stdout }} +{% else %} +timestamp={{ ansible_date_time.epoch }} +{% endif %} + +[wifi] +mode=ap +ssid={{ hass_wifi_blackbox.ssid }} +{% if hass_wifi_blackbox.hidden|default(false) -%} +hidden=true +{% endif %} + +[wifi-security] +group=ccmp; +key-mgmt=wpa-psk +pairwise=ccmp; +proto=rsn; +psk={{ hass_wifi_blackbox.pass }} + +[ipv4] +address1={{ hass_wifi_blackbox.ip }}/{{ hass_wifi_blackbox.cidr_prefix }} +method=manual + +[ipv6] +addr-gen-mode=stable-privacy +method=ignore + +[proxy] diff --git a/roles/hass/templates/blink1.yaml.j2 b/roles/hass/templates/blink1.yaml.j2 new file mode 100644 index 0000000..7c27e6b --- /dev/null +++ b/roles/hass/templates/blink1.yaml.j2 @@ -0,0 +1,71 @@ +{% if blink1_enabled -%} +friendly_name: blink1 +value_template: >- + {% raw -%} + {{ state_attr('sensor.blink1', 'rgb') != "#000000" }} + {% endraw %} + +# color_template: >- +# {% raw -%} +# {{ state_attr('sensor.blink1', 'rgb') }} +# {% endraw %} + +turn_on: + - service: rest_command.blink1_turn_on + - delay: + milliseconds: 500 + - service: homeassistant.update_entity + target: + entity_id: sensor.blink1 +turn_off: + - service: rest_command.blink1_turn_off + - delay: + milliseconds: 500 + - service: homeassistant.update_entity + target: + entity_id: sensor.blink1 +set_color: + - service: rest_command.blink1_turn_off + - service: rest_command.blink1_set_color + data: + # https://github.com/velijv/home-assistant-color-helpers#rgb-to-hex + # https://community.home-assistant.io/t/advanced-light-template-help/175654 + # https://community.home-assistant.io/t/using-hsv-hsb-to-set-colored-lights/15472 + rgb: >- + {%raw%} + {%- set h2 = h / 360 -%} + {%- set s2 = s / 100 -%} + {%- set v = 100 -%} + {%- set i = (h2 * 6 ) | round(2,'floor') | int-%} + {%- set f = h2 * 6 - i -%} + {%- set p = v * (1 - s2) -%} + {%- set q = v * (1 - f * s2) -%} + {%- set t = v * (1 - (1 - f) * s2) -%} + {%- if i % 6 == 0 -%} + {%- set r = v | int -%} + {%- set g = t | int -%} + {%- set b = p | int -%} + {%- elif i % 6 == 1 -%} + {%- set r = q | int -%} + {%- set g = v | int -%} + {%- set b = p | int -%} + {%- elif i % 6 == 2 -%} + {%- set r = p | int -%} + {%- set g = v | int -%} + {%- set b = t | int -%} + {%- elif i % 6 == 3 -%} + {%- set r = p | int -%} + {%- set g = q | int -%} + {%- set b = v | int -%} + {%- elif i % 6 == 4 -%} + {%- set r = t | int -%} + {%- set g = p | int -%} + {%- set b = v | int -%} + {%- elif i % 6 == 5 -%} + {%- set r = v | int -%} + {%- set g = p | int -%} + {%- set b = q | int -%} + {%- endif -%} + {{ '%02x%02x%02x' | format(r, g, b) }} + {%endraw%} +{% endif %} diff --git a/roles/hass/templates/climate.yaml.j2 b/roles/hass/templates/climate.yaml.j2 new file mode 100644 index 0000000..897c2b3 --- /dev/null +++ b/roles/hass/templates/climate.yaml.j2 @@ -0,0 +1,52 @@ +{% raw -%} +- platform: climate_template + name: Radiators + modes: + - "auto" + - "heat" + - "cool" + - "off" + min_temp: 0 + max_temp: 30 + + current_temperature_template: "{{ states('input_number.heating_setpoint_test') }}" + hvac_mode_template: "{{ states('input_select.heating_mode_test') }}" + current_humidity_template: 0.0 + swing_mode_template: false + availability_template: true + + set_temperature: + - service: input_number.set_value + data: + value: >- + {% set set_point = float(state_attr('climate.radiators', 'temperature'), 14.0) %} + {{ set_point }} + target: + entity_id: input_number.heating_setpoint_test + + set_hvac_mode: + - service: input_select.select_option + data: + option: >- + {% set hvac_mode = state_attr('climate.radiators', 'hvac_mode') | default('off') %} + {{ hvac_mode }} + target: + entity_id: input_select.heating_mode_test + +{% endraw %} + +{# use this in script? + {{ state_attr('climate.radiators', 'temperature') }} + + this should work, but doesnt + docs: https://www.home-assistant.io/integrations/template/ + {{ this.attributes.temperature }} + + https://github.com/jcwillox/hass-template-climate/issues/29 + + this syntax works: + {% set set_point = float(state_attr('climate.radiators', 'temperature'), 14.0) %} + {{ set_point }} + + target_temperature_template: "{{ states('input_number.heating_setpoint_test') }}" +#} diff --git a/roles/hass/templates/configuration.yaml.j2 b/roles/hass/templates/configuration.yaml.j2 index 0f12ae1..70be9ea 100644 --- a/roles/hass/templates/configuration.yaml.j2 +++ b/roles/hass/templates/configuration.yaml.j2 @@ -57,48 +57,84 @@ default_config: # zeroconf: # zone: +automation ui: !include automations.yaml +automation ansible: !include automations-ansible-managed.yaml +script: !include scripts.yaml +scene: !include scenes.yaml +template: !include templates.yaml +climate: !include climate.yaml + # Text to speech tts: - platform: voicerss api_key: !secret voicerss_api_key - platform: google_translate - -automation: !include automations.yaml -script: !include scripts.yaml -scene: !include scenes.yaml -template: !include templates.yaml - -{# calendar: - # {% for item in hass_caldav.urls %} - # - # - platform: caldav - # days: 30 - # username: !secret caldav_user - # password: !secret caldav_passwd - # # {{ item.name }} - # url: {{ item.url }} - # - # {% endfor %} - #} + - platform: picotts + language: "en-GB" calendar: + {% for item in hass_caldav.calendars -%} - platform: caldav days: 30 username: !secret caldav_user password: !secret caldav_passwd - # {{ hass_caldav.urls[0].name }} - url: {{ hass_caldav.urls[0].url }} + # {{ item.name | trim }} + url: {{ item.url | trim }} + {% endfor %} http: - # container runs with network_mode=host, so no network isolation. the docs say to not - # do this, and it doesnt work as expected either. - # using ufw/iptables for now.... - # - #server_host: 127.0.0.1 + server_host: 127.0.0.1 + server_port: 8123 trusted_proxies: - 127.0.0.1 use_x_forwarded_for: true +frontend: + themes: !include_dir_merge_named themes + +panel_custom: + - name: zwave + sidebar_title: Z-Wave + sidebar_icon: mdi:z-wave + js_url: /api/hassio/app/entrypoint.js + url_path: 'config/devices/dashboard?historyBack=1&config_entry=d6e38621854098348266029e18f93048' + embed_iframe: true + require_admin: true + config: + ingress: core_configurator + - name: automations + sidebar_title: Automations + sidebar_icon: mdi:robot + js_url: /api/hassio/app/entrypoint.js + url_path: 'config/automation/dashboard' + embed_iframe: true + require_admin: true + config: + ingress: core_configurator + - name: scripts + sidebar_title: Scripts + sidebar_icon: mdi:script + js_url: /api/hassio/app/entrypoint.js + url_path: 'config/script/dashboard' + embed_iframe: true + require_admin: true + config: + ingress: core_configurator + - name: integrations + sidebar_title: Integrations + sidebar_icon: mdi:integrated-circuit-chip + js_url: /api/hassio/app/entrypoint.js + url_path: 'config/integrations' + embed_iframe: true + require_admin: true + config: + ingress: core_configurator + + +{% set zone = {'zone': hass_zones} %} +{{ zone | to_nice_yaml }} + + homeassistant: auth_providers: - type: command_line @@ -109,6 +145,7 @@ homeassistant: currency: EUR unit_system: metric time_zone: "Europe/Berlin" + country: DE external_url: https://{{ hass_url }} internal_url: https://{{ hass_url }} allowlist_external_dirs: @@ -118,6 +155,42 @@ homeassistant: - "https://{{ hass_notflix_url }}" media_dirs: media: "/usr/var/media" + customize: + zone.home: + friendly_name: S21 + +lovelace: + mode: storage + dashboards: + lovelace-yaml: + mode: yaml + title: Mini + icon: mdi:view-dashboard + show_in_sidebar: true + require_admin: false + filename: mini.yaml + lovelace-yaml2: + mode: yaml + title: Test + icon: mdi:view-dashboard + show_in_sidebar: true + require_admin: false + filename: ui-test.yaml + + {# resources: + # - url: /config/custom_components/ui_lovelace_minimalist/cards/button-card/button-card.js + # type: module + # - url: /config/custom_components/ui_lovelace_minimalist/cards/button-card/button-card.js + # type: module #} + +recorder: + purge_keep_days: 3 + exclude: + entity_globs: + - binary_sensor.1066d799* + - sensor.1066d799* + - light.1066d799* + sensor: # https://www.home-assistant.io/integrations/dwd_weather_warnings/ @@ -144,28 +217,116 @@ sensor: # Stadt Berlin region_name: 811000000 + {% for item in hass_bvg -%} + - platform: bvg_berlin_public_transport + name: {{ item.name }} + stop_id: "{{ item.stop_id }}" + direction: {{ item.direction | trim }} + {% if item.walking_distance is defined -%} + walking_distance: {{ item.walking_distance | trim }} + {% endif -%} + file_path: "/config/bvg/" + {% endfor %} + + - platform: waqi + token: !secret waqi_token + locations: + {% for item in hass_waqi.locations -%} + - "{{ item | trim }}" + {%- endfor +%} + {#stations: + #{% for item in hass_waqi.stations -%} + #- "{{ item | trim }}" + #{% endfor %} + #} + + {% if blink1_enabled -%} + - platform: rest + resource: http://localhost:{{ blink1_server_port }}/blink1 + name: blink1 + json_attributes: + - rgb + - bright + value_template: "{%raw%}{{ value_json.rgb }}{%endraw%}" + {% endif %} + binary_sensor: - platform: workday country: DE workdays: [mon, tue, wed, thu, fri] excludes: [sat, sun, holiday] + + {% for radiator in hass_radiators -%} + {% if 'status' in radiator -%} + - platform: threshold + entity_id: sensor.radiator_{{ radiator.name }}_last_updated + # defined in templates.yaml + name: radiator_{{ radiator.name }}_reporting + # minutes + lower: 10 + {% endif -%} + {% endfor -%} {% for target in hass_ping -%} - platform: ping name: ping_{{ target.name }} host: {{ target.host }} - count: 1 - scan_interval: {{ target.interval_secs }} + count: {{ target.count | default('1') }} + scan_interval: {{ target.interval_secs | default('30') }} + {% endfor %} + +input_select: + heating_mode_test: + name: Heating Mode Test + options: + - "auto" + - "heat" + - "cool" + - "off" + initial: "off" + icon: mdi:thermometer-lines + + +input_number: + heating_setpoint_test: + name: Heating Setpoint Test + icon: mdi:home-thermometer + initial: 0 + min: 0 + max: 35 + step: 0.5 + +input_text: + {% for linux_tracker in hass_linux_presence_trackers -%} + webhook_{{ linux_tracker.name }}: + name: webhook_{{ linux_tracker.name }} + icon: "mdi:laptop" + initial: "inactive" + pattern: "^active|inactive$" {% endfor %} device_tracker: + - platform: bluetooth_le_tracker + interval_seconds: 12 + track_new_devices: false + track_battery: false + consider_home: 150 + new_device_defaults: + track_new_devices: false + - platform: bluetooth_tracker + request_rssi: false + interval_seconds: 12 + track_new_devices: false + consider_home: 150 + new_device_defaults: + track_new_devices: false - platform: ping hosts: {% for target in hass_ping -%} + {% if target.device_tracker|default(true) -%} {{ target.name }}: {{ target.host }} + {% endif -%} {% endfor %} -# enabling bluetooth -bluetooth: influxdb: host: "{{ influxdb_url }}" @@ -186,5 +347,79 @@ influxdb: source: hass home: S21 +matrix: + homeserver: https://{{ matrix_url }} + username: !secret matrix_username + password: !secret matrix_password + rooms: !secret matrix_rooms + commands: + - word: hass + name: hass + +notify: + - name: matrix + platform: matrix + default_room: !secret matrix_default_room + - name: pagerduty + platform: smtp + sender: hass@{{ domain }} + recipient: + - !secret smtp_recipient_email + server: !secret smtp_server + port: {{ smtp_port_starttls }} + username: !secret smtp_username + password: !secret smtp_passwd + encryption: starttls + sender_name: "{{ hass_url }}" + shell_command: matrixmsg: /usr/local/bin/matrixmsg.py + +rest_command: + {% if blink1_enabled -%} + blink1_turn_on: + url: {{ hass_blink1_url }}/blink1/on?bright=250 + #url: http://localhost:{{ blink1_server_port }}/blink1/fadeToRGB?rgb=ff0ff + method: GET + content_type: "application/json" + blink1_turn_off: + url: {{ hass_blink1_url }}/blink1/off + method: GET + content_type: "application/json" + blink1_turn_magenta: + url: {{ hass_blink1_url }}/blink1/fadeToRGB?rgb=ff00ff + method: GET + content_type: "application/json" + blink1_set_color: + url: "{{ hass_blink1_url }}/blink1/fadeToRGB?rgb={%raw%}{{ rgb }}{%endraw%}" + method: GET + {% endif %} + +light: + {% if blink1_enabled -%} + - platform: template + lights: + blink1: !include blink1.yaml + {% endif %} + +# enable 'wake_on_lan' for 'samsungtv' +wake_on_lan: + +samsungtv: + - host: {{ hass_wifi_blackbox.tv_ip }} + name: The TV + turn_on_action: + - service: wake_on_lan.send_magic_packet + data: + mac: "{{ hass_wifi_blackbox.tv_mac }}" + +feedreader: + urls: + {% for item in hass_feedreader -%} + - "{{ item.url | trim }}" + {% endfor %} + +{# logger: + # logs: + # pyatv: debug + # homeassistant.components.apple_tv: debug #} diff --git a/roles/hass/templates/configuration.yaml.j2.save b/roles/hass/templates/configuration.yaml.j2.save new file mode 100644 index 0000000..c93e38f --- /dev/null +++ b/roles/hass/templates/configuration.yaml.j2.save @@ -0,0 +1,410 @@ +# ansible: roles/hass/templates/configuration.yaml.j2 +# +# Loads default set of integrations. Icluding the cloud crap. Do remove. +# havent gotten it to work wthough, hass doesnt load properly +default_config: +# +# the dict contains this: +# https://github.com/home-assistant/core/blob/dev/homeassistant/components/default_config/manifest.json +# +# the cloud thing clistens on (at least) port 42161. +# since we need to run in host mode, and dont have network/port isolation by default +# we'll kill this stuff. +# +# for some reason the settings dialog for it is still at /config/cloud/login, but +# we arent listening on port 42161 anymore (yay!). (but hass doesnt start) +# +# for now we just block the ports with iptables/ufw +# +# config: +# application_credentials: +# automation: +# bluetooth: +# # there is no cloud, just other peoples computers.. +# #cloud: +# counter: +# dhcp: +# energy: +# frontend: +# hardware: +# history: +# homeassistant_alerts: +# input_boolean: +# input_button: +# input_datetime: +# input_number: +# input_select: +# input_text: +# logbook: +# map: +# media_source: +# mobile_app: +# my: +# network: +# person: +# scene: +# schedule: +# script: +# ssdp: +# # kind of undocumented, but didnt help +# stream: +# sun: +# system_health: +# tag: +# timer: +# usb: +# webhook: +# zeroconf: +# zone: + +automation ui: !include automations.yaml +automation ansible: !include automations-ansible-managed.yaml +script: !include scripts.yaml +scene: !include scenes.yaml +template: !include templates.yaml +climate: !include climate.yaml + +# Text to speech +tts: + - platform: voicerss + api_key: !secret voicerss_api_key + - platform: google_translate + - platform: picotts + language: "en-GB" + +calendar: + {% for item in hass_caldav.calendars -%} + - platform: caldav + days: 30 + username: !secret caldav_user + password: !secret caldav_passwd + # {{ item.name | trim }} + url: {{ item.url | trim }} + {% endfor %} + +http: + # container runs with network_mode=host, so no network isolation. the docs say to not + # do this, and it doesnt work as expected either. + # using ufw/iptables for now.... + # + #server_host: 127.0.0.1 + trusted_proxies: + - 127.0.0.1 + use_x_forwarded_for: true + +frontend: + themes: !include_dir_merge_named themes + +panel_custom: + - name: zwave + sidebar_title: Z-Wave + sidebar_icon: mdi:z-wave + js_url: /api/hassio/app/entrypoint.js + url_path: 'config/devices/dashboard?historyBack=1&config_entry=d6e38621854098348266029e18f93048' + embed_iframe: true + require_admin: true + config: + ingress: core_configurator + - name: automations + sidebar_title: Automations + sidebar_icon: mdi:robot + js_url: /api/hassio/app/entrypoint.js + url_path: 'config/automation/dashboard' + embed_iframe: true + require_admin: true + config: + ingress: core_configurator + - name: scripts + sidebar_title: Scripts + sidebar_icon: mdi:script + js_url: /api/hassio/app/entrypoint.js + url_path: 'config/script/dashboard' + embed_iframe: true + require_admin: true + config: + ingress: core_configurator + - name: integrations + sidebar_title: Integrations + sidebar_icon: mdi:integrated-circuit-chip + js_url: /api/hassio/app/entrypoint.js + url_path: 'config/integrations' + embed_iframe: true + require_admin: true + config: + ingress: core_configurator + + +{% set zone = {'zone': hass_zones} %} +{{ zone | to_nice_yaml }} + + +homeassistant: + auth_providers: + - type: command_line + command: /usr/local/bin/authelia-auth.py + args: + - {{ hass_url }} + name: Home + currency: EUR + unit_system: metric + time_zone: "Europe/Berlin" + country: DE + external_url: https://{{ hass_url }} + internal_url: https://{{ hass_url }} + allowlist_external_dirs: + - "/usr/var/media" + allowlist_external_urls: + - "https://{{ static_url }}" + - "https://{{ hass_notflix_url }}" + media_dirs: + media: "/usr/var/media" + customize: + zone.home: + friendly_name: S21 + +lovelace: + mode: storage + dashboards: + lovelace-yaml: + mode: yaml + title: Mini + icon: mdi:view-dashboard + show_in_sidebar: true + require_admin: false + filename: mini.yaml + lovelace-yaml2: + mode: yaml + title: Test + icon: mdi:view-dashboard + show_in_sidebar: true + require_admin: false + filename: ui-test.yaml + + {# resources: + # - url: /config/custom_components/ui_lovelace_minimalist/cards/button-card/button-card.js + # type: module + # - url: /config/custom_components/ui_lovelace_minimalist/cards/button-card/button-card.js + # type: module #} + +recorder: + purge_keep_days: 3 + exclude: + entity_globs: + - binary_sensor.1066d799* + - sensor.1066d799* + - light.1066d799* + +sensor: + # https://www.home-assistant.io/integrations/dwd_weather_warnings/ + # https://www.dwd.de/DE/leistungen/opendata/help/warnungen/warning_codes_pdf.pdf?__blob=publicationFile&v=5 + # https://www.dwd.de/DE/leistungen/opendata/help/warnungen/cap_warncellids_csv.html + # 111000000;Berlin;DE300;Berlin;BXX + # 711000002;Berlin - Friedrichshain-Kreuzberg;;B-Friedrh./Kbg.;BXB + # 711000003;Berlin - Pankow;;B-Pankow;BXG + # 711000011;Berlin - Lichtenberg;;B-Lichtenberg;BXC + # 811000000;Stadt Berlin;;Berlin; + # 911000000;Berlin;;Land Berlin;LBE + # 911100000;Berlin;;Berlin;BXZ + # 995000000;Brandenburg/Berlin;;Berlin/Brandenb;DWPD + + - platform: dwd_weather_warnings + # Berlin - Friedrichshain-Kreuzberg + region_name: 711000002 + + - platform: dwd_weather_warnings + # Berlin - Pankow + region_name: 711000003 + + - platform: dwd_weather_warnings + # Stadt Berlin + region_name: 811000000 + + {% for item in hass_bvg -%} + - platform: bvg_berlin_public_transport + name: {{ item.name }} + stop_id: "{{ item.stop_id }}" + direction: {{ item.direction | trim }} + {% if item.walking_distance is defined -%} + walking_distance: {{ item.walking_distance | trim }} + {% endif -%} + file_path: "/config/bvg/" + {% endfor %} + + - platform: waqi + token: !secret waqi_token + locations: + {% for item in hass_waqi.locations -%} + - "{{ item | trim }}" + {%- endfor +%} + {#stations: + #{% for item in hass_waqi.stations -%} + #- "{{ item | trim }}" + #{% endfor %} + #} + + {% if blink1_enabled -%} + - platform: rest + resource: http://localhost:{{ blink1_server_port }}/blink1 + name: blink1 + json_attributes: + - rgb + - bright + value_template: "{%raw%}{{ value_json.rgb }}{%endraw%}" + {% endif %} + +binary_sensor: + - platform: workday + country: DE + workdays: [mon, tue, wed, thu, fri] + excludes: [sat, sun, holiday] + + {% for radiator in hass_radiators -%} + {% if 'status' in radiator -%} + - platform: threshold + entity_id: sensor.radiator_{{ radiator.name }}_last_updated + # defined in templates.yaml + name: radiator_{{ radiator.name }}_reporting + # minutes + lower: 10 + {% endif -%} + {% endfor -%} + {% for target in hass_ping -%} + - platform: ping + name: ping_{{ target.name }} + host: {{ target.host }} + count: {{ target.count | default('1') }} + scan_interval: {{ target.interval_secs | default('30') }} + {% endfor %} + +input_select: + heating_mode_test: + name: Heating Mode Test + options: + - "auto" + - "heat" + - "cool" + - "off" + initial: "off" + icon: mdi:thermometer-lines + + +input_number: + heating_setpoint_test: + name: Heating Setpoint Test + icon: mdi:home-thermometer + initial: 0 + min: 0 + max: 35 + step: 0.5 + +input_text: + {% for linux_tracker in hass_linux_presence_trackers -%} + webhook_{{ linux_tracker.name }}: + name: webhook_{{ linux_tracker.name }} + icon: "mdi:laptop" + initial: "inactive" + pattern: "^active|inactive$" + {% endfor %} + +device_tracker: + - platform: ping + hosts: + {% for target in hass_ping -%} + {% if target.device_tracker|default(true) -%} + {{ target.name }}: {{ target.host }} + {% endif -%} + {% endfor %} + +influxdb: + host: "{{ influxdb_url }}" + port: 443 + database: hass + username: hass + password: !secret influxdb_pass + ssl: true + verify_ssl: true + max_retries: 3 + default_measurement: state + include: + entities: + - sensor.washing_machine_electric_consumption_w + - sensor.washing_machine_electric_consumption_kwh + tags: + instance: prod + source: hass + home: S21 + +matrix: + homeserver: https://{{ matrix_url }} + username: !secret matrix_username + password: !secret matrix_password + rooms: !secret matrix_rooms + commands: + - word: hass + name: hass + +notify: + - name: matrix + platform: matrix + default_room: !secret matrix_default_room + +shell_command: + matrixmsg: /usr/local/bin/matrixmsg.py + +rest_command: + {% if blink1_enabled -%} + blink1_turn_on: + url: {{ hass_blink1_url }}/blink1/on?bright=250 + #url: http://localhost:{{ blink1_server_port }}/blink1/fadeToRGB?rgb=ff0ff + method: GET + content_type: "application/json" + blink1_turn_off: + url: {{ hass_blink1_url }}/blink1/off + method: GET + content_type: "application/json" + blink1_turn_magenta: + url: {{ hass_blink1_url }}/blink1/fadeToRGB?rgb=ff00ff + method: GET + content_type: "application/json" + blink1_set_color: + url: "{{ hass_blink1_url }}/blink1/fadeToRGB?rgb={%raw%}{{ rgb }}{%endraw%}" + method: GET + {% endif %} + +light: + {% if blink1_enabled -%} + - platform: template + lights: + blink1: !include blink1.yaml + {% endif %} + +# enable 'wake_on_lan' for 'samsungtv' +wake_on_lan: + +samsungtv: + - host: {{ hass_wifi_blackbox.tv_ip }} + name: The TV + turn_on_action: + - service: wake_on_lan.send_magic_packet + data: + mac: "{{ hass_wifi_blackbox.tv_mac }}" + +feedreader: + urls: + {% for item in hass_feedreader -%} + - "{{ item.url | trim }}" + {% endfor %} + +# fixme +device_tracker: + - platform: ubus + host: "192.168.21.1" + username: !secret openwrt_user + password: !secret openwrt_pass + +# fixme +device_tracker: + - platform: unifi_direct + host: Y + username: YOUR_USERNAME + password: YOUR_PASSWORD diff --git a/roles/hass/templates/scripts-ansible-managed.yaml.j2 b/roles/hass/templates/scripts-ansible-managed.yaml.j2 new file mode 100644 index 0000000..e69de29 diff --git a/roles/hass/templates/secrets.yaml.j2 b/roles/hass/templates/secrets.yaml.j2 index 84174ce..f20bdb9 100644 --- a/roles/hass/templates/secrets.yaml.j2 +++ b/roles/hass/templates/secrets.yaml.j2 @@ -3,6 +3,9 @@ # we can "safely" template in the actual secrets here and keep them out of # configuration.yaml, which does get synced to the repo. +openwrt_user: "{{ hass_openwrt_user }}" +openwrt_pass: "{{ hass_openwrt_pass }}" + caldav_user: "{{ hass_caldav.user }}" caldav_passwd: "{{ hass_caldav.passwd }}" @@ -10,3 +13,17 @@ voicerss_api_key: {{ voicerss_api_key }} # see group_vars/monitoring.yml influxdb_pass: {{ hass_influxdb_pass }} + +matrix_username: "{{ hass_matrix.username }}" +matrix_password: "{{ hass_matrix.password }}" +{% set rooms = {'matrix_rooms': hass_matrix.rooms} -%} +{{ rooms | to_nice_yaml }} +matrix_default_room: "{{ hass_matrix.rooms[0] }}" + +smtp_recipient_email: "{{ pagerduty_email }}" +smtp_server: "{{ smtp_server }}" +smtp_username: "{{ smtp_username }}" +smtp_passwd: "{{ smtp_passwd }}" + + +waqi_token: {{ hass_waqi.token }} diff --git a/roles/hass/templates/templates.yaml.j2 b/roles/hass/templates/templates.yaml.j2 new file mode 100644 index 0000000..b4c7f0e --- /dev/null +++ b/roles/hass/templates/templates.yaml.j2 @@ -0,0 +1,126 @@ +- sensor: + - name: "chance_of_rain" + unit_of_measurement: "%" + icon: "mdi:weather-pouring" + state: >- + {% raw -%} + {% set ipma_2h = state_attr('weather.ipma_hourly_home', 'forecast')[:2] | map(attribute='precipitation_probability') %} + {% set ipma = state_attr('weather.ipma_hourly_home', 'forecast')[0]['precipitation_probability'] | int %} + {% set owm = states('sensor.owm_home_forecast_precipitation_probability') | int %} + {{ [owm, ipma, 0] | max | int }} + {% endraw %} + + {% for radiator in hass_radiators %} + {% if 'status' in radiator %} + + - name: "radiator_{{ radiator.name }}_last_updated" + unit_of_measurement: "minutes" + icon: "mdi:update" + device_class: duration + state: >- + {% raw %} {% {% endraw -%} + set since_last_update = now() - states.{{ radiator.status }}.last_updated + {%- raw %} %} {% endraw %} + + {% raw %} {{ {% endraw -%} + since_last_update.seconds|int // 60 + {%- raw %} }} {% endraw -%} + {% endif %} + {% endfor %} + {% for linux_tracker in hass_linux_presence_trackers -%} + {% endfor %} + +- binary_sensor: + {% if blink1_enabled -%} + - name: "blink1_on" + device_class: light + state: >- + {% raw -%} + {{ state_attr('sensor.blink1', 'rgb') != "#000000" }} + {% endraw %} + {% endif %} + + - name: "heating_on" + icon: "mdi:home-thermometer" + device_class: heat + state: >- + {% raw -%} + {% set all_radiators = states.climate | selectattr("attributes", "defined") | map(attribute="attributes") | selectattr("temperature", "defined") | map(attribute="temperature") | default([]) | list %} + {% set max_radiator_temp = all_radiators | max | default(0.0) | float %} + {% set target_temp = states('input_number.target_temp_heat') | default(0.0) | float %} + {{ max_radiator_temp >= target_temp }} + {% endraw %} + + - name: s21_anyone_home + icon: "mdi:home-account" + attributes: + friendly_name: "Anyone home" + state: >- + {% raw -%} + {{ state_attr("zone.home", "persons") | default([]) | length > 0 }} + {% endraw %} + + - name: doorbell_buzzer + state: >- + {% raw %} {{ is_state("switch.doorbell_buzzer", "on") }} {% endraw +%} + icon: >- + {% raw -%} + {% if is_state("switch.doorbell_buzzer", "on") %} + mdi:electric-switch-closed + {% else %} + mdi:electric-switch + {% endif %} + {% endraw %} + + - name: washing_machine_on + icon: "mdi:washing-machine" + device_class: running + delay_on: "00:00:05" + delay_off: "00:00:05" + state: >- + {% raw -%} + {% set current = states('sensor.washing_machine_electric_a') %} + {% set power = states('sensor.washing_machine_electric_w') %} + {% if current == "unavailable" or power == "unavailable" %} + {{ false }} + {% else %} + {{ current|default(0.0)|float > 0.14 or power|default(0.0)|float > 2.8 }} + {% endif %} + {% endraw %} + + {% for linux_tracker in hass_linux_presence_trackers -%} + - name: {{ linux_tracker.name }}_active + icon: "mdi:laptop" + state: >- + {% raw -%} {% {% endraw %} set state_text = states('input_text.webhook_{{ linux_tracker.name }}') {%raw%} %} {%endraw%} + {% raw %} + {{ state_text == "active" }} + {% endraw %} + + - name: "{{ linux_tracker.name }}_webhook_triggering" + state: >- + {% raw %} {% {% endraw -%} + set since_last_triggered = now() - state_attr('automation.webhook_presence_trackers_{{ linux_tracker.name }}', 'last_triggered') + {%- raw %} %} {% endraw %} + + {% raw %} {{ {% endraw -%} + since_last_triggered.seconds|int < 666 + {%- raw %} }} {% endraw -%} + + {% endfor %} + +- button: + name: doorbell_buzzer + icon: >- + {% raw -%} + {% if is_state("switch.doorbell_buzzer", "on") %} + mdi:electric-switch-closed + {% else %} + mdi:electric-switch + {% endif %} + {% endraw +%} + press: + - service: script.toggle_switch_like_button + data: + target_switch: switch.doorbell_buzzer + press_for_ms: 200 diff --git a/roles/home/meta/main.yml b/roles/home/meta/main.yml new file mode 100644 index 0000000..ca65528 --- /dev/null +++ b/roles/home/meta/main.yml @@ -0,0 +1,7 @@ +--- + +dependencies: + - unifi + - sudoisbot + - hass + - homeaudio diff --git a/roles/homeaudio/meta/main.yml b/roles/homeaudio/meta/main.yml new file mode 100644 index 0000000..3947289 --- /dev/null +++ b/roles/homeaudio/meta/main.yml @@ -0,0 +1,7 @@ +--- + +dependencies: + - podgrab + - airconnect + - shairplay + - owntone diff --git a/roles/owntone/handlers/main.yml b/roles/owntone/handlers/main.yml new file mode 100644 index 0000000..34e6178 --- /dev/null +++ b/roles/owntone/handlers/main.yml @@ -0,0 +1,12 @@ +--- + +- name: reload nginx + service: + name: nginx + state: reloaded + +- name: restart owntone container + docker_container: + name: hass + state: started + restart: true diff --git a/roles/owntone/tasks/main.yml b/roles/owntone/tasks/main.yml new file mode 100644 index 0000000..87b689c --- /dev/null +++ b/roles/owntone/tasks/main.yml @@ -0,0 +1,4 @@ +--- + +- import_tasks: owntone.yml + tags: owntone diff --git a/roles/owntone/tasks/owntone.yml b/roles/owntone/tasks/owntone.yml new file mode 100644 index 0000000..6efbee3 --- /dev/null +++ b/roles/owntone/tasks/owntone.yml @@ -0,0 +1,145 @@ +--- + +- name: create dir structure + file: + state: directory + path: "{{ owntone_path }}/{{ item.name }}" + mode: "{{ item.mode | default('0770') }}" + owner: "{{ owntone_user.uid }}" + group: "{{ owntone_group.gid }}" + tags: + - owntone-dirs + loop_control: + label: "{{ item.name }}" + with_items: + - name: '' + - name: config + - name: log + - name: audio + - name: audio/local_music + - name: audio/playlists + - name: audio/compilations + - name: audio/pipes + #- name: audio/lidarr + #- name: audio/audiobooks + #- name: audio/podcasts + +- name: create input pipe + command: + cmd: mkfifo "{{ owntone_path }}/audio/pipes/{{ item }}" + creates: "{{ owntone_path }}/audio/pipes/{{ item }}" + become_user: "{{ owntone_user.username }}" + loop_control: + label: "{{ item }}" + with_items: + - shairport-output.fifo + - shairport-metadata.fifo + tags: + - input.fifo + +- name: install certs + copy: + src: "/usr/local/etc/letsencrypt/live/{{ item }}" + dest: "/usr/local/etc/certs/" + owner: root + group: root + mode: 0755 + tags: + - letsencrypt-certs + notify: reload nginx + vars: + prediff_cmd: echo + with_items: + - "{{ owntone_url }}" + +- name: template nginx vhost + template: + src: 01-owntone.conf.j2 + dest: /etc/nginx/sites-enabled/01-owntone.conf + owner: root + group: root + mode: 0644 + tags: + - nginx + - owntone-nginx + notify: reload nginx + +- name: template config file + template: + src: owntone.conf.j2 + dest: "{{ owntone_path }}/config/owntone.conf" + owner: "{{ owntone_user.uid }}" + group: "{{ owntone_group.gid }}" + mode: 0644 + notify: + - restart owntone container + tags: + - owntone.conf + +- name: cron file + template: + src: owntone-cron.j2 + dest: /etc/cron.d/owntone + owner: root + group: root + mode: 0600 + tags: + - cron + - owntone-cron + +- name: install utils for tagging + apt: + name: + + - id3v2 + - ffmpeg + state: present + tags: + - packages + +- name: install yt-dlp + pip: + name: yt-dlp + state: latest + tags: + - packages + - pip-packages + - yt-dlp + +- name: fuse allow other + lineinfile: + path: /etc/fuse.conf + line: user_allow_other + state: present + +- name: start owntone container + docker_container: + name: owntone + #image: lscr.io/linuxserver/daapd:latest + image: git.sudo.is/ben/owntone:latest + detach: true + pull: true + restart_policy: "unless-stopped" + state: started + container_default_behavior: compatibility + networks_cli_compatible: false + # user: + network_mode: host + env: + VITE_OWNTONE_URL: "https://{{ owntone_url }}" + mounts: + - type: bind + source: "{{ owntone_path }}/config/owntone.conf" + target: "/etc/owntone.conf" + - type: bind + source: "{{ owntone_path }}/config" + target: "/config" + - type: bind + source: "{{ owntone_path }}/log" + target: "/log" + - type: bind + source: "{{ owntone_path }}/audio" + target: "/audio" + tags: + - owntone-container + - docker-containers diff --git a/roles/owntone/templates/01-owntone.conf.j2 b/roles/owntone/templates/01-owntone.conf.j2 new file mode 100644 index 0000000..5d913cc --- /dev/null +++ b/roles/owntone/templates/01-owntone.conf.j2 @@ -0,0 +1,70 @@ +server { + listen 443 ssl http2; + # listen {{ owntone_port_tcp }}; + + {% if inventory_hostname in wg_clients -%} + listen {{ wg_clients[inventory_hostname].ip }}:443 ssl http2; + {% endif -%} + + include /etc/nginx/authelia_internal.conf; + include listen-proxy-protocol.conf; + include /etc/nginx/sudo-known.conf; + + server_name {{ owntone_url }}; + + access_log /var/log/nginx/access_{{ owntone_url }}.log main; + error_log /var/log/nginx/error_{{ owntone_url }}.log warn; + + ssl_certificate /usr/local/etc/certs/{{ owntone_url }}/fullchain.pem; + ssl_certificate_key /usr/local/etc/certs/{{ owntone_url }}/privkey.pem; + + # ! + ssl_protocols TLSv1.2 TLSv1.3; + ssl_prefer_server_ciphers off; + + location / { + sub_filter 'owntone.local:3689' '{{ owntone_url }}'; + sub_filter 'http%3A%2F%2Fowntone.local%3A3689' 'https%3A%2F%2F{{ owntone_url }}'; + sub_filter 'http://owntone.local:3689' 'https://{{ owntone_url }}'; + sub_filter_types '*'; + sub_filter_once off; + + proxy_pass http://127.0.0.1:{{ owntone_port_tcp }}/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } +} +server { + # only needs to be accessed by players that are being streamed to + # maybe need to remove 'http2' + listen {{ ansible_default_ipv4.address }}:{{ owntone_port_ws }} ssl http2; + + server_name {{ owntone_url }}; + + access_log /var/log/nginx/access_{{ owntone_url }}_{{ owntone_port_ws }}.log main; + error_log /var/log/nginx/error_{{ owntone_url }}_{{ owntone_port_ws }}.log warn; + + ssl_certificate /usr/local/etc/certs/{{ owntone_url }}/fullchain.pem; + ssl_certificate_key /usr/local/etc/certs/{{ owntone_url }}/privkey.pem; + + # ! + ssl_protocols TLSv1.2 TLSv1.3; + ssl_prefer_server_ciphers off; + + location / { + proxy_pass http://127.0.0.1:{{ owntone_port_ws }}/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } + +} diff --git a/roles/owntone/templates/owntone-cron.j2 b/roles/owntone/templates/owntone-cron.j2 new file mode 100644 index 0000000..b79dde9 --- /dev/null +++ b/roles/owntone/templates/owntone-cron.j2 @@ -0,0 +1,7 @@ +# {{ ansible_managed }} + +# m h dom mon dow + +*/60 * * * * {{ owntone_user.username }} touch {{ owntone_path }}/audio/local_music/trigger.init-rescan + +# diff --git a/roles/owntone/templates/owntone-mathom.conf b/roles/owntone/templates/owntone-mathom.conf new file mode 100644 index 0000000..7f54a58 --- /dev/null +++ b/roles/owntone/templates/owntone-mathom.conf @@ -0,0 +1,452 @@ +# A quick guide to configuring OwnTone: +# +# For regular use, the most important setting to configure is "directories", +# which should be the location of your media. Whatever user you have set as +# "uid" must have read access to this location. If the location is a network +# mount, please see the README. +# +# In all likelihood, that's all you need to do! + +general { + # Username + # Make sure the user has read access to the library directories you set + # below, and full access to the databases, log and local audio + uid = "abc" + + # Database location + db_path = "/config/dbase_and_logs/songs3.db" + + # Database backup location + # Uncomment and specify a full path to enable abilty to use REST endpoint + # to initiate backup of songs3.db + #db_backup_path = "/var/cache/owntone/songs3.bak" + + # Log file and level + # Available levels: fatal, log, warning, info, debug, spam + logfile = "/config/dbase_and_logs/owntone.log" + loglevel = log + + # Admin password for the web interface + # Note that access to the web interface from computers in + # "trusted_network" (see below) does not require password + #admin_password = "" + + # Websocket port for the web interface. + websocket_port = 3688 + + # Websocket interface to bind listener to (e.g. "eth0"). Default is + # disabled, which means listen on all interfaces. + #websocket_interface = "" + + # Sets who is allowed to connect without authorisation. This applies to + # client types like Remotes, DAAP clients (iTunes) and to the web + # interface. Options are "any", "localhost" or the prefix to one or + # more ipv4/6 networks. The default is { "localhost", "192.168", "fd" } + trusted_networks = { "localhost", "192.168.21", "10.102.47", "fd" } + + # Enable/disable IPv6 + ipv6 = no + + # Set this if you want the server to bind to a specific IP address. Can + # be ipv6 or ipv4. Default (commented out or "::") is to listen on all + # IP addresses. + #bind_address = "::" + + # Location of cache database + cache_path = "/config/dbase_and_logs/cache.db" + + # DAAP requests that take longer than this threshold (in msec) get their + # replies cached for next time. Set to 0 to disable caching. + #cache_daap_threshold = 1000 + + # When starting playback, autoselect speaker (if none of the previously + # selected speakers/outputs are available) + #speaker_autoselect = no + + # Most modern systems have a high-resolution clock, but if you are on an + # unusual platform and experience audio drop-outs, you can try changing + # this option + #high_resolution_clock = yes +} + +# Library configuration +library { + # Name of the library as displayed by the clients (%h: hostname). If you + # change the name after pairing with Remote you may have to re-pair. + name = "audio.sudo.is" + + # TCP port to listen on. Default port is 3689 (daap) + port = 3689 + + # Password for the library. Optional. + #password = "" + + # Directories to index + directories = { "/music" } + + # Follow symlinks. Default: true. + #follow_symlinks = true + + # Directories containing podcasts + # For each directory that is indexed the path is matched against these + # names. If there is a match all items in the directory are marked as + # podcasts. Eg. if you index /music, and your podcasts are in + # /music/Podcasts, you can set this to "/Podcasts". + # (changing this setting only takes effect after rescan, see the README) + podcasts = { "/podcasts" } + + # Directories containing audiobooks + # For each directory that is indexed the path is matched against these + # names. If there is a match all items in the directory are marked as + # audiobooks. + # (changing this setting only takes effect after rescan, see the README) + audiobooks = { "/audiobooks" } + + # Directories containing compilations (eg soundtracks) + # For each directory that is indexed the path is matched against these + # names. If there is a match all items in the directory are marked as + # compilations. + # (changing this setting only takes effect after rescan, see the README) + compilations = { "/compilations" } + + # Compilations usually have many artists, and sometimes no album artist. + # If you dont want every artist to be listed in artist views, you can + # set a single name which will be used for all compilation tracks + # without an album artist, and for all tracks in the compilation + # directories. + # (changing this setting only takes effect after rescan, see the README) + compilation_artist = "Various Artists" + + # If your album and artist lists are cluttered, you can choose to hide + # albums and artists with only one track. The tracks will still be + # visible in other lists, e.g. songs and playlists. This setting + # currently only works in some remotes. + #hide_singles = false + + # Internet streams in your playlists will by default be shown in the + # "Radio" library, like iTunes does. However, some clients (like + # TunesRemote+) wont show the "Radio" library. If you would also like + # to have them shown like normal playlists, you can enable this option. + radio_playlists = true + + # These are the default playlists. If you want them to have other names, + # you can set it here. + #name_library = "Library" + #name_music = "Music" + #name_movies = "Movies" + #name_tvshows = "TV Shows" + #name_podcasts = "Podcasts" + #name_audiobooks = "Audiobooks" + #name_radio = "Radio" + + # Artwork file names (without file type extension) + # OwnTone will look for jpg and png files with these base names + artwork_basenames = { "artwork", "cover", "Folder" } + + # Enable searching for artwork corresponding to each individual media + # file instead of only looking for album artwork. This is disabled by + # default to reduce cache size. + #artwork_individual = false + + # File types the scanner should ignore + # Non-audio files will never be added to the database, but here you + # can prevent the scanner from even probing them. This might improve + # scan time. By default .db, .ini, .db-journal, .pdf and .metadata are + # ignored. + #filetypes_ignore = { ".db", ".ini", ".db-journal", ".pdf", ".metadata" } + + # File paths the scanner should ignore + # If you want to exclude files on a more advanced basis you can enter + # one or more POSIX regular expressions, and any file with a matching + # path will be ignored. + #filepath_ignore = { "myregex" } + + # Disable startup file scanning + # When OwnTone starts it will do an initial file scan of your + # library (and then watch it for changes). If you are sure your library + # never changes while OwnTone is not running, you can disable the + # initial file scan and save some system ressources. Disabling this scan + # may lead to OwnTones database coming out of sync with the + # library. If that happens read the instructions in the README on how + # to trigger a rescan. + #filescan_disable = false + + # Should metadata from m3u playlists, e.g. artist and title in EXTINF, + # override the metadata we get from radio streams? + #m3u_overrides = false + + # Should iTunes metadata override ours? + itunes_overrides = true + + # Should we import the content of iTunes smart playlists? + #itunes_smartpl = false + + # Decoding options for DAAP clients + # Since iTunes has native support for mpeg, mp4a, mp4v, alac and wav, + # such files will be sent as they are. Any other formats will be decoded + # to raw wav. If OwnTone detects a non-iTunes DAAP client, it is + # assumed to only support mpeg and wav, other formats will be decoded. + # Here you can change when to decode. Note that these settings have no + # effect on AirPlay. + # Formats: mp4a, mp4v, mpeg, alac, flac, mpc, ogg, wma, wmal, wmav, aif, wav + # Formats that should never be decoded + #no_decode = { "format", "format" } + # Formats that should always be decoded + #force_decode = { "format", "format" } + + # Watch named pipes in the library for data and autostart playback when + # there is data to be read. To exclude specific pipes from watching, + # consider using the above _ignore options. + #pipe_autostart = true + + # Enable automatic rating updates + # If enabled, rating is automatically updated after a song has either been + # played or skipped (only skipping to the next song is taken into account). + # The calculation is taken from the beets plugin "mpdstats" (see + # https://beets.readthedocs.io/en/latest/plugins/mpdstats.html). + # It consist of calculating a stable rating based only on the play- and + # skipcount and a rolling rating based on the current rating and the action + # (played or skipped). Both results are combined with a mix-factor of 0.75: + # new rating = 0.75 * stable rating + 0.25 * rolling rating) + #rating_updates = false + + # Allows creating, deleting and modifying m3u playlists in the library directories. + # Only supported by the player web interface and some mpd clients + # Defaults to being disabled. + allow_modifying_stored_playlists = true + + # A directory in one of the library directories that will be used as the default + # playlist directory. OwnTone creates new playlists in this directory if only + # a playlist name is provided (requires "allow_modify_stored_playlists" set to true). + default_playlist_directory = "/music/playlists" + + # By default OwnTone will - like iTunes - clear the playqueue if + # playback stops. Setting clear_queue_on_stop_disable to true will keep + # the playlist like MPD does. Note that some dacp clients do not show + # the playqueue if playback is stopped. + #clear_queue_on_stop_disable = false +} + +# Local audio output +audio { + # Name - used in the speaker list in Remote + nickname = "Computer" + + # Type of the output (alsa, pulseaudio, dummy or disabled) + #type = "alsa" + + # For pulseaudio output, an optional server hostname or IP can be + # specified (e.g. "localhost"). If not set, connection is made via local + # socket. + #server = "" + + # Audio PCM device name for local audio output - ALSA only + #card = "default" + + # Mixer channel to use for volume control - ALSA only + # If not set, PCM will be used if available, otherwise Master. + #mixer = "" + + # Mixer device to use for volume control - ALSA only + # If not set, the value for "card" will be used. + #mixer_device = "" + + # Enable or disable audio resampling to keep local audio in sync with + # e.g. Airplay. This feature relies on accurate ALSA measurements of + # delay, and some devices dont provide that. If that is the case you + # are better off disabling the feature. + #sync_disable = false + + # Here you can adjust when local audio is started relative to other + # speakers, e.g. Airplay. Negative values correspond to moving local + # audio ahead, positive correspond to delaying it. The unit is + # milliseconds. The offset must be between -1000 and 1000 (+/- 1 sec). + #offset_ms = 0 + + # To calculate what and if resampling is required, local audio delay is + # measured each second. After a period the collected measurements are + # used to estimate drift and latency, which determines if corrections + # are required. This setting sets the length of that period in seconds. + #adjust_period_seconds = 100 +} + +# ALSA device settings +# If you have multiple ALSA devices you can configure them individually via +# sections like the below. Make sure to set the "card name" correctly. See the +# README about ALSA for details. Note that these settings will override the ALSA +# settings in the "audio" section above. +#alsa "card name" { + # Name used in the speaker list. If not set, the card name will be used. + #nickname = "Computer" + + # Mixer channel to use for volume control + # If not set, PCM will be used if available, otherwise Master + #mixer = "" + + # Mixer device to use for volume control + # If not set, the card name will be used + #mixer_device = "" +# } + +# Pipe output +# Allows OwnTone to output audio data to a named pipe +#fifo { + #nickname = "fifo" + #path = "/path/to/fifo" +# } + +# AirPlay settings common to all devices +#airplay_shared { + # UDP ports used when airplay devices make connections back to + # OwnTone (choosing specific ports may be helpful when running + # OwnTone behind a firewall) + # control_port = 0 + # timing_port = 0 +# } + +# AirPlay per device settings +# (make sure you get the capitalization of the device name right) +#airplay "My AirPlay device" { + # OwnTone's volume goes to 11! If that's more than you can handle + # you can set a lower value here + #max_volume = 11 + + # Enable this option to exclude a particular AirPlay device from the + # speaker list + #exclude = false + + # Enable this option to keep a particular AirPlay device in the speaker + # list and thus ignore mdns notifications about it no longer being + # present. The speaker will remain until restart of OwnTone. + #permanent = false + + # Some devices spuriously disconnect during playback, and based on the + # device type OwnTone may attempt to reconnect. Setting this option + # overrides this so reconnecting is either always enabled or disabled. + #reconnect = false + + # AirPlay password + #password = "s1kr3t" + + # Disable AirPlay 1 (RAOP) + #raop_disable = false + + # Name used in the speaker list, overrides name from the device + #nickname = "My speaker name" +# } + +# Chromecast settings +# (make sure you get the capitalization of the device name right) +#chromecast "My Chromecast device" { + # OwnTone's volume goes to 11! If that's more than you can handle + # you can set a lower value here + #max_volume = 11 + + # Enable this option to exclude a particular device from the speaker + # list + #exclude = false + + # Name used in the speaker list, overrides name from the device + #nickname = "My speaker name" +# } + +# Spotify settings (only have effect if Spotify enabled - see README/INSTALL) +spotify { + # Set preferred bitrate for music streaming + # 0: No preference (default), 1: 96kbps, 2: 160kbps, 3: 320kbps + #bitrate = 0 + + # Your Spotify playlists will by default be put in a "Spotify" playlist + # folder. If you would rather have them together with your other + # playlists you can set this option to true. + #base_playlist_disable = false + + # Spotify playlists usually have many artist, and if you dont want + # every artist to be listed when artist browsing in Remote, you can set + # the artist_override flag to true. This will use the compilation_artist + # as album artist for Spotify items. + #artist_override = false + + # Similar to the different artists in Spotify playlists, the playlist + # items belong to different albums, and if you do not want every album + # to be listed when browsing in Remote, you can set the album_override + # flag to true. This will use the playlist name as album name for + # Spotify items. Notice that if an item is in more than one playlist, + # it will only appear in one album when browsing (in which album is + # random). + #album_override = false +} + +# RCP/Roku Soundbridge output settings +# (make sure you get the capitalization of the device name right) +#rcp "My SoundBridge device" { + # Enable this option to exclude a particular device from the speaker + # list + #exclude = false + + # A Roku/SoundBridge can power up in 2 modes: (default) reconnect to the + # previously used library (ie OwnTone) or in a 'cleared library' mode. + # The Roku power up behaviour is affected by how OwnTone disconnects + # from the Roku device. + # + # Set to false to maintain default Roku power on behaviour + #clear_on_close = false +# } + + +# MPD configuration (only have effect if MPD enabled - see README/INSTALL) +mpd { + # TCP port to listen on for MPD client requests. + # Default port is 6600, set to 0 to disable MPD support. + #port = 6600 + + # HTTP port to listen for artwork requests (only supported by some MPD + # clients and will need additional configuration in the MPD client to + # work). Set to 0 to disable serving artwork over http. + #http_port = 0 +} + +# SQLite configuration (allows to modify the operation of the SQLite databases) +# Make sure to read the SQLite documentation for the corresponding PRAGMA +# statements as changing them from the defaults may increase the possibility of +# database corruptions! By default the SQLite default values are used. +sqlite { + # Cache size in number of db pages for the library database + # (SQLite default page size is 1024 bytes and cache size is 2000 pages) + #pragma_cache_size_library = 2000 + + # Cache size in number of db pages for the daap cache database + # (SQLite default page size is 1024 bytes and cache size is 2000 pages) + #pragma_cache_size_cache = 2000 + + # Sets the journal mode for the database + # DELETE (default), TRUNCATE, PERSIST, MEMORY, WAL, OFF + #pragma_journal_mode = DELETE + + # Change the setting of the "synchronous" flag + # 0: OFF, 1: NORMAL, 2: FULL (default) + #pragma_synchronous = 2 + + # Number of bytes set aside for memory-mapped I/O for the library database + # (requires sqlite 3.7.17 or later) + # 0: disables mmap (default), any other value > 0: number of bytes for mmap + #pragma_mmap_size_library = 0 + + # Number of bytes set aside for memory-mapped I/O for the cache database + # (requires sqlite 3.7.17 or later) + # 0: disables mmap (default), any other value > 0: number of bytes for mmap + #pragma_mmap_size_cache = 0 + + # Should the database be vacuumed on startup? (increases startup time, + # but may reduce database size). Default is yes. + #vacuum = yes +} + +# Streaming audio settings for remote connections (ie stream.mp3) +streaming { + # Sample rate, typically 44100 or 48000 + #sample_rate = 44100 + + # Set the MP3 streaming bit rate (in kbps), valid options: 64 / 96 / 128 / 192 / 320 + bit_rate = 320 +} diff --git a/roles/owntone/templates/owntone.conf.j2 b/roles/owntone/templates/owntone.conf.j2 new file mode 100644 index 0000000..8e0214b --- /dev/null +++ b/roles/owntone/templates/owntone.conf.j2 @@ -0,0 +1,472 @@ +# A quick guide to configuring OwnTone: +# +# For regular use, the most important setting to configure is "directories", +# which should be the location of your media. Whatever user you have set as +# "uid" must have read access to this location. If the location is a network +# mount, please see the README. +# +# In all likelihood, that's all you need to do! + +general { + # Username + # Make sure the user has read access to the library directories you set + # below, and full access to the databases, log and local audio + uid = "owntone" + + db_path = "/config/dbase_and_logs/songs3.db" + + # Database backup location + # Uncomment and specify a full path to enable abilty to use REST endpoint + # to initiate backup of songs3.db + #db_backup_path = "/var/cache/owntone/songs3.bak" + + # Log file and level + # Available levels: fatal, log, warning, info, debug, spam + logfile = "/log/owntone.log" + loglevel = log + + # Admin password for the web interface + # Note that access to the web interface from computers in + # "trusted_network" (see below) does not require password + admin_password = "{{ owntone_admin_pass }}" + + # Websocket port for the web interface. + websocket_port = {{ owntone_port_ws }} + + # Websocket interface to bind listener to (e.g. "eth0"). Default is + # disabled, which means listen on all interfaces. + websocket_interface = "{{ owntone_interface }}" + + # Sets who is allowed to connect without authorisation. This applies to + # client types like Remotes, DAAP clients (iTunes) and to the web + # interface. Options are "any", "localhost" or the prefix to one or + # more ipv4/6 networks. The default is { "localhost", "192.168", "fd" } + {% set s21 = s21_cidr.split(".")[:3] | join(".") -%} + trusted_networks = { "localhost", "{{ s21 }}", "fd" } + + # Enable/disable IPv6 + ipv6 = no + + # Set this if you want the server to bind to a specific IP address. Can + # be ipv6 or ipv4. Default (commented out or "::") is to listen on all + # IP addresses. + #bind_address = "{{ owntone_interface }}" + + # Location of cache database + cache_path = "/config/dbase_and_logs/cache.db" + + # DAAP requests that take longer than this threshold (in msec) get their + # replies cached for next time. Set to 0 to disable caching. + #cache_daap_threshold = 1000 + + # When starting playback, autoselect speaker (if none of the previously + # selected speakers/outputs are available) + #speaker_autoselect = no + + # Most modern systems have a high-resolution clock, but if you are on an + # unusual platform and experience audio drop-outs, you can try changing + # this option + #high_resolution_clock = yes +} + +# Library configuration +library { + # Name of the library as displayed by the clients (%h: hostname). If you + # change the name after pairing with Remote you may have to re-pair. + name = "{{ owntone_url }}" + + # TCP port to listen on. Default port is 3689 (daap) + port = {{ owntone_port_tcp }} + + # Password for the library. Optional. + #password = "{{ owntone_library_pass }}" + + # Directories to index + directories = { "/audio" } + + # Follow symlinks. Default: true. + #follow_symlinks = true + + # Directories containing podcasts + # For each directory that is indexed the path is matched against these + # names. If there is a match all items in the directory are marked as + # podcasts. Eg. if you index /music, and your podcasts are in + # /music/Podcasts, you can set this to "/Podcasts". + # (changing this setting only takes effect after rescan, see the README) + podcasts = { "/podcasts" } + + # Directories containing audiobooks + # For each directory that is indexed the path is matched against these + # names. If there is a match all items in the directory are marked as + # audiobooks. + # (changing this setting only takes effect after rescan, see the README) + audiobooks = { "/audiobooks" } + + # Directories containing compilations (eg soundtracks) + # For each directory that is indexed the path is matched against these + # names. If there is a match all items in the directory are marked as + # compilations. + # (changing this setting only takes effect after rescan, see the README) + compilations = { "/compilations" } + + # Compilations usually have many artists, and sometimes no album artist. + # If you dont want every artist to be listed in artist views, you can + # set a single name which will be used for all compilation tracks + # without an album artist, and for all tracks in the compilation + # directories. + # (changing this setting only takes effect after rescan, see the README) + compilation_artist = "Various Artists" + + # If your album and artist lists are cluttered, you can choose to hide + # albums and artists with only one track. The tracks will still be + # visible in other lists, e.g. songs and playlists. This setting + # currently only works in some remotes. + #hide_singles = false + + # Internet streams in your playlists will by default be shown in the + # "Radio" library, like iTunes does. However, some clients (like + # TunesRemote+) wont show the "Radio" library. If you would also like + # to have them shown like normal playlists, you can enable this option. + radio_playlists = false + + # These are the default playlists. If you want them to have other names, + # you can set it here. + #name_library = "Library" + #name_music = "Music" + #name_movies = "Movies" + #name_tvshows = "TV Shows" + #name_podcasts = "Podcasts" + #name_audiobooks = "Audiobooks" + #name_radio = "Radio" + + # Artwork file names (without file type extension) + # OwnTone will look for jpg and png files with these base names + artwork_basenames = { "artwork", "cover", "Folder" } + + # Enable searching for artwork corresponding to each individual media + # file instead of only looking for album artwork. This is disabled by + # default to reduce cache size. + artwork_individual = true + + # File types the scanner should ignore + # Non-audio files will never be added to the database, but here you + # can prevent the scanner from even probing them. This might improve + # scan time. By default .db, .ini, .db-journal, .pdf and .metadata are + # ignored. + #filetypes_ignore = { ".db", ".ini", ".db-journal", ".pdf", ".metadata" } + + # File paths the scanner should ignore + # If you want to exclude files on a more advanced basis you can enter + # one or more POSIX regular expressions, and any file with a matching + # path will be ignored. + #filepath_ignore = { "myregex" } + + # Disable startup file scanning + # When OwnTone starts it will do an initial file scan of your + # library (and then watch it for changes). If you are sure your library + # never changes while OwnTone is not running, you can disable the + # initial file scan and save some system ressources. Disabling this scan + # may lead to OwnTones database coming out of sync with the + # library. If that happens read the instructions in the README on how + # to trigger a rescan. + #filescan_disable = false + + # Should metadata from m3u playlists, e.g. artist and title in EXTINF, + # override the metadata we get from radio streams? + #m3u_overrides = false + + # Should iTunes metadata override ours? + itunes_overrides = {{ owntone_itunes_metadata_override | default(false) }} + + # Should we import the content of iTunes smart playlists? + #itunes_smartpl = false + + # Decoding options for DAAP clients + # Since iTunes has native support for mpeg, mp4a, mp4v, alac and wav, + # such files will be sent as they are. Any other formats will be decoded + # to raw wav. If OwnTone detects a non-iTunes DAAP client, it is + # assumed to only support mpeg and wav, other formats will be decoded. + # Here you can change when to decode. Note that these settings have no + # effect on AirPlay. + # Formats: mp4a, mp4v, mpeg, alac, flac, mpc, ogg, wma, wmal, wmav, aif, wav + # Formats that should never be decoded + #no_decode = { "format", "format" } + # Formats that should always be decoded + #force_decode = { "format", "format" } + + # Watch named pipes in the library for data and autostart playback when + # there is data to be read. To exclude specific pipes from watching, + # consider using the above _ignore options. + pipe_autostart = true + + # Enable automatic rating updates + # If enabled, rating is automatically updated after a song has either been + # played or skipped (only skipping to the next song is taken into account). + # The calculation is taken from the beets plugin "mpdstats" (see + # https://beets.readthedocs.io/en/latest/plugins/mpdstats.html). + # It consist of calculating a stable rating based only on the play- and + # skipcount and a rolling rating based on the current rating and the action + # (played or skipped). Both results are combined with a mix-factor of 0.75: + # new rating = 0.75 * stable rating + 0.25 * rolling rating) + #rating_updates = false + + # Allows creating, deleting and modifying m3u playlists in the library directories. + # Only supported by the player web interface and some mpd clients + # Defaults to being disabled. + allow_modifying_stored_playlists = true + + # A directory in one of the library directories that will be used as the default + # playlist directory. OwnTone creates new playlists in this directory if only + # a playlist name is provided (requires "allow_modify_stored_playlists" set to true). + default_playlist_directory = "/audio/playlists" + + # By default OwnTone will - like iTunes - clear the playqueue if + # playback stops. Setting clear_queue_on_stop_disable to true will keep + # the playlist like MPD does. Note that some dacp clients do not show + # the playqueue if playback is stopped. + clear_queue_on_stop_disable = true +} + +# Local audio output +audio { + # Name - used in the speaker list in Remote + nickname = "Computer" + + # Type of the output (alsa, pulseaudio, dummy or disabled) + type = "disabled" + + # For pulseaudio output, an optional server hostname or IP can be + # specified (e.g. "localhost"). If not set, connection is made via local + # socket. + #server = "" + + # Audio PCM device name for local audio output - ALSA only + #card = "default" + + # Mixer channel to use for volume control - ALSA only + # If not set, PCM will be used if available, otherwise Master. + #mixer = "" + + # Mixer device to use for volume control - ALSA only + # If not set, the value for "card" will be used. + #mixer_device = "" + + # Enable or disable audio resampling to keep local audio in sync with + # e.g. Airplay. This feature relies on accurate ALSA measurements of + # delay, and some devices dont provide that. If that is the case you + # are better off disabling the feature. + #sync_disable = false + + # Here you can adjust when local audio is started relative to other + # speakers, e.g. Airplay. Negative values correspond to moving local + # audio ahead, positive correspond to delaying it. The unit is + # milliseconds. The offset must be between -1000 and 1000 (+/- 1 sec). + #offset_ms = 0 + + # To calculate what and if resampling is required, local audio delay is + # measured each second. After a period the collected measurements are + # used to estimate drift and latency, which determines if corrections + # are required. This setting sets the length of that period in seconds. + #adjust_period_seconds = 100 +} + +# ALSA device settings +# If you have multiple ALSA devices you can configure them individually via +# sections like the below. Make sure to set the "card name" correctly. See the +# README about ALSA for details. Note that these settings will override the ALSA +# settings in the "audio" section above. +#alsa "card name" { + # Name used in the speaker list. If not set, the card name will be used. + #nickname = "Computer" + + # Mixer channel to use for volume control + # If not set, PCM will be used if available, otherwise Master + #mixer = "" + + # Mixer device to use for volume control + # If not set, the card name will be used + #mixer_device = "" +# } + +# Pipe output +# Allows OwnTone to output audio data to a named pipe +fifo { + nickname = "fifo" + path = "/audio/output.fifo" +} + +# AirPlay settings common to all devices +#airplay_shared { + # UDP ports used when airplay devices make connections back to + # OwnTone (choosing specific ports may be helpful when running + # OwnTone behind a firewall) + # control_port = 0 + # timing_port = 0 +# } + +# AirPlay per device settings +# (make sure you get the capitalization of the device name right) +#airplay "My AirPlay device" { + # OwnTone's volume goes to 11! If that's more than you can handle + # you can set a lower value here + #max_volume = 11 + + # Enable this option to exclude a particular AirPlay device from the + # speaker list + #exclude = false + + # Enable this option to keep a particular AirPlay device in the speaker + # list and thus ignore mdns notifications about it no longer being + # present. The speaker will remain until restart of OwnTone. + #permanent = false + + # Some devices spuriously disconnect during playback, and based on the + # device type OwnTone may attempt to reconnect. Setting this option + # overrides this so reconnecting is either always enabled or disabled. + #reconnect = false + + # AirPlay password + #password = "s1kr3t" + + # Disable AirPlay 1 (RAOP) + #raop_disable = false + + # Name used in the speaker list, overrides name from the device + #nickname = "My speaker name" +# } + +{% for item in owntone_airplay -%} +{% if item.exclude|default(false) -%} +# Excluded AirPlay device: {{ item.name }} +{% endif %} +airplay "{{ item.name }}" { + {% if 'nickname' in item -%} + nickname = "{{ item.nickname }}" + {% endif -%} + {% if 'password' in item -%} + password = "{{ item.password }}" + {% endif -%} + {% if 'max_volume' in item -%} + max_volume = {{ item.max_volume }} + {% endif -%} + exclude = {{ item.exclude|default(false) | lower }} + permanent = {{ item.permanent|default(false) | lower }} + reconnect = {{ item.reconnect|default(false) | lower }} +} +{% endfor %} + +# Chromecast settings +# (make sure you get the capitalization of the device name right) +#chromecast "My Chromecast device" { + # OwnTone's volume goes to 11! If that's more than you can handle + # you can set a lower value here + #max_volume = 11 + + # Enable this option to exclude a particular device from the speaker + # list + #exclude = false + + # Name used in the speaker list, overrides name from the device + #nickname = "My speaker name" +# } + +# Spotify settings (only have effect if Spotify enabled - see README/INSTALL) +spotify { + # Set preferred bitrate for music streaming + # 0: No preference (default), 1: 96kbps, 2: 160kbps, 3: 320kbps + bitrate = 3 + + # Your Spotify playlists will by default be put in a "Spotify" playlist + # folder. If you would rather have them together with your other + # playlists you can set this option to true. + base_playlist_disable = true + + # Spotify playlists usually have many artist, and if you dont want + # every artist to be listed when artist browsing in Remote, you can set + # the artist_override flag to true. This will use the compilation_artist + # as album artist for Spotify items. + artist_override = true + + # Similar to the different artists in Spotify playlists, the playlist + # items belong to different albums, and if you do not want every album + # to be listed when browsing in Remote, you can set the album_override + # flag to true. This will use the playlist name as album name for + # Spotify items. Notice that if an item is in more than one playlist, + # it will only appear in one album when browsing (in which album is + # random). + album_override = true +} + +# RCP/Roku Soundbridge output settings +# (make sure you get the capitalization of the device name right) +#rcp "My SoundBridge device" { + # Enable this option to exclude a particular device from the speaker + # list + #exclude = false + + # A Roku/SoundBridge can power up in 2 modes: (default) reconnect to the + # previously used library (ie OwnTone) or in a 'cleared library' mode. + # The Roku power up behaviour is affected by how OwnTone disconnects + # from the Roku device. + # + # Set to false to maintain default Roku power on behaviour + #clear_on_close = false +# } + + +# MPD configuration (only have effect if MPD enabled - see README/INSTALL) +mpd { + # TCP port to listen on for MPD client requests. + # Default port is 6600, set to 0 to disable MPD support. + port = {{ owntone_port_mpd|default('6600') }} + + # HTTP port to listen for artwork requests (only supported by some MPD + # clients and will need additional configuration in the MPD client to + # work). Set to 0 to disable serving artwork over http. + #http_port = 0 +} + +# SQLite configuration (allows to modify the operation of the SQLite databases) +# Make sure to read the SQLite documentation for the corresponding PRAGMA +# statements as changing them from the defaults may increase the possibility of +# database corruptions! By default the SQLite default values are used. +sqlite { + # Cache size in number of db pages for the library database + # (SQLite default page size is 1024 bytes and cache size is 2000 pages) + #pragma_cache_size_library = 2000 + + # Cache size in number of db pages for the daap cache database + # (SQLite default page size is 1024 bytes and cache size is 2000 pages) + #pragma_cache_size_cache = 2000 + + # Sets the journal mode for the database + # DELETE (default), TRUNCATE, PERSIST, MEMORY, WAL, OFF + #pragma_journal_mode = DELETE + + # Change the setting of the "synchronous" flag + # 0: OFF, 1: NORMAL, 2: FULL (default) + #pragma_synchronous = 2 + + # Number of bytes set aside for memory-mapped I/O for the library database + # (requires sqlite 3.7.17 or later) + # 0: disables mmap (default), any other value > 0: number of bytes for mmap + #pragma_mmap_size_library = 0 + + # Number of bytes set aside for memory-mapped I/O for the cache database + # (requires sqlite 3.7.17 or later) + # 0: disables mmap (default), any other value > 0: number of bytes for mmap + #pragma_mmap_size_cache = 0 + + # Should the database be vacuumed on startup? (increases startup time, + # but may reduce database size). Default is yes. + #vacuum = yes +} + +# Streaming audio settings for remote connections (ie stream.mp3) +streaming { + # Sample rate, typically 44100 or 48000 + #sample_rate = 44100 + + # Set the MP3 streaming bit rate (in kbps), valid options: 64 / 96 / 128 / 192 / 320 + bit_rate = 320 +} diff --git a/roles/sensnet/meta/main.yml b/roles/sensnet/meta/main.yml index 0677e74..ed97d53 100644 --- a/roles/sensnet/meta/main.yml +++ b/roles/sensnet/meta/main.yml @@ -1,7 +1 @@ --- - -dependencies: - - mariadb - - zflux - - sudoisbot - - hass diff --git a/roles/sensors/tasks/sensors.yml b/roles/sensors/tasks/sensors.yml index 646b37b..1296512 100644 --- a/roles/sensors/tasks/sensors.yml +++ b/roles/sensors/tasks/sensors.yml @@ -56,6 +56,191 @@ when: dht_builder|default(False) tags: dht + # https://bluedot.readthedocs.io/en/latest/pairpipi.html#using-the-command-line + # + # $ sudo bluetoothctl + # [bluetooth]# discoverable on + # Changing discoverable on succeeded + # [bluetooth]# pairable on + # Changing pairable on succeeded + # [bluetooth]# agent on + # Agent is already registered + # [bluetooth]# default-agent + # Default agent request successful + # [bluetooth]# scan on + # [bluetooth]# pair EA:26:7D:4A:ED:E4 + # 0a-11e5-a837-0800200c9a66 + # Vendor specific + # [NEW] Descriptor + # /org/bluez/hci0/dev_EA_26_7D_4A_ED_E4/service0009/char000d/desc000f + # 00002901-0000-1000-8000-00805f9b34fb + # Characteristic User Description + # [NEW] Descriptor + # /org/bluez/hci0/dev_EA_26_7D_4A_ED_E4/service0009/char000d/desc0010 + # 00002902-0000-1000-8000-00805f9b34fb + # Client Characteristic Configuration + # [NEW] Characteristic + # /org/bluez/hci0/dev_EA_26_7D_4A_ED_E4/service0009/char0011 + # e6807d23-b90a-11e5-a837-0800200c9a66 + # Vendor specific + # [NEW] Descriptor + # /org/bluez/hci0/dev_EA_26_7D_4A_ED_E4/service0009/char0011/desc0013 + # 00002901-0000-1000-8000-00805f9b34fb + # Characteristic User Description + # [NEW] Characteristic + # /org/bluez/hci0/dev_EA_26_7D_4A_ED_E4/service0009/char0014 + # e6807d24-b90a-11e5-a837-0800200c9a66 + # Vendor specific + # [NEW] Descriptor + # /org/bluez/hci0/dev_EA_26_7D_4A_ED_E4/service0009/char0014/desc0016 + # 00002901-0000-1000-8000-00805f9b34fb + # Characteristic User Description + # [NEW] Descriptor + # /org/bluez/hci0/dev_EA_26_7D_4A_ED_E4/service0009/char0014/desc0017 + # 00002902-0000-1000-8000-00805f9b34fb + # Client Characteristic Configuration + # [NEW] Characteristic + # /org/bluez/hci0/dev_EA_26_7D_4A_ED_E4/service0009/char0018 + # e6807d25-b90a-11e5-a837-0800200c9a66 + # Vendor specific + # [NEW] Descriptor + # /org/bluez/hci0/dev_EA_26_7D_4A_ED_E4/service0009/char0018/desc001a + # 00002901-0000-1000-8000-00805f9b34fb + # Characteristic User Description + # [NEW] Characteristic + # /org/bluez/hci0/dev_EA_26_7D_4A_ED_E4/service0009/char001b + # e6807d26-b90a-11e5-a837-0800200c9a66 + # Vendor specific + # [NEW] Descriptor + # /org/bluez/hci0/dev_EA_26_7D_4A_ED_E4/service0009/char001b/desc001d + # 00002901-0000-1000-8000-00805f9b34fb + # Characteristic User Description + # [NEW] Characteristic + # /org/bluez/hci0/dev_EA_26_7D_4A_ED_E4/service0009/char001e + # e6807d27-b90a-11e5-a837-0800200c9a66 + # Vendor specific + # [NEW] Descriptor + # /org/bluez/hci0/dev_EA_26_7D_4A_ED_E4/service0009/char001e/desc0020 + # 00002901-0000-1000-8000-00805f9b34fb + # Characteristic User Description + # [NEW] Characteristic + # /org/bluez/hci0/dev_EA_26_7D_4A_ED_E4/service0009/char0021 + # e6807e20-b90a-11e5-a837-0800200c9a66 + # Vendor specific + # [NEW] Descriptor + # /org/bluez/hci0/dev_EA_26_7D_4A_ED_E4/service0009/char0021/desc0023 + # 00002901-0000-1000-8000-00805f9b34fb + # Characteristic User Description + # [NEW] Descriptor + # /org/bluez/hci0/dev_EA_26_7D_4A_ED_E4/service0009/char0021/desc0024 + # 00002902-0000-1000-8000-00805f9b34fb + # Client Characteristic Configuration + # [NEW] Characteristic + # /org/bluez/hci0/dev_EA_26_7D_4A_ED_E4/service0009/char0025 + # e6807e21-b90a-11e5-a837-0800200c9a66 + # Vendor specific + # [NEW] Descriptor + # /org/bluez/hci0/dev_EA_26_7D_4A_ED_E4/service0009/char0025/desc0027 + # 00002901-0000-1000-8000-00805f9b34fb + # Characteristic User Description + # [NEW] Characteristic + # /org/bluez/hci0/dev_EA_26_7D_4A_ED_E4/service0009/char0028 + # e6807e24-b90a-11e5-a837-0800200c9a66 + # Vendor specific + # [NEW] Descriptor + # /org/bluez/hci0/dev_EA_26_7D_4A_ED_E4/service0009/char0028/desc002a + # 00002901-0000-1000-8000-00805f9b34fb + # Characteristic User Description + # [NEW] Descriptor + # /org/bluez/hci0/dev_EA_26_7D_4A_ED_E4/service0009/char0028/desc002b + # 00002902-0000-1000-8000-00805f9b34fb + # Client Characteristic Configuration + # [NEW] Characteristic + # /org/bluez/hci0/dev_EA_26_7D_4A_ED_E4/service0009/char002c + # e6807e25-b90a-11e5-a837-0800200c9a66 + # Vendor specific + # [NEW] Descriptor + # /org/bluez/hci0/dev_EA_26_7D_4A_ED_E4/service0009/char002c/desc002e + # 00002901-0000-1000-8000-00805f9b34fb + # Characteristic User Description + # [NEW] Primary Service + # /org/bluez/hci0/dev_EA_26_7D_4A_ED_E4/service002f + # 0000181a-0000-1000-8000-00805f9b34fb + # Environmental Sensing + # [NEW] Characteristic + # /org/bluez/hci0/dev_EA_26_7D_4A_ED_E4/service002f/char0030 + # 00002a6e-0000-1000-8000-00805f9b34fb + # Temperature + # [NEW] Descriptor + # /org/bluez/hci0/dev_EA_26_7D_4A_ED_E4/service002f/char0030/desc0032 + # 00002901-0000-1000-8000-00805f9b34fb + # Characteristic User Description + # [NEW] Descriptor + # /org/bluez/hci0/dev_EA_26_7D_4A_ED_E4/service002f/char0030/desc0033 + # 00002902-0000-1000-8000-00805f9b34fb + # Client Characteristic Configuration + # [NEW] Characteristic + # /org/bluez/hci0/dev_EA_26_7D_4A_ED_E4/service002f/char0034 + # 00002a6f-0000-1000-8000-00805f9b34fb + # Humidity + # [NEW] Descriptor + # /org/bluez/hci0/dev_EA_26_7D_4A_ED_E4/service002f/char0034/desc0036 + # 00002901-0000-1000-8000-00805f9b34fb + # Characteristic User Description + # [NEW] Descriptor + # /org/bluez/hci0/dev_EA_26_7D_4A_ED_E4/service002f/char0034/desc0037 + # 00002902-0000-1000-8000-00805f9b34fb + # Client Characteristic Configuration + # [NEW] Primary Service + # /org/bluez/hci0/dev_EA_26_7D_4A_ED_E4/service0038 + # 0000180a-0000-1000-8000-00805f9b34fb + # Device Information + # [NEW] Characteristic + # /org/bluez/hci0/dev_EA_26_7D_4A_ED_E4/service0038/char0039 + # 00002a29-0000-1000-8000-00805f9b34fb + # Manufacturer Name String + # [NEW] Characteristic + # /org/bluez/hci0/dev_EA_26_7D_4A_ED_E4/service0038/char003b + # 00002a24-0000-1000-8000-00805f9b34fb + # Model Number String + # [NEW] Characteristic + # /org/bluez/hci0/dev_EA_26_7D_4A_ED_E4/service0038/char003d + # 00002a25-0000-1000-8000-00805f9b34fb + # Serial Number String + # [NEW] Characteristic + # /org/bluez/hci0/dev_EA_26_7D_4A_ED_E4/service0038/char003f + # 00002a27-0000-1000-8000-00805f9b34fb + # Hardware Revision String + # [NEW] Characteristic + # /org/bluez/hci0/dev_EA_26_7D_4A_ED_E4/service0038/char0041 + # 00002a26-0000-1000-8000-00805f9b34fb + # Firmware Revision String + # [NEW] Characteristic + # /org/bluez/hci0/dev_EA_26_7D_4A_ED_E4/service0038/char0043 + # 00002a28-0000-1000-8000-00805f9b34fb + # Software Revision String + # [CHG] Device EA:26:7D:4A:ED:E4 UUIDs: 00001800-0000-1000-8000-00805f9b34fb + # [CHG] Device EA:26:7D:4A:ED:E4 UUIDs: 00001801-0000-1000-8000-00805f9b34fb + # [CHG] Device EA:26:7D:4A:ED:E4 UUIDs: 0000180a-0000-1000-8000-00805f9b34fb + # [CHG] Device EA:26:7D:4A:ED:E4 UUIDs: 0000181a-0000-1000-8000-00805f9b34fb + # [CHG] Device EA:26:7D:4A:ED:E4 UUIDs: e6807d20-b90a-11e5-a837-0800200c9a66 + # [CHG] Device EA:26:7D:4A:ED:E4 ServicesResolved: yes + # [CHG] Device EA:26:7D:4A:ED:E4 Appearance: 0x03c0 + # [CHG] Device EA:26:7D:4A:ED:E4 Paired: yes + # Pairing successful + # [Beddit 2564]# quit +- name: set up bluetooth + apt: + name: + - pi-bluetooth + - bluez + - libgirepository1.0-dev + # - bluez-utils + # - bluetooth + # - blueman + when: bluetooth_enabled | default(false) + tags: bluetooth + - name: install fpm with gem gem: name: fpm diff --git a/roles/sudoisbot/meta/main.yml b/roles/sudoisbot/meta/main.yml new file mode 100644 index 0000000..626a065 --- /dev/null +++ b/roles/sudoisbot/meta/main.yml @@ -0,0 +1,5 @@ +--- + +dependencies: + - mariadb + - zflux -- 2.40.1 From f2679067796f5c1f0d56086777fdca46ecf69267 Mon Sep 17 00:00:00 2001 From: Ben Kristinsson Date: Tue, 21 Mar 2023 20:07:42 +0100 Subject: [PATCH 02/10] hass,zwavejs,owntone certs, www.sudo.is redirect to www --- roles/hass/tasks/hass.yml | 7 +++---- roles/hass/templates/01-hass.conf.j2 | 4 ++-- roles/hass/templates/01-zwavejs.conf.j2 | 4 ++-- roles/owntone/tasks/owntone.yml | 2 +- roles/owntone/templates/01-owntone.conf.j2 | 4 ++-- roles/www/templates/01-sudo.is.conf.j2 | 20 +++++++++++++++++++- 6 files changed, 29 insertions(+), 12 deletions(-) diff --git a/roles/hass/tasks/hass.yml b/roles/hass/tasks/hass.yml index b8e1d9a..d34ecb5 100644 --- a/roles/hass/tasks/hass.yml +++ b/roles/hass/tasks/hass.yml @@ -380,8 +380,8 @@ - name: start home-assistant container docker_container: name: hass - #image: ghcr.io/home-assistant/home-assistant:stable - image: git.sudo.is/ben/hass:latest + image: ghcr.io/home-assistant/home-assistant:stable + #image: git.sudo.is/ben/hass:latest detach: true pull: true restart_policy: "unless-stopped" @@ -454,8 +454,7 @@ vars: prediff_cmd: echo with_items: - - "{{ hass_url }}" - - "{{ zwavejs_url }}" + - "{{ domain }}" - name: template nginx vhosts for hass and friends template: diff --git a/roles/hass/templates/01-hass.conf.j2 b/roles/hass/templates/01-hass.conf.j2 index 3fabaa1..8e7c0db 100644 --- a/roles/hass/templates/01-hass.conf.j2 +++ b/roles/hass/templates/01-hass.conf.j2 @@ -137,8 +137,8 @@ server { error_log /var/log/nginx/error_{{ hass_url }}.log warn; ssl_session_timeout 5m; - ssl_certificate /usr/local/etc/certs/{{ hass_url }}/fullchain.pem; - ssl_certificate_key /usr/local/etc/certs/{{ hass_url }}/privkey.pem; + ssl_certificate /usr/local/etc/certs/{{ domain }}/fullchain.pem; + ssl_certificate_key /usr/local/etc/certs/{{ domain }}/privkey.pem; fastcgi_hide_header X-Powered-By; } diff --git a/roles/hass/templates/01-zwavejs.conf.j2 b/roles/hass/templates/01-zwavejs.conf.j2 index 41ed80a..09b6bce 100644 --- a/roles/hass/templates/01-zwavejs.conf.j2 +++ b/roles/hass/templates/01-zwavejs.conf.j2 @@ -40,8 +40,8 @@ server { error_log /var/log/nginx/error_{{ zwavejs_url }}.log warn; ssl_session_timeout 5m; - ssl_certificate /usr/local/etc/certs/{{ zwavejs_url }}/fullchain.pem; - ssl_certificate_key /usr/local/etc/certs/{{ zwavejs_url }}/privkey.pem; + ssl_certificate /usr/local/etc/certs/{{ domain }}/fullchain.pem; + ssl_certificate_key /usr/local/etc/certs/{{ domain }}/privkey.pem; fastcgi_hide_header X-Powered-By; } diff --git a/roles/owntone/tasks/owntone.yml b/roles/owntone/tasks/owntone.yml index 6efbee3..3bc8cb5 100644 --- a/roles/owntone/tasks/owntone.yml +++ b/roles/owntone/tasks/owntone.yml @@ -50,7 +50,7 @@ vars: prediff_cmd: echo with_items: - - "{{ owntone_url }}" + - "{{ domain }}" - name: template nginx vhost template: diff --git a/roles/owntone/templates/01-owntone.conf.j2 b/roles/owntone/templates/01-owntone.conf.j2 index 5d913cc..ef10169 100644 --- a/roles/owntone/templates/01-owntone.conf.j2 +++ b/roles/owntone/templates/01-owntone.conf.j2 @@ -15,8 +15,8 @@ server { access_log /var/log/nginx/access_{{ owntone_url }}.log main; error_log /var/log/nginx/error_{{ owntone_url }}.log warn; - ssl_certificate /usr/local/etc/certs/{{ owntone_url }}/fullchain.pem; - ssl_certificate_key /usr/local/etc/certs/{{ owntone_url }}/privkey.pem; + ssl_certificate /usr/local/etc/certs/{{ domain }}/fullchain.pem; + ssl_certificate_key /usr/local/etc/certs/{{ domain }}/privkey.pem; # ! ssl_protocols TLSv1.2 TLSv1.3; diff --git a/roles/www/templates/01-sudo.is.conf.j2 b/roles/www/templates/01-sudo.is.conf.j2 index e86ea53..818d985 100644 --- a/roles/www/templates/01-sudo.is.conf.j2 +++ b/roles/www/templates/01-sudo.is.conf.j2 @@ -1,8 +1,26 @@ +server { + server_name {%- for d in server_names %} {{ d }}{% endfor %}; + {% if inventory_hostname in wg_clients -%} + listen {{ wg_clients[inventory_hostname].ip }}:443 ssl http2; + {% endif -%} + listen 443 ssl http2; + include listen-proxy-protocol.conf; + + ssl_certificate /usr/local/etc/certs/www.{{ domain }}/fullchain.pem; + ssl_certificate_key /usr/local/etc/certs/www.{{ domain }}/privkey.pem; + + location / { + return 301 https://www.$http_host$request_uri; + } + + access_log /var/log/nginx/access_{{ domain }}.log main; + error_log /var/log/nginx/error_{{ domain }}.log warn; +} server { - server_name {%- for d in server_names %} www.{{ d }} {{ d }}{% endfor %}; + server_name {%- for d in server_names %} www.{{ d }}{% endfor %}; {% if inventory_hostname in wg_clients -%} listen {{ wg_clients[inventory_hostname].ip }}:443 ssl http2; -- 2.40.1 From af273ad5248d1feccc35aa8eb930a64d8e4ba1dd Mon Sep 17 00:00:00 2001 From: Ben Kristinsson Date: Tue, 21 Mar 2023 20:10:43 +0100 Subject: [PATCH 03/10] update controller --- roles/unifi/tasks/unifi.yml | 12 ++++++++---- roles/unifi/templates/02-unifi.conf.j2 | 13 +++++++++++-- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/roles/unifi/tasks/unifi.yml b/roles/unifi/tasks/unifi.yml index 6553008..a734cc0 100644 --- a/roles/unifi/tasks/unifi.yml +++ b/roles/unifi/tasks/unifi.yml @@ -14,7 +14,7 @@ vars: prediff_cmd: echo with_items: - - "{{ unifi_url }}" + - "{{ domain }}" - name: template nginx vhost template: @@ -61,10 +61,13 @@ # v6.5.55 # v6.0.45 # v5.14.23 +# +# updated: v5.14.23 -> stable-5 -> stable-6 -> latest (v7) - name: start docker container docker_container: name: "unifi" - image: "jacobalberty/unifi:v5.14.23" + #image: "jacobalberty/unifi:v5.14.23" + image: jacobalberty/unifi:latest auto_remove: false detach: true restart_policy: "unless-stopped" @@ -86,8 +89,9 @@ - "8080:8080/tcp" # Device/ controller comm. - "127.0.0.1:8443:8443/tcp" # Controller GUI/API as seen in a web browser - "10001:10001/udp" # AP discovery - dns_servers: - - "{{ ansible_docker0.ipv4.address }}" + tags: + - unifi-container + - docker-containers - name: wait for controller to be responsive diff --git a/roles/unifi/templates/02-unifi.conf.j2 b/roles/unifi/templates/02-unifi.conf.j2 index ee4f8b6..31e9347 100644 --- a/roles/unifi/templates/02-unifi.conf.j2 +++ b/roles/unifi/templates/02-unifi.conf.j2 @@ -15,6 +15,15 @@ server { proxy_set_header Host $host; } + location /inform { + proxy_pass http://localhost:8080; + + proxy_redirect off; + proxy_set_header Host $host; + + } + + location / { proxy_pass https://localhost:8443/; @@ -32,8 +41,8 @@ server { ssl_session_timeout 5m; - ssl_certificate /usr/local/etc/certs/{{ unifi_url }}/fullchain.pem; - ssl_certificate_key /usr/local/etc/certs/{{ unifi_url }}/privkey.pem; + ssl_certificate /usr/local/etc/certs/{{ domain }}/fullchain.pem; + ssl_certificate_key /usr/local/etc/certs/{{ domain }}/privkey.pem; add_header Referrer-Policy "no-referrer" always; add_header X-Content-Type-Options "nosniff" always; -- 2.40.1 From 32dce4c2bad89f9b2e5865c5a373af69ed699c1c Mon Sep 17 00:00:00 2001 From: Ben Kristinsson Date: Tue, 21 Mar 2023 20:12:18 +0100 Subject: [PATCH 04/10] fix --- roles/owntone/templates/01-owntone.conf.j2 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/roles/owntone/templates/01-owntone.conf.j2 b/roles/owntone/templates/01-owntone.conf.j2 index ef10169..559df50 100644 --- a/roles/owntone/templates/01-owntone.conf.j2 +++ b/roles/owntone/templates/01-owntone.conf.j2 @@ -49,8 +49,8 @@ server { access_log /var/log/nginx/access_{{ owntone_url }}_{{ owntone_port_ws }}.log main; error_log /var/log/nginx/error_{{ owntone_url }}_{{ owntone_port_ws }}.log warn; - ssl_certificate /usr/local/etc/certs/{{ owntone_url }}/fullchain.pem; - ssl_certificate_key /usr/local/etc/certs/{{ owntone_url }}/privkey.pem; + ssl_certificate /usr/local/etc/certs/{{ domain }}/fullchain.pem; + ssl_certificate_key /usr/local/etc/certs/{{ domain }}/privkey.pem; # ! ssl_protocols TLSv1.2 TLSv1.3; -- 2.40.1 From 3045cc8d9bf9fb424ea63c25c5c4a501ad8f786f Mon Sep 17 00:00:00 2001 From: Ben Kristinsson Date: Mon, 1 May 2023 00:00:22 +0200 Subject: [PATCH 05/10] fix typo --- roles/hass/files/templates.yaml | 126 ------ roles/hass/tasks/hass.yml | 5 +- .../hass/templates/configuration.yaml.j2.save | 410 ------------------ 3 files changed, 1 insertion(+), 540 deletions(-) delete mode 100644 roles/hass/files/templates.yaml delete mode 100644 roles/hass/templates/configuration.yaml.j2.save diff --git a/roles/hass/files/templates.yaml b/roles/hass/files/templates.yaml deleted file mode 100644 index b4c7f0e..0000000 --- a/roles/hass/files/templates.yaml +++ /dev/null @@ -1,126 +0,0 @@ -- sensor: - - name: "chance_of_rain" - unit_of_measurement: "%" - icon: "mdi:weather-pouring" - state: >- - {% raw -%} - {% set ipma_2h = state_attr('weather.ipma_hourly_home', 'forecast')[:2] | map(attribute='precipitation_probability') %} - {% set ipma = state_attr('weather.ipma_hourly_home', 'forecast')[0]['precipitation_probability'] | int %} - {% set owm = states('sensor.owm_home_forecast_precipitation_probability') | int %} - {{ [owm, ipma, 0] | max | int }} - {% endraw %} - - {% for radiator in hass_radiators %} - {% if 'status' in radiator %} - - - name: "radiator_{{ radiator.name }}_last_updated" - unit_of_measurement: "minutes" - icon: "mdi:update" - device_class: duration - state: >- - {% raw %} {% {% endraw -%} - set since_last_update = now() - states.{{ radiator.status }}.last_updated - {%- raw %} %} {% endraw %} - - {% raw %} {{ {% endraw -%} - since_last_update.seconds|int // 60 - {%- raw %} }} {% endraw -%} - {% endif %} - {% endfor %} - {% for linux_tracker in hass_linux_presence_trackers -%} - {% endfor %} - -- binary_sensor: - {% if blink1_enabled -%} - - name: "blink1_on" - device_class: light - state: >- - {% raw -%} - {{ state_attr('sensor.blink1', 'rgb') != "#000000" }} - {% endraw %} - {% endif %} - - - name: "heating_on" - icon: "mdi:home-thermometer" - device_class: heat - state: >- - {% raw -%} - {% set all_radiators = states.climate | selectattr("attributes", "defined") | map(attribute="attributes") | selectattr("temperature", "defined") | map(attribute="temperature") | default([]) | list %} - {% set max_radiator_temp = all_radiators | max | default(0.0) | float %} - {% set target_temp = states('input_number.target_temp_heat') | default(0.0) | float %} - {{ max_radiator_temp >= target_temp }} - {% endraw %} - - - name: s21_anyone_home - icon: "mdi:home-account" - attributes: - friendly_name: "Anyone home" - state: >- - {% raw -%} - {{ state_attr("zone.home", "persons") | default([]) | length > 0 }} - {% endraw %} - - - name: doorbell_buzzer - state: >- - {% raw %} {{ is_state("switch.doorbell_buzzer", "on") }} {% endraw +%} - icon: >- - {% raw -%} - {% if is_state("switch.doorbell_buzzer", "on") %} - mdi:electric-switch-closed - {% else %} - mdi:electric-switch - {% endif %} - {% endraw %} - - - name: washing_machine_on - icon: "mdi:washing-machine" - device_class: running - delay_on: "00:00:05" - delay_off: "00:00:05" - state: >- - {% raw -%} - {% set current = states('sensor.washing_machine_electric_a') %} - {% set power = states('sensor.washing_machine_electric_w') %} - {% if current == "unavailable" or power == "unavailable" %} - {{ false }} - {% else %} - {{ current|default(0.0)|float > 0.14 or power|default(0.0)|float > 2.8 }} - {% endif %} - {% endraw %} - - {% for linux_tracker in hass_linux_presence_trackers -%} - - name: {{ linux_tracker.name }}_active - icon: "mdi:laptop" - state: >- - {% raw -%} {% {% endraw %} set state_text = states('input_text.webhook_{{ linux_tracker.name }}') {%raw%} %} {%endraw%} - {% raw %} - {{ state_text == "active" }} - {% endraw %} - - - name: "{{ linux_tracker.name }}_webhook_triggering" - state: >- - {% raw %} {% {% endraw -%} - set since_last_triggered = now() - state_attr('automation.webhook_presence_trackers_{{ linux_tracker.name }}', 'last_triggered') - {%- raw %} %} {% endraw %} - - {% raw %} {{ {% endraw -%} - since_last_triggered.seconds|int < 666 - {%- raw %} }} {% endraw -%} - - {% endfor %} - -- button: - name: doorbell_buzzer - icon: >- - {% raw -%} - {% if is_state("switch.doorbell_buzzer", "on") %} - mdi:electric-switch-closed - {% else %} - mdi:electric-switch - {% endif %} - {% endraw +%} - press: - - service: script.toggle_switch_like_button - data: - target_switch: switch.doorbell_buzzer - press_for_ms: 200 diff --git a/roles/hass/tasks/hass.yml b/roles/hass/tasks/hass.yml index d34ecb5..2d056a4 100644 --- a/roles/hass/tasks/hass.yml +++ b/roles/hass/tasks/hass.yml @@ -395,16 +395,13 @@ - NET_ADMIN env: TZ: "Etc/UTC" - devices: - - "/dev/blink1:/dev/blink1:rwm" - # #- "/dev/rtl_sdr:/dev/rtl_sdr:rw" mounts: - type: bind source: "{{ systemuserlist.hass.home }}/home-assistant/config" target: /config - type: bind source: "{{ systemuserlist.hass.home }}/home-assistant/.config" - target: /.config t + target: /.config - type: bind source: "{{ systemuserlist.hass.home }}/home-assistant/.cache" target: /.cache diff --git a/roles/hass/templates/configuration.yaml.j2.save b/roles/hass/templates/configuration.yaml.j2.save deleted file mode 100644 index c93e38f..0000000 --- a/roles/hass/templates/configuration.yaml.j2.save +++ /dev/null @@ -1,410 +0,0 @@ -# ansible: roles/hass/templates/configuration.yaml.j2 -# -# Loads default set of integrations. Icluding the cloud crap. Do remove. -# havent gotten it to work wthough, hass doesnt load properly -default_config: -# -# the dict contains this: -# https://github.com/home-assistant/core/blob/dev/homeassistant/components/default_config/manifest.json -# -# the cloud thing clistens on (at least) port 42161. -# since we need to run in host mode, and dont have network/port isolation by default -# we'll kill this stuff. -# -# for some reason the settings dialog for it is still at /config/cloud/login, but -# we arent listening on port 42161 anymore (yay!). (but hass doesnt start) -# -# for now we just block the ports with iptables/ufw -# -# config: -# application_credentials: -# automation: -# bluetooth: -# # there is no cloud, just other peoples computers.. -# #cloud: -# counter: -# dhcp: -# energy: -# frontend: -# hardware: -# history: -# homeassistant_alerts: -# input_boolean: -# input_button: -# input_datetime: -# input_number: -# input_select: -# input_text: -# logbook: -# map: -# media_source: -# mobile_app: -# my: -# network: -# person: -# scene: -# schedule: -# script: -# ssdp: -# # kind of undocumented, but didnt help -# stream: -# sun: -# system_health: -# tag: -# timer: -# usb: -# webhook: -# zeroconf: -# zone: - -automation ui: !include automations.yaml -automation ansible: !include automations-ansible-managed.yaml -script: !include scripts.yaml -scene: !include scenes.yaml -template: !include templates.yaml -climate: !include climate.yaml - -# Text to speech -tts: - - platform: voicerss - api_key: !secret voicerss_api_key - - platform: google_translate - - platform: picotts - language: "en-GB" - -calendar: - {% for item in hass_caldav.calendars -%} - - platform: caldav - days: 30 - username: !secret caldav_user - password: !secret caldav_passwd - # {{ item.name | trim }} - url: {{ item.url | trim }} - {% endfor %} - -http: - # container runs with network_mode=host, so no network isolation. the docs say to not - # do this, and it doesnt work as expected either. - # using ufw/iptables for now.... - # - #server_host: 127.0.0.1 - trusted_proxies: - - 127.0.0.1 - use_x_forwarded_for: true - -frontend: - themes: !include_dir_merge_named themes - -panel_custom: - - name: zwave - sidebar_title: Z-Wave - sidebar_icon: mdi:z-wave - js_url: /api/hassio/app/entrypoint.js - url_path: 'config/devices/dashboard?historyBack=1&config_entry=d6e38621854098348266029e18f93048' - embed_iframe: true - require_admin: true - config: - ingress: core_configurator - - name: automations - sidebar_title: Automations - sidebar_icon: mdi:robot - js_url: /api/hassio/app/entrypoint.js - url_path: 'config/automation/dashboard' - embed_iframe: true - require_admin: true - config: - ingress: core_configurator - - name: scripts - sidebar_title: Scripts - sidebar_icon: mdi:script - js_url: /api/hassio/app/entrypoint.js - url_path: 'config/script/dashboard' - embed_iframe: true - require_admin: true - config: - ingress: core_configurator - - name: integrations - sidebar_title: Integrations - sidebar_icon: mdi:integrated-circuit-chip - js_url: /api/hassio/app/entrypoint.js - url_path: 'config/integrations' - embed_iframe: true - require_admin: true - config: - ingress: core_configurator - - -{% set zone = {'zone': hass_zones} %} -{{ zone | to_nice_yaml }} - - -homeassistant: - auth_providers: - - type: command_line - command: /usr/local/bin/authelia-auth.py - args: - - {{ hass_url }} - name: Home - currency: EUR - unit_system: metric - time_zone: "Europe/Berlin" - country: DE - external_url: https://{{ hass_url }} - internal_url: https://{{ hass_url }} - allowlist_external_dirs: - - "/usr/var/media" - allowlist_external_urls: - - "https://{{ static_url }}" - - "https://{{ hass_notflix_url }}" - media_dirs: - media: "/usr/var/media" - customize: - zone.home: - friendly_name: S21 - -lovelace: - mode: storage - dashboards: - lovelace-yaml: - mode: yaml - title: Mini - icon: mdi:view-dashboard - show_in_sidebar: true - require_admin: false - filename: mini.yaml - lovelace-yaml2: - mode: yaml - title: Test - icon: mdi:view-dashboard - show_in_sidebar: true - require_admin: false - filename: ui-test.yaml - - {# resources: - # - url: /config/custom_components/ui_lovelace_minimalist/cards/button-card/button-card.js - # type: module - # - url: /config/custom_components/ui_lovelace_minimalist/cards/button-card/button-card.js - # type: module #} - -recorder: - purge_keep_days: 3 - exclude: - entity_globs: - - binary_sensor.1066d799* - - sensor.1066d799* - - light.1066d799* - -sensor: - # https://www.home-assistant.io/integrations/dwd_weather_warnings/ - # https://www.dwd.de/DE/leistungen/opendata/help/warnungen/warning_codes_pdf.pdf?__blob=publicationFile&v=5 - # https://www.dwd.de/DE/leistungen/opendata/help/warnungen/cap_warncellids_csv.html - # 111000000;Berlin;DE300;Berlin;BXX - # 711000002;Berlin - Friedrichshain-Kreuzberg;;B-Friedrh./Kbg.;BXB - # 711000003;Berlin - Pankow;;B-Pankow;BXG - # 711000011;Berlin - Lichtenberg;;B-Lichtenberg;BXC - # 811000000;Stadt Berlin;;Berlin; - # 911000000;Berlin;;Land Berlin;LBE - # 911100000;Berlin;;Berlin;BXZ - # 995000000;Brandenburg/Berlin;;Berlin/Brandenb;DWPD - - - platform: dwd_weather_warnings - # Berlin - Friedrichshain-Kreuzberg - region_name: 711000002 - - - platform: dwd_weather_warnings - # Berlin - Pankow - region_name: 711000003 - - - platform: dwd_weather_warnings - # Stadt Berlin - region_name: 811000000 - - {% for item in hass_bvg -%} - - platform: bvg_berlin_public_transport - name: {{ item.name }} - stop_id: "{{ item.stop_id }}" - direction: {{ item.direction | trim }} - {% if item.walking_distance is defined -%} - walking_distance: {{ item.walking_distance | trim }} - {% endif -%} - file_path: "/config/bvg/" - {% endfor %} - - - platform: waqi - token: !secret waqi_token - locations: - {% for item in hass_waqi.locations -%} - - "{{ item | trim }}" - {%- endfor +%} - {#stations: - #{% for item in hass_waqi.stations -%} - #- "{{ item | trim }}" - #{% endfor %} - #} - - {% if blink1_enabled -%} - - platform: rest - resource: http://localhost:{{ blink1_server_port }}/blink1 - name: blink1 - json_attributes: - - rgb - - bright - value_template: "{%raw%}{{ value_json.rgb }}{%endraw%}" - {% endif %} - -binary_sensor: - - platform: workday - country: DE - workdays: [mon, tue, wed, thu, fri] - excludes: [sat, sun, holiday] - - {% for radiator in hass_radiators -%} - {% if 'status' in radiator -%} - - platform: threshold - entity_id: sensor.radiator_{{ radiator.name }}_last_updated - # defined in templates.yaml - name: radiator_{{ radiator.name }}_reporting - # minutes - lower: 10 - {% endif -%} - {% endfor -%} - {% for target in hass_ping -%} - - platform: ping - name: ping_{{ target.name }} - host: {{ target.host }} - count: {{ target.count | default('1') }} - scan_interval: {{ target.interval_secs | default('30') }} - {% endfor %} - -input_select: - heating_mode_test: - name: Heating Mode Test - options: - - "auto" - - "heat" - - "cool" - - "off" - initial: "off" - icon: mdi:thermometer-lines - - -input_number: - heating_setpoint_test: - name: Heating Setpoint Test - icon: mdi:home-thermometer - initial: 0 - min: 0 - max: 35 - step: 0.5 - -input_text: - {% for linux_tracker in hass_linux_presence_trackers -%} - webhook_{{ linux_tracker.name }}: - name: webhook_{{ linux_tracker.name }} - icon: "mdi:laptop" - initial: "inactive" - pattern: "^active|inactive$" - {% endfor %} - -device_tracker: - - platform: ping - hosts: - {% for target in hass_ping -%} - {% if target.device_tracker|default(true) -%} - {{ target.name }}: {{ target.host }} - {% endif -%} - {% endfor %} - -influxdb: - host: "{{ influxdb_url }}" - port: 443 - database: hass - username: hass - password: !secret influxdb_pass - ssl: true - verify_ssl: true - max_retries: 3 - default_measurement: state - include: - entities: - - sensor.washing_machine_electric_consumption_w - - sensor.washing_machine_electric_consumption_kwh - tags: - instance: prod - source: hass - home: S21 - -matrix: - homeserver: https://{{ matrix_url }} - username: !secret matrix_username - password: !secret matrix_password - rooms: !secret matrix_rooms - commands: - - word: hass - name: hass - -notify: - - name: matrix - platform: matrix - default_room: !secret matrix_default_room - -shell_command: - matrixmsg: /usr/local/bin/matrixmsg.py - -rest_command: - {% if blink1_enabled -%} - blink1_turn_on: - url: {{ hass_blink1_url }}/blink1/on?bright=250 - #url: http://localhost:{{ blink1_server_port }}/blink1/fadeToRGB?rgb=ff0ff - method: GET - content_type: "application/json" - blink1_turn_off: - url: {{ hass_blink1_url }}/blink1/off - method: GET - content_type: "application/json" - blink1_turn_magenta: - url: {{ hass_blink1_url }}/blink1/fadeToRGB?rgb=ff00ff - method: GET - content_type: "application/json" - blink1_set_color: - url: "{{ hass_blink1_url }}/blink1/fadeToRGB?rgb={%raw%}{{ rgb }}{%endraw%}" - method: GET - {% endif %} - -light: - {% if blink1_enabled -%} - - platform: template - lights: - blink1: !include blink1.yaml - {% endif %} - -# enable 'wake_on_lan' for 'samsungtv' -wake_on_lan: - -samsungtv: - - host: {{ hass_wifi_blackbox.tv_ip }} - name: The TV - turn_on_action: - - service: wake_on_lan.send_magic_packet - data: - mac: "{{ hass_wifi_blackbox.tv_mac }}" - -feedreader: - urls: - {% for item in hass_feedreader -%} - - "{{ item.url | trim }}" - {% endfor %} - -# fixme -device_tracker: - - platform: ubus - host: "192.168.21.1" - username: !secret openwrt_user - password: !secret openwrt_pass - -# fixme -device_tracker: - - platform: unifi_direct - host: Y - username: YOUR_USERNAME - password: YOUR_PASSWORD -- 2.40.1 From 97e03a4e6b0c6e15d9fabeec674456968f2b4c90 Mon Sep 17 00:00:00 2001 From: Ben Kristinsson Date: Mon, 1 May 2023 00:04:04 +0200 Subject: [PATCH 06/10] fix abs container task --- roles/audiobookshelf/tasks/audiobookshelf.yml | 7 ++++--- .../templates/02-audiobookshelf.conf.j2 | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/roles/audiobookshelf/tasks/audiobookshelf.yml b/roles/audiobookshelf/tasks/audiobookshelf.yml index 72b995f..c66e5f1 100644 --- a/roles/audiobookshelf/tasks/audiobookshelf.yml +++ b/roles/audiobookshelf/tasks/audiobookshelf.yml @@ -14,6 +14,7 @@ - name: '' - name: audiobooks - name: config + mode: "0755" - name: metadata - name: podcasts @@ -47,10 +48,11 @@ - name: start audiobookshelf container docker_container: name: audiobookshelf + auto_remove: false image: ghcr.io/advplyr/audiobookshelf:latest detach: true pull: true - restart_policy: no + restart_policy: "no" state: started container_default_behavior: compatibility networks_cli_compatible: false @@ -58,11 +60,10 @@ networks: - name: bridgewithdns ipv4_address: "{{ bridgewithdns.audiobookshelf }}" - ports: - - "10.102.47.146:13378:80" env: AUDIOBOOKSHELF_UID: "{{ audiobookshelf_user.uid }}" AUDIOBOOKSHELF_GID: "{{ audiobookshelf_group.gid }}" + user: "{{ audiobookshelf_user.uid }}:{{ audiobookshelf_group.gid }}" mounts: - type: bind source: "{{ audiobookshelf_path }}/audiobooks" diff --git a/roles/audiobookshelf/templates/02-audiobookshelf.conf.j2 b/roles/audiobookshelf/templates/02-audiobookshelf.conf.j2 index ab3f2bc..0461508 100644 --- a/roles/audiobookshelf/templates/02-audiobookshelf.conf.j2 +++ b/roles/audiobookshelf/templates/02-audiobookshelf.conf.j2 @@ -16,6 +16,24 @@ server { include /etc/nginx/authelia_internal.conf; + location /feed/ { + include /etc/nginx/require_auth.conf; + + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Host $host; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + + proxy_http_version 1.1; + + proxy_pass http://{{ bridgewithdns.audiobookshelf }}:80; + proxy_redirect http:// https://; + + proxy_hide_header "Content-Type"; + add_header "Content-Type" "application/rss+xml"; + + } location / { include /etc/nginx/require_auth.conf; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; -- 2.40.1 From de4bf2be23cb588430144c4da17fe11b1d8b538b Mon Sep 17 00:00:00 2001 From: Ben Kristinsson Date: Mon, 1 May 2023 00:10:39 +0200 Subject: [PATCH 07/10] move semi dead tasks to a file that isnt included, they should be in common-ufw or their own role --- roles/hass/tasks/hass.yml | 99 ---------------------------------- roles/hass/tasks/network.yml | 100 +++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 99 deletions(-) create mode 100644 roles/hass/tasks/network.yml diff --git a/roles/hass/tasks/hass.yml b/roles/hass/tasks/hass.yml index 2d056a4..d0f93b9 100644 --- a/roles/hass/tasks/hass.yml +++ b/roles/hass/tasks/hass.yml @@ -1,104 +1,5 @@ --- -# - name: allow ssh -# ufw: -# rule: allow -# to_port: "22" -# direction: in -# state: enabled -# tags: -# - ufw - -# - name: allow loopback -# ufw: -# rule: allow -# interface: lo -# direction: in -# state: enabled -# tags: -# - ufw - -# - name: default policy -# ufw: -# policy: allow -# state: enabled -# tags: -# - ufw - -# - name: deny hass cloud port stuff -# ufw: -# # drops packets -# rule: deny -# to_port: '42161' -# direction: in -# state: enabled -# tags: -# - ufw - -# - name: reject zwavejs ws and hass ports (loopback only) -# ufw: -# # connection refused -# rule: reject -# to_port: "{{ item }}" -# direction: in -# state: enabled -# with_items: -# - "8091" -# - "8123" -# tags: -# - ufw - -- name: get current timestamp line - command: grep "timestamp=" /etc/NetworkManager/system-connections/blackbox.connection - check_mode: false - ignore_errors: true - changed_when: false - register: timestamp - tags: - - hass-wifi - - hass-blackbox - -# nmcli device wifi blackbox ifname wlo1 ssid {{ hass_wifi_blackbox.ssid }} password {{ hass_wifi_blackbox.pass }} -- name: config for blackbox wifi ap with NetworkManager - template: - src: blackbox.connection.j2 - dest: /etc/NetworkManager/system-connections/blackbox.connection - owner: root - group: root - mode: 0600 - tags: - - hass-wifi - - hass-blackbox - notify: - - nmcli conn reload - - nmcli device wifi hotspot - -# # routing/forwarding should not be enabled, but block it to be sure -# - name: allow local traffic on blackbox wifi -# ufw: -# rule: allow -# interface: "{{ hass_wifi_blackbox.iface }}" -# direction: out -# dest: "{{ hass_wifi_blackbox.ip }}/{{ hass_wifi_blackbox.cidr_prefix }}" -# state: enabled -# tags: -# - hass-wifi -# - hass-blackbox -# - ufw - -# - name: reject everything else on the blackbox wifi -# ufw: -# # connection refused -# rule: reject -# interface: "{{ hass_wifi_blackbox.iface }}" -# direction: out -# dest: any -# state: enabled -# tags: -# - hass-wifi -# - hass-blackbox -# - ufw - - name: copy ssh keys for {{ hass_config_repo_name }} template: src: "private/sshkeys/{{ item }}" diff --git a/roles/hass/tasks/network.yml b/roles/hass/tasks/network.yml new file mode 100644 index 0000000..7fb26a6 --- /dev/null +++ b/roles/hass/tasks/network.yml @@ -0,0 +1,100 @@ + +- name: allow ssh + ufw: + rule: allow + to_port: "22" + direction: in + state: enabled + tags: + - ufw + +- name: allow loopback + ufw: + rule: allow + interface: lo + direction: in + state: enabled + tags: + - ufw + +- name: default policy + ufw: + policy: allow + state: enabled + tags: + - ufw + +- name: deny hass cloud port stuff + ufw: + # drops packets + rule: deny + to_port: '42161' + direction: in + state: enabled + tags: + - ufw + +- name: reject zwavejs ws and hass ports (loopback only) + ufw: + # connection refused + rule: reject + to_port: "{{ item }}" + direction: in + state: enabled + with_items: + - "8091" + - "8123" + tags: + - ufw + +- name: get current timestamp line + command: grep "timestamp=" /etc/NetworkManager/system-connections/blackbox.connection + check_mode: false + ignore_errors: true + changed_when: false + register: timestamp + tags: + - hass-wifi + - hass-blackbox + + +# nmcli device wifi blackbox ifname wlo1 ssid {{ hass_wifi_blackbox.ssid }} password {{ hass_wifi_blackbox.pass }} +- name: config for blackbox wifi ap with NetworkManager + template: + src: blackbox.connection.j2 + dest: /etc/NetworkManager/system-connections/blackbox.connection + owner: root + group: root + mode: 0600 + tags: + - hass-wifi + - hass-blackbox + notify: + - nmcli conn reload + - nmcli device wifi hotspot + +# routing/forwarding should not be enabled, but block it to be sure +- name: allow local traffic on blackbox wifi + ufw: + rule: allow + interface: "{{ hass_wifi_blackbox.iface }}" + direction: out + dest: "{{ hass_wifi_blackbox.ip }}/{{ hass_wifi_blackbox.cidr_prefix }}" + state: enabled + tags: + - hass-wifi + - hass-blackbox + - ufw + +- name: reject everything else on the blackbox wifi + ufw: + # connection refused + rule: reject + interface: "{{ hass_wifi_blackbox.iface }}" + direction: out + dest: any + state: enabled + tags: + - hass-wifi + - hass-blackbox + - ufw -- 2.40.1 From 057c43c67751f170b6680ccceb247fd61be729af Mon Sep 17 00:00:00 2001 From: Ben Kristinsson Date: Mon, 1 May 2023 00:13:52 +0200 Subject: [PATCH 08/10] move the linuxserver.io image insanity workaround files, might have to use this strategy again, but also less clutter please --- roles/airconnect/templates/{ => linuxserver.io}/30-install.j2 | 0 roles/airconnect/templates/{ => linuxserver.io}/run.j2 | 0 .../airconnect/templates/{ => linuxserver.io}/supervisord.conf.j2 | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename roles/airconnect/templates/{ => linuxserver.io}/30-install.j2 (100%) rename roles/airconnect/templates/{ => linuxserver.io}/run.j2 (100%) rename roles/airconnect/templates/{ => linuxserver.io}/supervisord.conf.j2 (100%) diff --git a/roles/airconnect/templates/30-install.j2 b/roles/airconnect/templates/linuxserver.io/30-install.j2 similarity index 100% rename from roles/airconnect/templates/30-install.j2 rename to roles/airconnect/templates/linuxserver.io/30-install.j2 diff --git a/roles/airconnect/templates/run.j2 b/roles/airconnect/templates/linuxserver.io/run.j2 similarity index 100% rename from roles/airconnect/templates/run.j2 rename to roles/airconnect/templates/linuxserver.io/run.j2 diff --git a/roles/airconnect/templates/supervisord.conf.j2 b/roles/airconnect/templates/linuxserver.io/supervisord.conf.j2 similarity index 100% rename from roles/airconnect/templates/supervisord.conf.j2 rename to roles/airconnect/templates/linuxserver.io/supervisord.conf.j2 -- 2.40.1 From 13a36b63db1b7f10eea87890929421c2724a634c Mon Sep 17 00:00:00 2001 From: Ben Kristinsson Date: Mon, 1 May 2023 00:15:59 +0200 Subject: [PATCH 09/10] handlers taht should be in common-ufw or new role --- roles/hass/handlers/main.yml | 6 ------ roles/hass/handlers/network.yml | 7 +++++++ 2 files changed, 7 insertions(+), 6 deletions(-) create mode 100644 roles/hass/handlers/network.yml diff --git a/roles/hass/handlers/main.yml b/roles/hass/handlers/main.yml index 5fc0b27..e473eca 100644 --- a/roles/hass/handlers/main.yml +++ b/roles/hass/handlers/main.yml @@ -30,9 +30,3 @@ - name: udevadm reload rules command: udevadm control --reload-rules - -- name: nmcli conn reload - command: nmcli conn reload - -- name: nmcli device wifi hotspot - command: nmcli device wifi hotspot diff --git a/roles/hass/handlers/network.yml b/roles/hass/handlers/network.yml new file mode 100644 index 0000000..997e6d7 --- /dev/null +++ b/roles/hass/handlers/network.yml @@ -0,0 +1,7 @@ +--- + +- name: nmcli conn reload + command: nmcli conn reload + +- name: nmcli device wifi hotspot + command: nmcli device wifi hotspot -- 2.40.1 From afb1e34b1acfbecd9da993b1a8b8c9e5c7ca0521 Mon Sep 17 00:00:00 2001 From: Ben Kristinsson Date: Mon, 1 May 2023 00:19:16 +0200 Subject: [PATCH 10/10] owntone default config --- .../{templates/owntone-mathom.conf => files/owntone-default.conf} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename roles/owntone/{templates/owntone-mathom.conf => files/owntone-default.conf} (100%) diff --git a/roles/owntone/templates/owntone-mathom.conf b/roles/owntone/files/owntone-default.conf similarity index 100% rename from roles/owntone/templates/owntone-mathom.conf rename to roles/owntone/files/owntone-default.conf -- 2.40.1