catching up ()

a whole bunch of changes, home audio system (airconnect, audiobookshelf, owntone) lost of hass changes

hass,zwavejs,owntone certs, www.sudo.is redirect to www

update controller

fix

fix typo

fix abs container task

move semi dead tasks to a file that isnt included, they should be in common-ufw or their own role

move the linuxserver.io image insanity workaround files, might have to use this strategy again, but also less clutter please

handlers taht should be in common-ufw or new role

owntone default config

Co-authored-by: Ben Kristinsson <ben@sudo.is>
Reviewed-on: 
This commit is contained in:
ben 2023-04-30 22:22:23 +00:00
parent 7c382db774
commit 912d41b053
48 changed files with 3120 additions and 220 deletions

View File

@ -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

View File

@ -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'

View File

@ -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

View File

@ -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 }}"

View File

@ -0,0 +1,3 @@
---
- import_tasks: airconnect.yml
tags: airconnect

View File

@ -0,0 +1,37 @@
<?xml version="1.0"?>
<airupnp>
<common>
<protocolInfo>
<pcm>http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=00;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=0d500000000000000000000000000000</pcm>
<wav>http-get:*:audio/wav:DLNA.ORG_OP=00;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=0d500000000000000000000000000000</wav>
<flac>http-get:*:audio/flac:DLNA.ORG_OP=00;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=0d500000000000000000000000000000</flac>
<mp3>http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=00;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=0d500000000000000000000000000000</mp3>
</protocolInfo>
<enabled>1</enabled>
<max_volume>100</max_volume>
<http_length>-1</http_length>
<upnp_max>1</upnp_max>
<codec>mp3:320</codec>
<metadata>1</metadata>
<flush>1</flush>
<artwork></artwork>
<latency>0:1000</latency>
<drift>0</drift>
</common>
<main_log>info</main_log>
<upnp_log>info</upnp_log>
<util_log>warn</util_log>
<raop_log>info</raop_log>
<log_limit>-1</log_limit>
<max_players>32</max_players>
<binding>?</binding>
<ports>0:0</ports>
{% for item in airconnect_upnp -%}
<device>
<udn>uuid:{{ item.local_uid }}</udn>
<name>{{ item.name }}</name>
<mac>{{ item.mac }}</mac>
<enabled>{% if item.enabled|default(true) %}1{% else %}0{% endif %}</enabled>
</device>
{% endfor %}
</airupnp>

View File

@ -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

View File

@ -0,0 +1,3 @@
#!/usr/bin/with-contenv bash
supervisord --configuration /etc/supervisord.conf

View File

@ -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

View File

@ -0,0 +1,6 @@
---
- name: reload nginx
service:
name: nginx
state: reloaded

View File

@ -0,0 +1,82 @@
---
- 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
mode: "0755"
- 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
auto_remove: false
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 }}"
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"
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

View File

@ -0,0 +1,3 @@
---
- import_tasks: audiobookshelf.yml
tags: audiobookshelf

View File

@ -0,0 +1,50 @@
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 /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;
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://;
}
}

View File

@ -1 +1,2 @@
---
blink1_enabled: false

View File

@ -0,0 +1,52 @@
-----BEGIN PGP ARMORED FILE-----
Comment: Use "gpg --dearmor" for unpacking
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
=P2ir
-----END PGP ARMORED FILE-----

View File

@ -0,0 +1,52 @@
-----BEGIN PGP ARMORED FILE-----
Comment: Use "gpg --dearmor" for unpacking
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
=P2ir
-----END PGP ARMORED FILE-----

View File

@ -1,6 +0,0 @@
- 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 }}"

View File

@ -15,4 +15,18 @@
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

View File

@ -0,0 +1,7 @@
---
- name: nmcli conn reload
command: nmcli conn reload
- name: nmcli device wifi hotspot
command: nmcli device wifi hotspot

View File

@ -1,53 +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: copy ssh keys for {{ hass_config_repo_name }}
template:
src: "private/sshkeys/{{ item }}"
@ -66,22 +18,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 +68,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 +125,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 +239,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 +253,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,20 +275,25 @@
- 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: 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"
mounts:
@ -242,19 +303,27 @@
- type: bind
source: "{{ systemuserlist.hass.home }}/home-assistant/.config"
target: /.config
- 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
@ -283,20 +352,23 @@
vars:
prediff_cmd: echo
with_items:
- "{{ hass_url }}"
- "{{ domain }}"
- 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

View File

@ -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

View File

@ -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/{{ domain }}/fullchain.pem;
ssl_certificate_key /usr/local/etc/certs/{{ domain }}/privkey.pem;
fastcgi_hide_header X-Powered-By;
}

View File

@ -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;
}

View File

@ -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/{{ domain }}/fullchain.pem;
ssl_certificate_key /usr/local/etc/certs/{{ domain }}/privkey.pem;
fastcgi_hide_header X-Powered-By;
}

View File

@ -0,0 +1 @@
SUBSYSTEM=="usb", ATTRS{idVendor}=="0bda", ATTRS{idProduct}=="2838", GROUP="adm", MODE="0666", SYMLINK+="rtl_sdr"

View File

@ -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 %}

View File

@ -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]

View File

@ -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 %}

View File

@ -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') }}"
#}

View File

@ -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 #}

View File

@ -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 }}

View File

@ -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

7
roles/home/meta/main.yml Normal file
View File

@ -0,0 +1,7 @@
---
dependencies:
- unifi
- sudoisbot
- hass
- homeaudio

View File

@ -0,0 +1,7 @@
---
dependencies:
- podgrab
- airconnect
- shairplay
- owntone

View File

@ -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
}

View File

@ -0,0 +1,12 @@
---
- name: reload nginx
service:
name: nginx
state: reloaded
- name: restart owntone container
docker_container:
name: hass
state: started
restart: true

View File

@ -0,0 +1,4 @@
---
- import_tasks: owntone.yml
tags: owntone

View File

@ -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:
- "{{ domain }}"
- 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

View File

@ -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/{{ domain }}/fullchain.pem;
ssl_certificate_key /usr/local/etc/certs/{{ domain }}/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/{{ domain }}/fullchain.pem;
ssl_certificate_key /usr/local/etc/certs/{{ domain }}/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";
}
}

View File

@ -0,0 +1,7 @@
# {{ ansible_managed }}
# m h dom mon dow
*/60 * * * * {{ owntone_user.username }} touch {{ owntone_path }}/audio/local_music/trigger.init-rescan
#

View File

@ -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
}

View File

@ -1,7 +1 @@
---
dependencies:
- mariadb
- zflux
- sudoisbot
- hass

View File

@ -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

View File

@ -0,0 +1,5 @@
---
dependencies:
- mariadb
- zflux

View File

@ -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

View File

@ -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;

View File

@ -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;