Рано или поздно Вы придете к тому, что Вам понадобится посмотреть как изменялась например температура в комнате на Вашем датчике температуры. Или посмотреть когда открывалась та или иная дверь по датчику открытия двери.
В Home Аssistant по умолчанию для хранения данных используют СУБД SQLite. Как настроить БД, написано в разделе официального сайта, посвященном интеграции recorder.
Но данные в Home Assistant хранятся только 14 дней. Не более. И вся остальная информация по истечению 30 дней начинает перезаписываться.
Ну и естественно если Вам нужно хранение данных больше 14 дней, то это нам не подходит. Очень интересно же посмотреть температуру например через год и сравнить как она изменилась.
Какие же альтернативы?
Среди интеграций на сайте можно найти InfluxDB , а также одноименный аддон. В данной СУБД мы можем хранить данные практически бесконечно (пока не закончится место на диске). Так-же можем настраивать интервал хранения данных.
Ниже описана установка и настройка InfluxDB для хранения даннхы из Home Assistant и не только.
ТК моя установка Home Assistant это Docker-контейнер, то продолжим использовать эту среду контейнеризации.
Необходимо создать нужные директории в месте хранения данных образов: у меня это /opt/containerd/influxdb. В созданной директории необходимо разметить docker-compose.yaml. Я использую версию образа 1.8 мне так удобно.
Следующее содержимое помещаем в файл:
version: '3.0'
services:
influxdb:
container_name: influxdb
image: influxdb:1.8
restart: unless-stopped
ports:
- 8086:8086 # WebUI
environment:
- TZ=Europe/Moscow
- DOCKER_INFLUXDB_INIT_MODE=setup
- DOCKER_INFLUXDB_INIT_USERNAME=influx_user
- DOCKER_INFLUXDB_INIT_PASSWORD=influx_password
- DOCKER_INFLUXDB_INIT_ORG=smg
volumes:
- /opt/containerd/influxdb/data:/var/lib/influxdb
- /opt/containerd/influxdb/config/:/etc/influxdb
ulimits:
nofile:
soft: 32768
hard: 32768
Запускаем
sudo docker-compose up
Смотрим вывод в консоль, если все в порядке запускаем в виде демона
sudo docker-compose up -d
Обращаю внимание, что у данной бд есть встроенный конфиг. Таким образом, если не указать переменных в файле докера и не будет конфиг файла в объявленной директории, то будет загружен конфиг по умолчанию.
по этому приведу рабочий конфиг, с путями как файле docker-compose.yaml
reporting-disabled = false
bind-address = "127.0.0.1:8088"
[meta]
dir = "/var/lib/influxdb/meta"
retention-autocreate = true
logging-enabled = true
[data]
dir = "/var/lib/influxdb/data"
index-version = "inmem"
wal-dir = "/var/lib/influxdb/wal"
wal-fsync-delay = "0s"
validate-keys = false
strict-error-handling = false
query-log-enabled = true
cache-max-memory-size = 1073741824
cache-snapshot-memory-size = 26214400
cache-snapshot-write-cold-duration = "10m0s"
compact-full-write-cold-duration = "4h0m0s"
compact-throughput = 50331648
compact-throughput-burst = 50331648
max-series-per-database = 1000000
max-values-per-tag = 100000
max-concurrent-compactions = 0
max-index-log-file-size = 1048576
series-id-set-cache-size = 100
series-file-max-concurrent-snapshot-compactions = 0
trace-logging-enabled = false
tsm-use-madv-willneed = false
[coordinator]
write-timeout = "10s"
max-concurrent-queries = 0
query-timeout = "0s"
log-queries-after = "0s"
max-select-point = 0
max-select-series = 0
max-select-buckets = 0
[retention]
enabled = true
check-interval = "30m0s"
[shard-precreation]
enabled = true
check-interval = "10m0s"
advance-period = "30m0s"
[monitor]
store-enabled = true
store-database = "_internal"
store-interval = "10s"
[subscriber]
enabled = true
http-timeout = "30s"
insecure-skip-verify = false
ca-certs = ""
write-concurrency = 40
write-buffer-size = 1000
[http]
enabled = true
bind-address = ":8086"
auth-enabled = false
log-enabled = true
suppress-write-log = false
write-tracing = false
flux-enabled = false
flux-log-enabled = false
pprof-enabled = true
pprof-auth-enabled = false
debug-pprof-enabled = false
ping-auth-enabled = false
prom-read-auth-enabled = false
https-enabled = false
https-certificate = "/etc/ssl/influxdb.pem"
https-private-key = ""
max-row-limit = 0
max-connection-limit = 0
shared-secret = ""
realm = "InfluxDB"
unix-socket-enabled = false
unix-socket-permissions = "0777"
bind-socket = "/var/run/influxdb.sock"
max-body-size = 25000000
access-log-path = ""
max-concurrent-write-limit = 0
max-enqueued-write-limit = 0
enqueued-write-timeout = 30000000000
[logging]
format = "auto"
level = "info"
suppress-logo = false
[[graphite]]
enabled = false
bind-address = ":2003"
database = "graphite"
retention-policy = ""
protocol = "tcp"
batch-size = 5000
batch-pending = 10
batch-timeout = "1s"
consistency-level = "one"
separator = "."
udp-read-buffer = 0
[[collectd]]
enabled = false
bind-address = ":25826"
database = "collectd"
retention-policy = ""
batch-size = 5000
batch-pending = 10
batch-timeout = "10s"
read-buffer = 0
typesdb = "/usr/share/collectd/types.db"
security-level = "none"
auth-file = "/etc/collectd/auth_file"
parse-multivalue-plugin = "split"
[[opentsdb]]
enabled = false
bind-address = ":4242"
database = "opentsdb"
retention-policy = ""
consistency-level = "one"
tls-enabled = false
certificate = "/etc/ssl/influxdb.pem"
batch-size = 1000
batch-pending = 5
batch-timeout = "1s"
log-point-errors = true
[[udp]]
enabled = false
bind-address = ":8089"
database = "udp"
retention-policy = ""
batch-size = 5000
batch-pending = 10
read-buffer = 0
batch-timeout = "1s"
precision = ""
[continuous_queries]
log-enabled = true
enabled = true
query-stats-enabled = false
run-interval = "1s"
[tls]
min-version = ""
max-version = ""
Для начала создадим базу данных в influxDB: перейдем в контейнер
sudo docker exec -it influxdb bash
Зайдем в шелл самой influxDB
influx
>
create database homeassistant;
Теперь необходимо уговорить Home Assistant сгружать данные в нашу БД. Есть 2 пути через token или через логин-пароль. token можно получить через вебинтерфейс, но тк мы явно задали логин-пароль в docker-compose.yaml , то будем использовать их.
При помощи редактора в конфигурационном файле configuration.yaml нашего Home Assistant добавим:
influxdb:
### Connection Setting ###
host: 10.10.10.10
port: 8086
database: homeassistant
username: influx_user
password: influx_password
max_retries: 3
default_measurement: state
### measurements setup ###
tags:
source: HA
tags_attributes:
- friendly_name
default_measurement: units
exclude:
entities:
- zone.home
domains:
- persistent_notification
- person
include:
domains:
- sensor
- binary_sensor
Перезагружаем Home Assistant идем пьем кофе, в туалет и мыться) Возвращаемся и проверяем какие же значения накопились у нас в БД.
root@3a10718c527b:/# influx
Connected to http://localhost:8086 version 1.8.10
InfluxDB shell version: 1.8.10
> use homeassistant
Using database homeassistant
> select * from m3;
name: m3
time domain entity_id friendly_name source value
---- ------ --------- ------------- ------ -----
1691592681106899000 sensor cold_water_metter Счетчик холодной воды HA 111.2
1691593297217400000 sensor hot_water_metter Счетчик горячей воды HA 61.599
1691595210053849000 sensor cold_water_metter Счетчик холодной воды HA 111.21
1691595336062350000 sensor hot_water_metter Счетчик горячей воды HA 61.609
1691595362178264000 sensor cold_water_metter Счетчик холодной воды HA 111.22
1691595498080755000 sensor cold_water_metter Счетчик холодной воды HA 111.23
1691595515080629000 sensor hot_water_metter Счетчик горячей воды HA 61.619
1691595591153860000 sensor cold_water_metter Счетчик холодной воды HA 111.24
1691604515214112000 sensor cold_water_metter Счетчик холодной воды HA 111.25
1691604649140700000 sensor hot_water_metter Счетчик горячей воды HA 61.629
1691605580169837000 sensor cold_water_metter Счетчик холодной воды HA 111.26
1691606680094474000 sensor hot_water_metter Счетчик горячей воды HA 61.639
Отлично, данные по воде есть, значит и остальное тоже. На этом можно закончить…
Но нет, добавим тюнинг:
Выполняем следующие запросы в БД. Они устанавливают следующую политику хранения: часовые данные хранятся месяц, недельные — год, а месячные - бессрочно.
CREATE RETENTION POLICY "month" ON "homeassistant" DURATION 30d REPLICATION 1;
CREATE RETENTION POLICY "year" ON "homeassistant" DURATION 52w REPLICATION 1;
CREATE RETENTION POLICY "infinite" ON "homeassistant" DURATION INF REPLICATION 1;
Теперь, собственно, главная фишка — агрегация данных с помощью continuous query. Это механизм, который автоматически запускает запрос через заданные промежутки времени, агрегирует данные по этому запросу, а результат складывает в новое значение.
CREATE CONTINUOUS QUERY cq_water_hourly ON homeassistant BEGIN SELECT max(value) AS value INTO homeassistant.month.water_meter_hour FROM homeassistant.autogen.m3 GROUP BY time(1h), entity_id fill(previous) END
Эта команда:
Ночью или когда никого нет дома потребления воды нет, а соответственно новых записей в homeassistant.autogen.m3 тоже нет. Чтобы не было пропусков значений в обычных запросах можно использовать fill(previous). Это заставит InfluxDB использовать значение прошлого часа.
К сожалению, у continuous query есть особенность: трюк fill(previous) не работает и записи просто не создаются. Причем это какая-то непреодолимая проблема, которая обсуждается уже не первый год. С этой проблемой мы разберемся позже, а fill(previous) в continuous query пусть будет — оно не мешает.
посмотрим вывод:
select * from homeassistant.month.water_meter_hour group by entity_id
Обратите внимание, что значения в базе сохраняются в UTC, поэтому в этом списке отличаются на 3 часа — значения за 7 утра в выводе InfluxDB соответствуют значениям за 10 утра на графиках выше. Также обратите внимание, что между 2 и 5 утра записей просто нету — это та самая особенность continuous query.
Как видите, агрегированное значение также является монотонно возрастающей последовательностью, только записи идут реже — раз в час. Но это не проблема — мы можем написать еще один запрос, который будет добывать правильные данные для графика.
SELECT difference(max(value)) FROM homeassistant.month.water_meter_hour WHERE entity_id='cold_water_metter' and time >= now() -24h GROUP BY time(1h), entity_id fill(previous)
Эта команда:
cold_water_metter' за последние сутки (time >= now() -24h).Осталось только построить графики.
Далее будем использовать наши данные, но это уже в следующей статье.