recent hass changes #57
|
@ -8,6 +8,8 @@ airconnect_group:
|
|||
name: airconnect
|
||||
gid: 1337
|
||||
|
||||
# for flac: 'flc'
|
||||
airconnect_codec: mp3:320
|
||||
|
||||
airconnect_max_volume: "100"
|
||||
airconnect_upnp: []
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<max_volume>{{ airconnect_max_volume }}</max_volume>
|
||||
<http_length>-1</http_length>
|
||||
<upnp_max>1</upnp_max>
|
||||
<codec>mp3:320</codec>
|
||||
<codec>{{ airconnect_codec }}</codec>
|
||||
<metadata>1</metadata>
|
||||
<flush>1</flush>
|
||||
<artwork></artwork>
|
||||
|
@ -28,7 +28,9 @@
|
|||
<ports>0:0</ports>
|
||||
{% for item in airconnect_upnp -%}
|
||||
<device>
|
||||
{% if 'local_uuid' in item -%}
|
||||
<udn>uuid:{{ item.local_uuid }}</udn>
|
||||
{% endif -%}
|
||||
<name>{{ item.name }}</name>
|
||||
{% if 'mac' in item -%}
|
||||
<mac>{{ item.mac | upper }}</mac>
|
||||
|
|
|
@ -0,0 +1,254 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
from datetime import datetime
|
||||
import subprocess
|
||||
import json
|
||||
import sys
|
||||
import shutil
|
||||
import os
|
||||
import time
|
||||
|
||||
from loguru import logger
|
||||
import dateutil.parser
|
||||
from dateutil.parser._parser import ParserError
|
||||
|
||||
def delete_file(file_path):
|
||||
try:
|
||||
os.remove(file_path)
|
||||
logger.debug(f"deleted: '{file_path}'")
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
|
||||
def replace_file(src, dest):
|
||||
try:
|
||||
logger.debug(f"src: {src}, dest: {dest}")
|
||||
delete_file(dest)
|
||||
shutil.move(src, dest)
|
||||
logger.debug(f"replaced '{dest}'")
|
||||
except (PermissionError, OSError, FileNotFoundError) as e:
|
||||
logger.error(e)
|
||||
raise SystemExit(2)
|
||||
|
||||
|
||||
def ffprobe_get_tags(file_path):
|
||||
cmd = [
|
||||
"ffprobe",
|
||||
"-v", "quiet",
|
||||
file_path,
|
||||
"-print_format", "json",
|
||||
"-show_entries",
|
||||
"stream_tags:format_tags"
|
||||
]
|
||||
try:
|
||||
p = subprocess.run(cmd, capture_output=True, check=True)
|
||||
j = json.loads(p.stdout)
|
||||
return j['format']['tags']
|
||||
except subprocess.CalledProcessError as e:
|
||||
logger.error(f"{cmd[0]} exited with returncode {e.returncode} \n{e.stderr.decode()}")
|
||||
raise SystemExit(e.returncode)
|
||||
except KeyError as e:
|
||||
logger.error(f"key {e} for file '{file_path}' not found in ffprobe stdout: {p.stdout.decode()}")
|
||||
raise SystemExit(2)
|
||||
|
||||
|
||||
def ffmpeg_write_date_tag(podcast_file, out_file, iso_date_str):
|
||||
delete_file(out_file)
|
||||
|
||||
cmd = [
|
||||
"ffmpeg",
|
||||
"-nostdin",
|
||||
"-y", # overwrite output file
|
||||
"-i", podcast_file,
|
||||
#"-c", "copy",
|
||||
"-metadata", f"date={iso_date_str}",
|
||||
#"-metadata", f"releasedate={iso_date_str}",
|
||||
out_file
|
||||
]
|
||||
|
||||
try:
|
||||
p = subprocess.run(cmd, capture_output=True, check=True, stdin=None)
|
||||
p.check_returncode()
|
||||
logger.debug(f"output: '{out_file}'")
|
||||
replace_file(out_file, podcast_file)
|
||||
except subprocess.CalledProcessError as e:
|
||||
logger.error(f"{cmd[0]} exited with returncode {e.returncode} \n{e.stderr.decode()}")
|
||||
raise SystemExit(e.returncode)
|
||||
finally:
|
||||
delete_file(out_file)
|
||||
|
||||
|
||||
def eyeD3_write_date_tag(podcast_file, iso_date_str):
|
||||
|
||||
# import eyeD3 ?
|
||||
|
||||
podcast_dir = os.path.basename(podcast_file)
|
||||
cover_path = os.path.join(podcast_dir, "cover.jpg")
|
||||
|
||||
cmd = [
|
||||
"eyeD3",
|
||||
"--release-date", iso_date_str,
|
||||
"--orig-release-date", iso_date_str,
|
||||
"--recording-date", iso_date_str,
|
||||
# this overwrites 'release date' i think:
|
||||
#"--release-year", iso_date_str.split("-")[0],
|
||||
#"--preserve-file-times"
|
||||
]
|
||||
# if os.path.exists(cover_path):
|
||||
# cmd.extend(["--add-image", f"{cover_path}:FRONT_COVER"])
|
||||
cmd.append(podcast_file)
|
||||
|
||||
logger.debug(" ".join(cmd))
|
||||
|
||||
try:
|
||||
subprocess.run(cmd, capture_output=True, check=True, stdin=None)
|
||||
logger.debug(f"updated: '{podcast_file}'")
|
||||
except subprocess.CalledProcessError as e:
|
||||
logger.error(f"{cmd[0]} exited with returncode {e.returncode} \n{e.stderr.decode()}")
|
||||
raise SystemExit(e.returncode)
|
||||
|
||||
|
||||
def get_podcast_name_from_dir(podcast_file):
|
||||
podcast_dir = os.path.dirname(podcast_file)
|
||||
if podcast_dir.startswith("/"):
|
||||
# for now lets just do absolute dirs for names
|
||||
podcast_name = os.path.basename(podcast_dir)
|
||||
logger.debug(f"podcast name: {podcast_name}")
|
||||
return podcast_name
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def eyeD3_write_album_tag(podcast_file, podcast_name):
|
||||
# "album" is the name of the podcast
|
||||
|
||||
cmd = ["eyeD3", "--album", podcast_name, podcast_file]
|
||||
try:
|
||||
subprocess.run(cmd, capture_output=True, check=True, stdin=None)
|
||||
except subprocess.CalledProcessError as e:
|
||||
logger.error(f"{cmd[0]} exited with returncode {e.returncode} \n{e.stderr.decode()}")
|
||||
raise SystemExit(e.returncode)
|
||||
|
||||
|
||||
def parse_iso_date(date_str):
|
||||
try:
|
||||
dt = dateutil.parser.parse(date_str)
|
||||
return dt.date().isoformat()
|
||||
except (ParserError, TypeError) as e:
|
||||
logger.warning(f"invalid date string: '{date_str}'")
|
||||
|
||||
|
||||
def parse_TDAT_tag(tag_tdat):
|
||||
try:
|
||||
iso_date_str = tag_tdat.split(' ')[0]
|
||||
return parse_iso_date(iso_date_str)
|
||||
except (AttributeError, IndexError) as e:
|
||||
logger.debug(f"invalid 'TDAT' tag: '{tag_tdat}'")
|
||||
return None
|
||||
|
||||
|
||||
def get_iso_date_in_file(file_path):
|
||||
tags = ffprobe_get_tags(file_path)
|
||||
|
||||
tag_TDAT = tags.get("TDAT")
|
||||
tag_date = tags.get("date")
|
||||
|
||||
parsed_TDAT = parse_TDAT_tag(tag_TDAT)
|
||||
parsed_date = parse_iso_date(tag_date)
|
||||
|
||||
if parsed_TDAT is None and parsed_date is None:
|
||||
logger.error(f"no valid date found in '{file_path}' - TDAT: '{tag_TDAT}', date: '{tag_date}'")
|
||||
raise SystemExit(3)
|
||||
|
||||
else:
|
||||
logger.debug(f"TDAT: '{parsed_TDAT}' ('{tag_TDAT}'), date: '{parsed_date}' ('{tag_date}')")
|
||||
logger.debug(f"date: {parsed_date}")
|
||||
|
||||
if parsed_TDAT != parsed_date:
|
||||
logger.debug(f"dates in 'TDAT' ({parsed_TDAT}) and 'date' ({parsed_date}) differ!")
|
||||
|
||||
if parsed_date is not None:
|
||||
return parsed_date
|
||||
else:
|
||||
return parsed_TDAT
|
||||
|
||||
|
||||
def file_dates_are_ok(file_path):
|
||||
tags = ffprobe_get_tags(file_path)
|
||||
tag_date = tags.get("date")
|
||||
try:
|
||||
dt = datetime.fromisoformat(tag_date)
|
||||
ts = time.mktime(dt.timetuple())
|
||||
os.stat(file_path).st_mtime == ts
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
def set_utime(file_path, iso_date_str):
|
||||
dt = dateutil.parser.parse(iso_date_str)
|
||||
ts = time.mktime(dt.timetuple())
|
||||
# shutil.move(file_path, f"{file_path}.new")
|
||||
# shutil.move(f"{file_path}.new", file_path)
|
||||
os.utime(file_path, (ts, ts))
|
||||
try:
|
||||
os.utime(os.path.dirname(file_path), (ts, ts))
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
return dt
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("podcast_file")
|
||||
parser.add_argument("--out-file", default="/tmp/out-{os.getpid()}.mp3")
|
||||
parser.add_argument("--debug", action="store_true")
|
||||
parser.add_argument("--quiet", action="store_true")
|
||||
parser.add_argument("--ffmpeg", action="store_true")
|
||||
parser.add_argument("--mtime", action="store_true", help="only set mtime, no mp3 metadata")
|
||||
parser.add_argument("--fix-album-tag", action="store_true", help="write album tag (podcast name)")
|
||||
parser.add_argument("--podcast-name")
|
||||
args = parser.parse_args()
|
||||
|
||||
if not args.debug:
|
||||
logger.remove()
|
||||
|
||||
if args.quiet:
|
||||
logger.add(sys.stderr, level="ERROR")
|
||||
else:
|
||||
logger.add(sys.stderr, level="INFO")
|
||||
|
||||
return args
|
||||
|
||||
|
||||
def main():
|
||||
args = parse_args()
|
||||
logger.debug(f"checking: '{os.path.basename(args.podcast_file)}'")
|
||||
|
||||
if args.fix_album_tag:
|
||||
podcast_name = get_podcast_name_from_dir(args.podcast_file)
|
||||
if podcast_name is not None:
|
||||
eyeD3_write_album_tag(args.podcast_file, podcast_name)
|
||||
logger.info(f"set album tag to '{podcast_name}' for '{args.podcast_file}'")
|
||||
|
||||
|
||||
date = get_iso_date_in_file(args.podcast_file)
|
||||
|
||||
if args.mtime:
|
||||
dt = set_utime(args.podcast_filen, date)
|
||||
logger.info(f"set mtime for '{os.path.basename(args.podcast_file)}' to '{dt.isoformat()}' according to mp3 metadata")
|
||||
|
||||
elif file_dates_are_ok(args.podcast_file):
|
||||
logger.info(f"metadata date and filesystem utimes ar ok for {args.podcast_file}', did not modify file")
|
||||
else:
|
||||
if args.ffmpeg:
|
||||
ffmpeg_write_date_tag(args.podcast_file, args.out_file, date)
|
||||
else:
|
||||
eyeD3_write_date_tag(args.podcast_file, date)
|
||||
|
||||
set_utime(args.podcast_file, date)
|
||||
logger.success(f"updated date in '{args.podcast_file}' as {date}")
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -0,0 +1,168 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
orig_file="$1"
|
||||
release_date="$2"
|
||||
|
||||
if [[ "$orig_file" == "" ]]; then
|
||||
echo "missing parameter"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$release_date" == "" ]]; then
|
||||
echo "missing parameter"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$3" == "" ]]; then
|
||||
prefix=""
|
||||
else
|
||||
prefix="${3}-"
|
||||
fi
|
||||
|
||||
orig_name=$(basename "$1")
|
||||
orig_dir=$(dirname "$1")
|
||||
cover_path="${orig_dir}/cover.jpg"
|
||||
|
||||
ffmpeg_file="ffmpeg-${prefix}${orig_name}"
|
||||
eyed3_file="eyeD3-${prefix}${orig_name}"
|
||||
abs_podcast_dir="/deadspace/audiobookshelf/podcasts/00-test-metadata"
|
||||
|
||||
rm "${ffmpeg_file}" &> /dev/null || true
|
||||
rm "no-date-tag-${ffmpeg_file}" &> /dev/null || true
|
||||
rm "releasedate-${ffmpeg_file}" &> /dev/null || true
|
||||
rm "release_date-${ffmpeg_file}" &> /dev/null || true
|
||||
rm "${eyed3_file}" &> /dev/null || true
|
||||
|
||||
rm -vf "${abs_podcast_dir}/${ffmpeg_file}"
|
||||
rm -vf "${abs_podcast_dir}/${eyed3_file}"
|
||||
rm -vf "${abs_podcast_dir}/metadata.abs"
|
||||
rm -vf "${abs_podcast_dir}/cover.jpg"
|
||||
ls -l "${abs_podcast_dir}/"
|
||||
|
||||
cp "${orig_file}" "${eyed3_file}"
|
||||
|
||||
|
||||
echo
|
||||
set -x
|
||||
eyeD3 --add-image "${cover_path}:FRONT_COVER" --release-date "${release_date}" "${eyed3_file}" > /dev/null
|
||||
|
||||
#ffmpeg -i "${orig_file}" -c copy -metadata date="${release_date}" -metadata releasedate="${release_date}" "${ffmpeg_file}" &> /dev/null
|
||||
ffmpeg -i "${orig_file}" -c copy -metadata date="${release_date}" "${ffmpeg_file}" &> /dev/null
|
||||
#ffmpeg -i "${orig_file}" -c copy -metadata releasedate="${release_date}" "releasedate-${ffmpeg_file}" &> /dev/null
|
||||
#ffmpeg -i "${orig_file}" -c copy -metadata release_date="${release_date}" "release_date-${ffmpeg_file}" &> /dev/null
|
||||
set +x
|
||||
|
||||
echo
|
||||
echo "--------"
|
||||
echo
|
||||
|
||||
echo "file: ${orig_name}"
|
||||
echo "original as downloaded by abs"
|
||||
|
||||
echo
|
||||
echo "> ffprobe ${orig_file}"
|
||||
ffprobe "${orig_file}" 2>&1 | grep date
|
||||
echo
|
||||
echo "> eyeD3 ${orig_file}"
|
||||
eyeD3 "${orig_file}" 2>&1 | grep date
|
||||
echo
|
||||
|
||||
|
||||
echo
|
||||
echo "--------"
|
||||
echo
|
||||
|
||||
echo "file: ${eyed3_file}"
|
||||
echo "eyeD3 with '--release-date ${release_date}'"
|
||||
|
||||
echo
|
||||
echo "> ffprobe ${eyed3_file}"
|
||||
ffprobe "${eyed3_file}" 2>&1 | egrep "image|date|COVER"
|
||||
echo
|
||||
echo "> eyeD3 ${eyed3_file}"
|
||||
eyeD3 "${eyed3_file}" 2>&1 | egrep "image|date|COVER"
|
||||
echo
|
||||
|
||||
echo
|
||||
echo "--------"
|
||||
echo
|
||||
|
||||
echo "file: ${ffmpeg_file}"
|
||||
#echo "ffmpeg with ONLY 'date=${release_date}'"
|
||||
echo "ffmpeg with BOTH 'date=${release_date}' and 'releasedate=${release_date}' set to ISO date strings"
|
||||
|
||||
echo
|
||||
echo "> ffprobe ${ffmpeg_file}"
|
||||
ffprobe "${ffmpeg_file}" 2>&1 | grep date
|
||||
echo
|
||||
echo "> eyeD3 ${ffmpeg_file}"
|
||||
eyeD3 "${ffmpeg_file}" 2>&1 | grep date
|
||||
echo
|
||||
|
||||
echo
|
||||
echo "--------"
|
||||
echo
|
||||
|
||||
|
||||
# echo
|
||||
# echo "--------"
|
||||
# echo
|
||||
# echo "file: releasedate-${ffmpeg_file}"
|
||||
# echo "ffmpeg with ONLY 'releasedate=${release_date}'"
|
||||
|
||||
# echo
|
||||
# echo "> ffprobe releasedate-${ffmpeg_file}"
|
||||
# ffprobe "releasedate-${ffmpeg_file}" 2>&1 | grep date
|
||||
# echo
|
||||
# echo "> eyeD3 releasedate-${ffmpeg_file}"
|
||||
# eyeD3 "releasedate-${ffmpeg_file}" 2>&1 | grep date
|
||||
# echo
|
||||
|
||||
# echo
|
||||
# echo "--------"
|
||||
# echo
|
||||
|
||||
# echo "file: release_date-${ffmpeg_file}"
|
||||
# echo "ffmpeg with ONLY 'release_date=${release_date}'"
|
||||
|
||||
# echo
|
||||
# echo "> ffprobe release_date-${ffmpeg_file}"
|
||||
# ffprobe "release_date-${ffmpeg_file}" 2>&1 | grep date
|
||||
# echo
|
||||
# echo "> eyeD3 release_date-${ffmpeg_file}"
|
||||
# eyeD3 "release_date-${ffmpeg_file}" 2>&1 | grep date
|
||||
# echo
|
||||
|
||||
|
||||
|
||||
chgrp media "${eyed3_file}"
|
||||
chgrp media "${ffmpeg_file}"
|
||||
|
||||
# chgrp media "releasedate-${ffmpeg_file}"
|
||||
# chgrp media "release_date-${ffmpeg_file}"
|
||||
|
||||
|
||||
|
||||
set -x
|
||||
mv "${eyed3_file}" "${abs_podcast_dir}/"
|
||||
mv "${ffmpeg_file}" "${abs_podcast_dir}/"
|
||||
|
||||
#mv "releasedate-${ffmpeg_file}" "${abs_podcast_dir}/"
|
||||
#mv "release_date-${ffmpeg_file}" "${abs_podcast_dir}/"
|
||||
set +x
|
||||
|
||||
echo
|
||||
echo "> ls '${abs_podcast_dir}/'"
|
||||
ls -1 "${abs_podcast_dir}/"
|
||||
echo
|
||||
echo
|
||||
echo "done"
|
||||
|
||||
|
||||
# curl -si 'https://audio.sudo.is/api/rescan' -X PUT \
|
||||
# -H 'Accept: application/json, text/plain, */*' \
|
||||
# -H 'Cookie: authelia_session=W^lyficYWGLOKCevu9qzsTsrFF4i!hA0' \
|
||||
# -H 'Content-Length: 0' \
|
||||
# -H 'TE: trailers' | head -n 1
|
|
@ -8,6 +8,7 @@
|
|||
group: "{{ audiobookshelf_group.gid }}"
|
||||
tags:
|
||||
- audiobookshelf-dirs
|
||||
- abs-dirs
|
||||
loop_control:
|
||||
label: "{{ item.name }}"
|
||||
with_items:
|
||||
|
@ -43,6 +44,7 @@
|
|||
tags:
|
||||
- nginx
|
||||
- audiobookshelf-nginx
|
||||
- abs-nginx
|
||||
notify: reload nginx
|
||||
|
||||
- name: start audiobookshelf container
|
||||
|
@ -79,4 +81,18 @@
|
|||
target: /metadata
|
||||
tags:
|
||||
- audiobookshelf-container
|
||||
- abs-container
|
||||
- docker-containers
|
||||
|
||||
- name: copy abs scripts
|
||||
copy:
|
||||
src: "{{ item }}"
|
||||
dest: "/usr/local/bin/{{ item }}"
|
||||
owner: "{{ audiobookshelf_user.uid }}"
|
||||
group: "{{ audiobookshelf_group.gid }}"
|
||||
mode: 0755
|
||||
with_items:
|
||||
- fix-podcast-date.py
|
||||
tags:
|
||||
- abs-scripts
|
||||
- audiobookshelf-scripts
|
||||
|
|
|
@ -105,7 +105,19 @@ def run_restic(repo_url, restic_args, dry_run, non_interactive):
|
|||
return
|
||||
|
||||
# .run(env={}) is possible (but then child doesnt inherit parent env)
|
||||
return subprocess.run(restic_cmd, check=True)
|
||||
try:
|
||||
ps = subprocess.run(restic_cmd, check=True, capture_output=False)
|
||||
# if ps.stdout is not None:
|
||||
# summary = ps.stdout.decode().split('\n')[-7:]
|
||||
# if backup:
|
||||
# logger.success(f"{summary[2].strip()} ({summary[4]})")
|
||||
except subprocess.CalledProcessError as e:
|
||||
cmd = " ".join(e.cmd)
|
||||
# e.stdout, e.strderr
|
||||
if e.stderr is not None:
|
||||
logger.error(e.stderr.decode())
|
||||
logger.error(f"command '{cmd}' returned non-zero exit status {e.returncode}.")
|
||||
raise SystemExit(e.returncode)
|
||||
|
||||
def find_nobackup(config):
|
||||
# very interesting restic option:
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
input_boolean:
|
||||
amp_muted:
|
||||
name: amp_muted
|
||||
|
||||
input_text:
|
||||
nad_c370_sonos:
|
||||
name: NAD C370 input for Sonos
|
||||
initial: disc
|
||||
pattern: disc
|
||||
|
||||
nad_c370_apple_tv:
|
||||
name: NAD C370 input for Apple TV
|
||||
initial: cd
|
||||
pattern: cd
|
||||
|
||||
nad_c370_tv:
|
||||
name: NAD C370 input for TV
|
||||
initial: cd
|
||||
pattern: cd
|
||||
|
||||
input_select:
|
||||
amp_inputs:
|
||||
name: amp_inputs
|
||||
options:
|
||||
- video
|
||||
- disc
|
||||
- cd
|
||||
- aux
|
||||
- tape1
|
||||
- tape2
|
||||
|
||||
script:
|
||||
amp_on:
|
||||
mode: single
|
||||
sequence:
|
||||
- service: switch.turn_on
|
||||
target:
|
||||
entity_id: switch.nad_c370
|
||||
|
||||
amp_off:
|
||||
mode: single
|
||||
sequence:
|
||||
- service: switch.turn_off
|
||||
target:
|
||||
entity_id: switch.nad_c370
|
||||
|
||||
amp_mute_toggle:
|
||||
mode: single
|
||||
sequence:
|
||||
- service: remote.send_command
|
||||
target:
|
||||
entity_id: remote.broadlink
|
||||
data:
|
||||
command: mute
|
||||
device: nad_c370
|
||||
|
||||
amp_remote_select_input:
|
||||
mode: single
|
||||
sequence:
|
||||
- service: remote.send_command
|
||||
target:
|
||||
entity_id: remote.broadlink
|
||||
data:
|
||||
command: input_{{ source }}
|
||||
device: nad_c370
|
||||
|
||||
amp_select_input2:
|
||||
mode: single
|
||||
sequence:
|
||||
- service: script.amp_remote_select_input
|
||||
data:
|
||||
soure: >-
|
||||
{% set input_device = states('input_select.media_center_input') %}
|
||||
{{ states('input_text.nad_c370' ~ input_device ) }}
|
||||
amp_select_input:
|
||||
mode: single
|
||||
sequence:
|
||||
- if:
|
||||
- condition: state
|
||||
entity_id: input_select.media_center_inputs
|
||||
state: "tv"
|
||||
then:
|
||||
- service: input_boolean.amp_remote_select_input
|
||||
data:
|
||||
amp_input_source: cd
|
||||
- if:
|
||||
- condition: state
|
||||
entity_id: input_select.media_center_inputs
|
||||
state: "apple_tv"
|
||||
then:
|
||||
- service: input_boolean.amp_remtote_select_input
|
||||
data:
|
||||
amp_input_source: cd
|
||||
- if:
|
||||
- condition: state
|
||||
entity_id: input_select.media_center_inputs
|
||||
state: "sonos"
|
||||
then:
|
||||
- service: input_boolean.amp_remtote_select_input
|
||||
data:
|
||||
amp_input_source: disc
|
||||
|
||||
|
||||
media_player:
|
||||
- platform: universal
|
||||
name: NAD C370
|
||||
universal_id: nad_c370
|
||||
children: []
|
||||
turn_on:
|
||||
service: script.amp_on
|
||||
turn_off:
|
||||
service: script.amp_off
|
||||
|
||||
select_source:
|
||||
service: script.amp_select_input
|
||||
|
||||
attributes: state_attr('input_select.amp_inputs', 'options') | source_list
|
|
@ -0,0 +1,132 @@
|
|||
input_boolean:
|
||||
standing_fan_1_state_power:
|
||||
name: standing_fan_1_state_power
|
||||
icon: mdi:fan
|
||||
# no 'initial' value: restores to previous state on restart
|
||||
#initial: false
|
||||
|
||||
input_select:
|
||||
standing_fan_1_preset_mode:
|
||||
name: standing_fan_1_preset_mode
|
||||
options:
|
||||
- silent_night
|
||||
- turbo
|
||||
- normal
|
||||
# no 'initial' value: restores to previous state on restart
|
||||
#initial: normal
|
||||
icon: mdi:fan
|
||||
|
||||
script:
|
||||
standing_fan_1_power_toggle:
|
||||
mode: single
|
||||
sequence:
|
||||
- service: remote.send_command
|
||||
target:
|
||||
entity_id: remote.broadlink
|
||||
data:
|
||||
device: standing_fan_1
|
||||
command: power_toggle
|
||||
|
||||
standing_fan_1_on:
|
||||
mode: single
|
||||
sequence:
|
||||
- if:
|
||||
- condition: state
|
||||
entity_id: input_boolean.standing_fan_1_state_power
|
||||
state: "off"
|
||||
then:
|
||||
- service: script.standing_fan_1_power_toggle
|
||||
- service: input_boolean.turn_on
|
||||
data: {}
|
||||
target:
|
||||
entity_id: input_boolean.standing_fan_1_state_power
|
||||
|
||||
standing_fan_1_off:
|
||||
mode: single
|
||||
sequence:
|
||||
- if:
|
||||
- condition: state
|
||||
entity_id: input_boolean.standing_fan_1_state_power
|
||||
state: "on"
|
||||
then:
|
||||
- service: script.standing_fan_1_power_toggle
|
||||
- service: input_boolean.turn_off
|
||||
data: {}
|
||||
target:
|
||||
entity_id: input_boolean.standing_fan_1_state_power
|
||||
|
||||
standing_fan_1_mode_silent_night:
|
||||
mode: single
|
||||
sequence:
|
||||
- service: remote.send_command
|
||||
target:
|
||||
entity_id: remote.broadlink
|
||||
data:
|
||||
device: standing_fan_1
|
||||
command: silent_night
|
||||
|
||||
standing_fan_1_mode_turbo:
|
||||
mode: single
|
||||
sequence:
|
||||
- service: remote.send_command
|
||||
target:
|
||||
entity_id: remote.broadlink
|
||||
data:
|
||||
device: standing_fan_1
|
||||
command: turbo
|
||||
|
||||
standing_fan_1_mode_normal:
|
||||
mode: single
|
||||
sequence:
|
||||
# send the command for the active (if it is active) mode to turn it off (or back to 'normal')
|
||||
- service: script.standing_fan_1_mode_{{ states('input_select.standing_fan_1_preset_mode') }}
|
||||
|
||||
standing_fan_1_reset_power_state:
|
||||
mode: single
|
||||
sequence:
|
||||
- service: input_boolean.toggle
|
||||
target:
|
||||
entity_id: input_boolean.standing_fan_1_state_power
|
||||
data: {}
|
||||
|
||||
standing_fan_1_set_preset_mode:
|
||||
mode: single
|
||||
sequence:
|
||||
- service: script.standing_fan_1_mode_{{ preset_mode }}
|
||||
- service: input_select.select_option
|
||||
target:
|
||||
entity_id: input_select.standing_fan_1_preset_mode
|
||||
data:
|
||||
option: "{{ preset_mode }}"
|
||||
|
||||
# automation:
|
||||
# - alias: standing_fan_1_hass_shutdown
|
||||
# mode: single
|
||||
# trigger:
|
||||
# - platform: homeassistant
|
||||
# event: shutdown
|
||||
# condition: []
|
||||
# action:
|
||||
# - service: script.standing_fan_1_off
|
||||
|
||||
|
||||
fan:
|
||||
- platform: template
|
||||
fans:
|
||||
standing_fan_1:
|
||||
unique_id: standing_fan_1
|
||||
friendly_name: "Standing fan 1"
|
||||
value_template: "{{ states('input_boolean.standing_fan_1_state_power') }}"
|
||||
preset_mode_template: "{{ states('input_select.standing_fan_1_preset_mode') }}"
|
||||
turn_on:
|
||||
service: script.standing_fan_1_on
|
||||
turn_off:
|
||||
service: script.standing_fan_1_off
|
||||
set_preset_mode:
|
||||
service: script.standing_fan_1_set_preset_mode
|
||||
data:
|
||||
preset_mode: "{{ preset_mode }}"
|
||||
preset_modes:
|
||||
- silent_night
|
||||
- turbo
|
||||
- normal
|
|
@ -0,0 +1,209 @@
|
|||
input_boolean:
|
||||
grow_lights_1_state:
|
||||
name: grow_lights_1_state
|
||||
icon: mdi:sprout
|
||||
|
||||
input_number:
|
||||
grow_lights_1_brightness:
|
||||
name: grow_lights_1_brightness
|
||||
min: 1
|
||||
max: 8
|
||||
step: 1.0
|
||||
initial: 8
|
||||
mode: slider
|
||||
icon: mdi:sprout
|
||||
|
||||
|
||||
script:
|
||||
grow_lights_1_on:
|
||||
mode: single
|
||||
sequence:
|
||||
- service: input_boolean.turn_on
|
||||
data: {}
|
||||
target:
|
||||
entity_id: input_boolean.grow_lights_1_state
|
||||
- service: remote.send_command
|
||||
target:
|
||||
entity_id: remote.broadlink
|
||||
data:
|
||||
device: grow_lights_1
|
||||
command: power_on
|
||||
num_repeats: 3
|
||||
delay_secs: 0.1
|
||||
hold_secs: 0.1
|
||||
- service: script.grow_lights_1_brightness_max
|
||||
|
||||
grow_lights_1_off:
|
||||
mode: single
|
||||
sequence:
|
||||
- service: input_boolean.turn_off
|
||||
data: {}
|
||||
target:
|
||||
entity_id: input_boolean.grow_lights_1_state
|
||||
- service: remote.send_command
|
||||
target:
|
||||
entity_id: remote.broadlink
|
||||
data:
|
||||
device: grow_lights_1
|
||||
command: power_off
|
||||
num_repeats: 3
|
||||
delay_secs: 0.1
|
||||
hold_secs: 0.1
|
||||
|
||||
grow_lights_1_brightness_up:
|
||||
mode: single
|
||||
sequence:
|
||||
- service: remote.send_command
|
||||
target:
|
||||
entity_id: remote.broadlink
|
||||
data:
|
||||
device: grow_lights_1
|
||||
command: brightness_up
|
||||
delay_secs: 0.1
|
||||
hold_secs: 0.1
|
||||
num_repeats: "{{ num_repeats | default(1) | int | abs }}"
|
||||
|
||||
grow_lights_1_brightness_down:
|
||||
mode: single
|
||||
sequence:
|
||||
- service: remote.send_command
|
||||
target:
|
||||
entity_id: remote.broadlink
|
||||
data:
|
||||
device: grow_lights_1
|
||||
command: brightness_down
|
||||
delay_secs: 0.1
|
||||
hold_secs: 0.1
|
||||
num_repeats: "{{ num_repeats | default(1) | int | abs }}"
|
||||
|
||||
grow_lights_1_brightness_min:
|
||||
mode: single
|
||||
sequence:
|
||||
- service: remote.send_command
|
||||
target:
|
||||
entity_id: remote.broadlink
|
||||
data:
|
||||
device: grow_lights_1
|
||||
command: brightness_down
|
||||
hold_secs: 0.1
|
||||
num_repeats: "{{ 8*2 | int }}"
|
||||
- service: input_number.set_value
|
||||
target:
|
||||
entity_id: input_number.grow_lights_1_brightness
|
||||
data:
|
||||
value: "{{ 1 | int }}"
|
||||
|
||||
grow_lights_1_brightness_max:
|
||||
mode: single
|
||||
sequence:
|
||||
- service: remote.send_command
|
||||
target:
|
||||
entity_id: remote.broadlink
|
||||
data:
|
||||
device: grow_lights_1
|
||||
command: brightness_up
|
||||
hold_secs: 0.1
|
||||
num_repeats: "{{ 8*2 | int }}"
|
||||
- service: input_number.set_value
|
||||
target:
|
||||
entity_id: input_number.grow_lights_1_brightness
|
||||
data:
|
||||
value: "{{ 8 | int }}"
|
||||
|
||||
grow_lights_1_brightness_set:
|
||||
mode: single
|
||||
sequence:
|
||||
- service: script.grow_lights_1_brightness_min
|
||||
- service: script.grow_lights_1_brightness_up
|
||||
data:
|
||||
num_repeats: "{{ brightness_steps | int }}"
|
||||
- service: input_number.set_value
|
||||
target:
|
||||
entity_id: input_number.grow_lights_1_brightness
|
||||
data:
|
||||
value: "{{ brightness_steps | int }}"
|
||||
|
||||
automation:
|
||||
- alias: grow_lights_1_state
|
||||
trigger:
|
||||
- platform: time_pattern
|
||||
minutes: /15
|
||||
condition: []
|
||||
action:
|
||||
- service: >-
|
||||
script.grow_lights_1_{{ states('binary_sensor.grow_lights_1_schedule') }}
|
||||
data: {}
|
||||
|
||||
- alias: grow_lights_1_hass_start
|
||||
trigger:
|
||||
- platform: homeassistant
|
||||
event: start
|
||||
condition: []
|
||||
action:
|
||||
- service: >-
|
||||
light.turn_{{ states('binary_sensor.grow_lights_1_schedule') }}
|
||||
target:
|
||||
entity_id: light.grow_lights_1
|
||||
data: {}
|
||||
|
||||
- alias: grow_lights_1_schedule
|
||||
trigger:
|
||||
- platform: state
|
||||
entity_id: binary_sensor.grow_lights_1_schedule
|
||||
to: "on"
|
||||
- platform: state
|
||||
entity_id: binary_sensor.grow_lights_1_schedule
|
||||
to: "off"
|
||||
condition: []
|
||||
action:
|
||||
- service: >-
|
||||
light.turn_{{ trigger.to_state.state }}
|
||||
target:
|
||||
entity_id: light.grow_lights_1
|
||||
data: {}
|
||||
|
||||
|
||||
binary_sensor:
|
||||
- platform: tod
|
||||
unique_id: grow_lights_1_schedule
|
||||
name: grow_lights_1_schedule
|
||||
after: "09:00"
|
||||
before: "19:00"
|
||||
|
||||
|
||||
light:
|
||||
- platform: template
|
||||
lights:
|
||||
grow_lights_1:
|
||||
unique_id: grow_lights_1
|
||||
friendly_name: "Grow lights 1"
|
||||
value_template: "{{ states('input_boolean.grow_lights_1_state') }}"
|
||||
level_template: >-
|
||||
{% set brightness_max_8 = states('input_number.grow_lights_1_brightness')|int %}
|
||||
{% set brightness_max_256 = brightness_max_8 * 32 %}
|
||||
{% set brightness_max_255 = min(255, brightness_max_256) %}
|
||||
{{ max(1, brightness_max_255) }}
|
||||
icon_template: >-
|
||||
{% if is_state(this.entity_id, 'on') %}
|
||||
mdi:sprout
|
||||
{% else %}
|
||||
mdi:sprout-outline
|
||||
{% endif %}
|
||||
turn_on:
|
||||
service: script.grow_lights_1_on
|
||||
turn_off:
|
||||
service: script.grow_lights_1_off
|
||||
set_level:
|
||||
service: >-
|
||||
{% if brightness > 200 %}
|
||||
script.grow_lights_1_brightness_max
|
||||
{% elif brightness < 100 %}
|
||||
script.grow_lights_1_brightness_min
|
||||
{% else %}
|
||||
script.grow_lights_1_brightness_set
|
||||
{% endif %}
|
||||
data:
|
||||
brightness: "{{ brightness | int }}"
|
||||
brightness_steps: >-
|
||||
{% set steps = brightness // 32 | default(8) | round | int %}
|
||||
{{ max(1, steps) | int }}
|
|
@ -0,0 +1,123 @@
|
|||
input_boolean:
|
||||
tv_assumed_state:
|
||||
name: tv_assumed_power_state
|
||||
icon: mdi:television
|
||||
|
||||
|
||||
input_select:
|
||||
media_center_inputs:
|
||||
name: media_center_inputs
|
||||
options:
|
||||
- sonos
|
||||
- apple_tv
|
||||
- tv
|
||||
|
||||
script:
|
||||
tv_power_toggle:
|
||||
mode: single
|
||||
sequence:
|
||||
- service: remote.send_command
|
||||
target:
|
||||
entity_id: remote.broadlink
|
||||
data:
|
||||
command: power_toggle
|
||||
device: tv
|
||||
|
||||
tv_on:
|
||||
mode: single
|
||||
sequence:
|
||||
- if:
|
||||
- condition: state
|
||||
entity_id: input_boolean.tv_assumed_power_state
|
||||
state: "off"
|
||||
then:
|
||||
- service: script.tv_power_toggle
|
||||
- service: input_boolean.turn_on
|
||||
target:
|
||||
entity_id: input_boolean.tv_assumed_power_state
|
||||
|
||||
tv_off:
|
||||
mode: single
|
||||
sequence:
|
||||
- if:
|
||||
- condition: state
|
||||
entity_id: input_boolean.tv_assumed_power_state
|
||||
state: "on"
|
||||
then:
|
||||
- service: script.tv_power_toggle
|
||||
- service: input_boolean.turn_off
|
||||
target:
|
||||
entity_id: input_boolean.tv_assumed_power_state
|
||||
|
||||
apple_tv_on:
|
||||
mode: single
|
||||
sequence:
|
||||
# - service: homeassistant.reload_config_entry
|
||||
# target:
|
||||
# entity_id:
|
||||
# #- media_player.apple_tv
|
||||
# - remote.apple_tv
|
||||
- service: remote.send_command
|
||||
data:
|
||||
command:
|
||||
- wakeup
|
||||
- select
|
||||
delay_secs: 1
|
||||
target:
|
||||
entity_id: media_player.apple_tv
|
||||
|
||||
apple_tv_off:
|
||||
mode: single
|
||||
sequence:
|
||||
- service: remote.send_command
|
||||
data:
|
||||
command:
|
||||
- home_hold
|
||||
- select
|
||||
delay_secs: 1
|
||||
target:
|
||||
entity_id: media_player.apple_tv
|
||||
# - service: homeassistant.reload_config_entry
|
||||
# target:
|
||||
# entity_id:
|
||||
# #- media_player.apple_tv
|
||||
# - remote.apple_tv
|
||||
|
||||
|
||||
media_center_select_input:
|
||||
mode: single
|
||||
sequence:
|
||||
- service: input_select.select_option
|
||||
target:
|
||||
entity_id: input_boolean.media_center_inputs
|
||||
data:
|
||||
option: "{{ media_center_input }}"
|
||||
- service: script.amp_select_input
|
||||
# another one for tv?
|
||||
|
||||
|
||||
media_center_on:
|
||||
mode: single
|
||||
sequence:
|
||||
- service: script.amp_on
|
||||
- service: script.tv_on
|
||||
- service: script.apple_tv_on
|
||||
|
||||
media_center_off:
|
||||
mode: single
|
||||
sequence:
|
||||
- service: script.amp_off
|
||||
- service: script.tv_off
|
||||
- service: script.apple_tv_off
|
||||
- service: media_player.turn_off
|
||||
target:
|
||||
entity_id: media_player.owntone_output_living_room
|
||||
|
||||
media_player:
|
||||
- platform: universal
|
||||
name: Media Center
|
||||
universal_id: media_center
|
||||
children:
|
||||
- media_player.apple_tv
|
||||
- media_player.living_room_audio
|
||||
- media_player.owntone_server
|
|
@ -0,0 +1,88 @@
|
|||
input_boolean:
|
||||
usb_led_string_1_state:
|
||||
name: usb_led_string_1_state
|
||||
initial: false
|
||||
|
||||
script:
|
||||
usb_led_string_1_on:
|
||||
sequence:
|
||||
- service: remote.send_command
|
||||
target:
|
||||
entity_id: remote.broadlink
|
||||
data:
|
||||
device: usb_led_string_1
|
||||
command:
|
||||
- power_on
|
||||
- steady_on
|
||||
- service: input_boolean.turn_on
|
||||
data: {}
|
||||
target:
|
||||
entity_id: input_boolean.usb_led_string_1_state
|
||||
|
||||
usb_led_string_1_off:
|
||||
sequence:
|
||||
- service: remote.send_command
|
||||
target:
|
||||
entity_id: remote.broadlink
|
||||
data:
|
||||
device: usb_led_string_1
|
||||
command:
|
||||
- power_off
|
||||
- service: input_boolean.turn_off
|
||||
data: {}
|
||||
target:
|
||||
entity_id: input_boolean.usb_led_string_1_state
|
||||
|
||||
automation:
|
||||
- alias: usb_led_string_1_state
|
||||
trigger:
|
||||
- platform: state
|
||||
entity_id: input_boolean.usb_led_string_1_state
|
||||
for:
|
||||
hours: 0
|
||||
minutes: 5
|
||||
seconds: 0
|
||||
to: "on"
|
||||
id: "on"
|
||||
- platform: state
|
||||
entity_id: input_boolean.usb_led_string_1_state
|
||||
for:
|
||||
hours: 0
|
||||
minutes: 5
|
||||
seconds: 0
|
||||
to: "off"
|
||||
id: "off"
|
||||
condition: []
|
||||
action:
|
||||
- service: >-
|
||||
script.usb_led_string_1_{{ trigger.id }}
|
||||
data: {}
|
||||
|
||||
- alias: usb_led_string_1_hass_start
|
||||
trigger:
|
||||
- platform: homeassistant
|
||||
event: start
|
||||
condition: []
|
||||
action:
|
||||
- service: >-
|
||||
{% if is_state('sun.sun', 'below_horizon') %}
|
||||
light.turn_on
|
||||
{% else %}
|
||||
light.turn_off
|
||||
{% endif %}
|
||||
target:
|
||||
entity_id: light.usb_led_string_1
|
||||
data: {}
|
||||
|
||||
light:
|
||||
- platform: template
|
||||
lights:
|
||||
usb_led_string_1:
|
||||
unique_id: usb_led_string_1
|
||||
friendly_name: "USB LED string 1"
|
||||
value_template: "{{ states('input_boolean.usb_led_string_1_state') }}"
|
||||
icon_template: mdi:led-strip-variant
|
||||
turn_on:
|
||||
service: script.usb_led_string_1_on
|
||||
turn_off:
|
||||
service: script.usb_led_string_1_off
|
|
@ -32,6 +32,7 @@
|
|||
mode: "0775"
|
||||
- name: home-assistant
|
||||
- name: home-assistant/config
|
||||
- name: home-assistant/config/packages
|
||||
- name: home-assistant/config/python_scripts
|
||||
- name: home-assistant/config/bvg
|
||||
- name: home-assistant/.config # might not be needed, misread 'cache' as 'config'
|
||||
|
@ -68,7 +69,6 @@
|
|||
- hass-git
|
||||
- hass-git-clone
|
||||
|
||||
|
||||
- name: home assistant config files
|
||||
template:
|
||||
src: "{{ item }}.j2"
|
||||
|
@ -81,20 +81,36 @@
|
|||
- secrets.yaml
|
||||
- configuration.yaml
|
||||
- templates.yaml
|
||||
- climate.yaml
|
||||
- automations-ansible-managed.yaml
|
||||
- scripts-ansible-managed.yaml
|
||||
- blink1.yaml
|
||||
# - packages/climate.yaml
|
||||
- packages/toothbrush.yaml
|
||||
tags:
|
||||
- hass-config
|
||||
|
||||
- name: copy dashboards
|
||||
- name: copy config files
|
||||
copy:
|
||||
src: "{{ item }}"
|
||||
dest: "{{ systemuserlist.hass.home }}/home-assistant/config/{{ item }}"
|
||||
mode: 0644
|
||||
owner: "{{ systemuserlist.hass.uid }}"
|
||||
group: "{{ systemuserlist.hass.gid }}"
|
||||
notify: restart hass container
|
||||
with_items:
|
||||
- packages/usb_led_strings.yaml
|
||||
- packages/grow_lights.yaml
|
||||
- packages/fans.yaml
|
||||
tags:
|
||||
- hass-config
|
||||
|
||||
- name: copy dashboard config files
|
||||
copy:
|
||||
src: "private/hass/{{ item }}"
|
||||
dest: "{{ systemuserlist.hass.home }}/home-assistant/config/{{ item }}"
|
||||
mode: 0755
|
||||
owner: hass
|
||||
group: hass
|
||||
mode: 0644
|
||||
owner: "{{ systemuserlist.hass.uid }}"
|
||||
group: "{{ systemuserlist.hass.gid }}"
|
||||
notify: restart hass container
|
||||
with_items:
|
||||
- mini.yaml
|
||||
|
@ -200,6 +216,7 @@
|
|||
- mplayer
|
||||
- bluez
|
||||
- bluetooth
|
||||
- espeak-ng # for glados-tts
|
||||
- fapg
|
||||
- podget
|
||||
- sqlite3
|
||||
|
@ -281,8 +298,8 @@
|
|||
- name: start home-assistant container
|
||||
docker_container:
|
||||
name: hass
|
||||
image: ghcr.io/home-assistant/home-assistant:stable
|
||||
#image: git.sudo.is/ben/hass:latest
|
||||
#image: ghcr.io/home-assistant/home-assistant:stable
|
||||
image: git.sudo.is/ben/hass:latest
|
||||
detach: true
|
||||
pull: true
|
||||
restart_policy: "unless-stopped"
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
title: 'device not reporting'
|
||||
message: 'stopped reporting: {%raw%}{{ trigger.id }}{%endraw%}'
|
||||
|
||||
{% if hass_light_switches | length > 0 -%}
|
||||
- alias: refresh_light_switches_state
|
||||
description: the tkbhome switches dont automatically report their state
|
||||
trigger:
|
||||
|
@ -39,6 +40,7 @@
|
|||
- {{ item.entity_id }}
|
||||
{% endif -%}
|
||||
{% endfor %}
|
||||
{% endif -%}
|
||||
|
||||
{% for item in hass_light_switches -%}
|
||||
{% set domain = item.entity_id.split('.')[0] %}
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
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:
|
||||
|
|
|
@ -62,7 +62,7 @@ automation ansible: !include automations-ansible-managed.yaml
|
|||
script: !include scripts.yaml
|
||||
scene: !include scenes.yaml
|
||||
template: !include templates.yaml
|
||||
climate: !include climate.yaml
|
||||
#climate: !include climate.yaml
|
||||
|
||||
# Text to speech
|
||||
tts:
|
||||
|
@ -89,6 +89,8 @@ http:
|
|||
- 127.0.0.1
|
||||
use_x_forwarded_for: true
|
||||
|
||||
api:
|
||||
|
||||
frontend:
|
||||
themes: !include_dir_merge_named themes
|
||||
|
||||
|
@ -158,6 +160,7 @@ homeassistant:
|
|||
customize:
|
||||
zone.home:
|
||||
friendly_name: S21
|
||||
packages: !include_dir_named packages
|
||||
|
||||
lovelace:
|
||||
mode: storage
|
||||
|
@ -228,6 +231,24 @@ sensor:
|
|||
file_path: "/config/bvg/"
|
||||
{% endfor %}
|
||||
|
||||
- platform: worldclock
|
||||
time_zone: UTC
|
||||
name: UTC
|
||||
- platform: worldclock
|
||||
time_zone: Atlantic/Reykjavik
|
||||
name: Iceland
|
||||
- platform: worldclock
|
||||
time_zone: Europe/Berlin
|
||||
name: Berlin
|
||||
- platform: worldclock
|
||||
time_zone: America/New_York
|
||||
name: Boston
|
||||
- platform: worldclock
|
||||
time_zone: America/Chicago
|
||||
name: Austin
|
||||
|
||||
|
||||
|
||||
- platform: waqi
|
||||
token: !secret waqi_token
|
||||
locations:
|
||||
|
@ -306,24 +327,33 @@ input_text:
|
|||
|
||||
device_tracker:
|
||||
- platform: bluetooth_le_tracker
|
||||
# device tracker will only look for the following global settings
|
||||
# under the configuration of the first configured platform
|
||||
interval_seconds: 12
|
||||
track_new_devices: false
|
||||
track_battery: false
|
||||
consider_home: 150
|
||||
new_device_defaults:
|
||||
track_new_devices: false
|
||||
consider_home: 120
|
||||
# setting this to 'false' still results in new devices being added
|
||||
# to known_devices.yaml when they are discovered, but they wont be
|
||||
# tracked unless 'track' is set to 'true' for a device there (edit
|
||||
# the file to track a discovered device).
|
||||
track_new_devices: true
|
||||
#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
|
||||
request_rssi: true
|
||||
track_new_devices: true
|
||||
- platform: ping
|
||||
# these are probably not used, because they are "global settings"
|
||||
# and only the first values from the first platform (bluetooth_le_tracker)
|
||||
# are used according to the docs
|
||||
interval_seconds: 30
|
||||
track_new_devices: true
|
||||
# but consider_hoem can be overridden
|
||||
consider_home: 120
|
||||
hosts:
|
||||
{% for target in hass_ping -%}
|
||||
{% if target.device_tracker|default(true) -%}
|
||||
{{ target.name }}: {{ target.host }}
|
||||
ping_{{ target.name }}: {{ target.host }}
|
||||
{% endif -%}
|
||||
{% endfor %}
|
||||
|
||||
|
@ -402,24 +432,19 @@ light:
|
|||
blink1: !include blink1.yaml
|
||||
{% endif %}
|
||||
|
||||
# enable 'wake_on_lan' for 'samsungtv'
|
||||
wake_on_lan:
|
||||
{#
|
||||
# feedreader:
|
||||
# urls:
|
||||
# {% for item in hass_feedreader -%}
|
||||
# - "{{ item.url | trim }}"
|
||||
# {% endfor %}
|
||||
#}
|
||||
|
||||
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 }}"
|
||||
logger:
|
||||
default: warning
|
||||
logs:
|
||||
pyatv: debug
|
||||
homeassistant.components.apple_tv: debug
|
||||
|
||||
feedreader:
|
||||
urls:
|
||||
{% for item in hass_feedreader -%}
|
||||
- "{{ item.url | trim }}"
|
||||
{% endfor %}
|
||||
|
||||
{# logger:
|
||||
# logs:
|
||||
# pyatv: debug
|
||||
# homeassistant.components.apple_tv: debug #}
|
||||
# enable SVT play
|
||||
svt_play:
|
||||
|
|
|
@ -25,13 +25,26 @@ fi
|
|||
mkdir -p ${PATH_REPO}/config/
|
||||
mkdir -p ${PATH_REPO}/config/.storage
|
||||
|
||||
{% for item in hass_config_repo_files -%}
|
||||
cp ${PATH_HASS}/config/{{ item }} ${PATH_REPO}/config/{{ item }}
|
||||
{% for item in hass_config_repo_cp -%}
|
||||
{% if item.dir|default(false) %}{% set cp_r = "r" -%}
|
||||
{% else %}{% set cp_r = "" -%}
|
||||
{% endif -%}
|
||||
{% if item.src.endswith("*") -%}
|
||||
{% if item.src.count("/") > 0 -%}
|
||||
{% set dest = item.src.split("/")[:-1] | join("/") + "/" -%}
|
||||
{% else -%}
|
||||
{% set dest = "" -%}
|
||||
{% endif -%}
|
||||
{% else -%}
|
||||
{% set dest = item.src %}
|
||||
{% endif %}
|
||||
cp -a{{ cp_r }} ${PATH_HASS}/config/{{ item.src }} ${PATH_REPO}/config/{{ dest }}
|
||||
{% endfor %}
|
||||
|
||||
|
||||
if test -n "$(git status --porcelain)" ; then
|
||||
{% for item in hass_config_repo_files -%}
|
||||
git add config/{{ item }} > /dev/null
|
||||
{% for item in hass_config_repo_cp -%}
|
||||
git add config/{{ item.src }} > /dev/null
|
||||
{% endfor %}
|
||||
|
||||
git commit -m "config updated" > /dev/null
|
||||
|
|
|
@ -5,7 +5,4 @@
|
|||
# sync hass config to {{ hass_config_repo }}
|
||||
*/15 * * * * {{ systemuserlist.hass.username }} /usr/local/bin/git-hass-config.sh
|
||||
|
||||
|
||||
|
||||
|
||||
#
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
input_number:
|
||||
toothbrushing_target_time:
|
||||
name: toothbrushing_target_time
|
||||
min: 10
|
||||
max: 600
|
||||
step: 1
|
||||
initial: {{ toothbrushing_target_time }}
|
||||
icon: "mdi:toothbrush-electric"
|
||||
mode: box
|
||||
|
||||
template:
|
||||
- sensor:
|
||||
|
||||
{% for item in hass_toothbrushes -%}
|
||||
|
||||
- name: "{{ item.entity_name }}"
|
||||
unique_id: "{{ item.entity_name }}"
|
||||
icon: "mdi:toothbrush-electric"
|
||||
state_class: "measurement"
|
||||
unit_of_measurement: "s"
|
||||
{% raw -%}
|
||||
state: >-
|
||||
{% set target_time = states('input_number.toothbrushing_target_time') | int %}
|
||||
{% set source_entity_id = this.attributes.source_entity_id | default("unknown") %}
|
||||
{% if states(source_entity_id) not in ["unknown", "unavailable"] %}
|
||||
{% set time = states(source_entity_id) | int %}
|
||||
{% endif %}
|
||||
{% set brush_time = time|default(0)|int %}
|
||||
{{ max(0, target_time - brush_time) }}
|
||||
{% endraw -%}
|
||||
attributes:
|
||||
source_entity_id: "{{ item.source_entity_id }}"
|
||||
|
||||
{% endfor %}
|
||||
|
||||
- binary_sensor:
|
||||
|
||||
{% for item in hass_toothbrushes -%}
|
||||
|
||||
- name: "{{ item.entity_name }}"
|
||||
unique_id: "{{ item.entity_name }}"
|
||||
icon: "mdi:toothbrush-electric"
|
||||
delay_off: "00:00:10"
|
||||
{% raw -%}
|
||||
state: >-
|
||||
{{ states("sensor." ~ this.name)|int == 0 }}
|
||||
attributes:
|
||||
hours_since_last_brush: >-
|
||||
{% set time_since = now() - this.last_changed %}
|
||||
{{ time_since.seconds // 60 // 60 }}
|
||||
|
||||
{% endraw %}
|
||||
|
||||
{% endfor %}
|
|
@ -1,3 +1,22 @@
|
|||
{#
|
||||
# device attributes:
|
||||
# - config_entries
|
||||
# - connections
|
||||
# - identifiers
|
||||
# - manufacturer
|
||||
# - model
|
||||
# - name
|
||||
# - sw_version
|
||||
# - hw_version
|
||||
# - entry_type
|
||||
# - id
|
||||
# - via_device_id
|
||||
# - area_id
|
||||
# - name_by_user
|
||||
# - disabled_by
|
||||
# - configuration_url
|
||||
#}
|
||||
|
||||
- sensor:
|
||||
- name: "chance_of_rain"
|
||||
unit_of_measurement: "%"
|
||||
|
@ -14,7 +33,7 @@
|
|||
{% if 'status' in radiator %}
|
||||
|
||||
- name: "radiator_{{ radiator.name }}_last_updated"
|
||||
unit_of_measurement: "minutes"
|
||||
unit_of_measurement: "min"
|
||||
icon: "mdi:update"
|
||||
device_class: duration
|
||||
state: >-
|
||||
|
@ -30,6 +49,22 @@
|
|||
{% for linux_tracker in hass_linux_presence_trackers -%}
|
||||
{% endfor %}
|
||||
|
||||
|
||||
|
||||
{# {% if is_state("sensor." ~ this.name ~ "_state", "unavailable") %}
|
||||
# {{ false | bool }}
|
||||
# {% elif is_state("sensor." ~ this.name ~ "_state", "unavailable") %}
|
||||
# {{ false | bool }}
|
||||
# {% elif is_state("sensor." ~ this.name ~ "_time", "unknown") %}
|
||||
# {{ false | bool }}
|
||||
# {% else %}
|
||||
# {{ true | bool }}
|
||||
#
|
||||
# friendly_name: >-
|
||||
# {{ device_attr("sensor." ~ this.name ~ "_state", "name_by_user") }}
|
||||
#
|
||||
#}
|
||||
|
||||
- binary_sensor:
|
||||
{% if blink1_enabled -%}
|
||||
- name: "blink1_on"
|
||||
|
@ -51,15 +86,6 @@
|
|||
{{ 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 +%}
|
||||
|
@ -88,6 +114,7 @@
|
|||
{% endif %}
|
||||
{% endraw %}
|
||||
|
||||
|
||||
{% for linux_tracker in hass_linux_presence_trackers -%}
|
||||
- name: {{ linux_tracker.name }}_active
|
||||
icon: "mdi:laptop"
|
||||
|
|
|
@ -224,12 +224,13 @@ audio {
|
|||
nickname = "Computer"
|
||||
|
||||
# Type of the output (alsa, pulseaudio, dummy or disabled)
|
||||
type = "disabled"
|
||||
#type = "disabled"
|
||||
type = "pulseaudio"
|
||||
|
||||
# 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 = ""
|
||||
server = "localhost"
|
||||
|
||||
# Audio PCM device name for local audio output - ALSA only
|
||||
#card = "default"
|
||||
|
@ -379,7 +380,7 @@ spotify {
|
|||
# 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
|
||||
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
|
||||
|
|
Loading…
Reference in New Issue