
Z każdym razem, gdy otrzymuję rachunek za prąd i wodę, zastanawiam się – czy moja rodzina naprawdę tyle zużywa? Tak, w łazience mamy ogrzewanie podłogowe i bojler, ale przecież nie pracują non-stop. Wodę także ekonomicznie wykorzystujemy (choć lubimy się powylegiwać w wannie). Kilka lat temu już i do inteligentnego domu, ale na tym się skończyło. Do analizy zużycia zmobilizowałem się dopiero teraz, o czym zresztą jest ten artykuł.
Niedawno przeszedłem na Home Assistant jako system inteligentnego domu. Jednym z powodów była możliwość zorganizowania zbierania dużej ilości danych z możliwością wygodnego tworzenia różnych rodzajów wykresów.
Informacje opisane w tym artykule nie są nowe, wszystkie te rzeczy w różnych odsłonach były już opisane w internecie. Jednak każdy artykuł zazwyczaj opisuje tylko jedno podejście lub aspekt. Musiałem porównać wszystkie te podejścia i wybrać najbardziej odpowiednie. Artykuł i tak nie daje wyczerpujących informacji na temat zbierania danych, ale jest pewnego rodzaju konspektem tego, jak to zrobiłem. Tak więc konstruktywna krytyka i propozycje ulepszeń są mile widziane.
Sformułowanie zadania
Celem dzisiejszego ćwiczenia jest uzyskanie ładnych wykresów zużycia wody i prądu:
- Godzinowy za 2 dni
- Dzienny za 2 tygodnie
- (opcjonalnie) tygodniowy i miesięczny
W tym czekają nas pewne trudności:
- Standardowe komponenty wykresów są zazwyczaj dość ubogie. W najlepszym przypadku można stworzyć wykres liniowy na podstawie punktów.
Jeśli dobrze poszukać, można znaleźć komponenty zewnętrzne, które rozszerzają możliwości standardowego wykresu. Dla Home Assistant przyzwoity i ładny komponent to , ale on także jest nieco ograniczony:
- Trudno ustawić parametry wykresu słupkowego na większych odstępach (szerokość słupka jest ustawiana w ułamkach godziny, co oznacza, że odstępy dłuższe niż godzina będą ustalane jako liczby dziesiętne)
- Nie można na jednym wykresie dodawać różnych encji (na przykład temperatury i wilgotności, lub połączyć wykres słupkowy z liniowym)
- Nie tylko to, że home assistant domyślnie korzysta z najbardziej prymitywnej bazy danych SQLite (a ja, nieudacznik, nie poradziłem sobie z instalacją MySQL lub Postgresa), ale także dane są przechowywane w sposób niezbyt optymalny. Na przykład przy każdej zmianie nawet najdrobniejszego parametru cyfrowego do bazy zapisywany jest ogromny json o rozmiarze około kilobajta.
{"entity_id": "sensor.water_cold_hourly", "old_state": {"entity_id": "sensor.water_cold_hourly", "state": "3", "attributes": {"source": "sensor.water_meter_cold", "status": "collecting", "last_period": "29", "last_reset": "2020-02-23T21:00:00.022246+02:00", "meter_period": "hourly", "unit_of_measurement": "l", "friendly_name": "water_cold_hourly", "icon": "mdi:counter"}, "last_changed": "2020-02-23T19:05:06.897604+00:00", "last_updated": "2020-02-23T19:05:06.897604+00:00", "context": {"id": "aafc8ca305ba4e49ad4c97f0eddd8893", "parent_id": null, "user_id": null}}, "new_state": {"entity_id": "sensor.water_cold_hourly", "state": "4", "attributes": {"source": "sensor.water_meter_cold", "status": "collecting", "last_period": "29", "last_reset": "2020-02-23T21:00:00.022246+02:00", "meter_period": "hourly", "unit_of_measurement": "l", "friendly_name": "water_cold_hourly", "icon": "mdi:counter"}, "last_changed": "2020-02-23T19:11:11.251545+00:00", "last_updated": "2020-02-23T19:11:11.251545+00:00", "context": {"id": "0de64b8af6f14bb9a419dcf3b200ef56", "parent_id": null, "user_id": null}}}Mam całkiem sporo czujników (czujniki temperatury w każdym pokoju, liczniki wody i energii elektrycznej), a niektóre z nich generują dość dużo danych. Na przykład licznik energii elektrycznej SDM220 generuje około dziesięciu wartości co 10-15 sekund, a chciałbym zainstalować około 8 takich liczników. Jest też cała masa parametrów, które są obliczane na podstawie innych czujników. Tak więc te wartości mogą łatwo rozrosnąć bazę danych o 100-200 MB dziennie. Po tygodniu system będzie ledwo działał, a po miesiącu karta pamięci (przy typowej instalacji home assistant na Raspberry Pi) umrze, a już o przechowywaniu danych przez rok nie ma mowy.
- Jeśli masz szczęście, twój licznik potrafi sam liczyć zużycie. Możesz w dowolnym momencie zwrócić się do licznika i zapytać, które godziny skumulowanej wartości zużycia. Zazwyczaj wszystkie liczniki energii elektrycznej z interfejsem cyfrowym (RS232/RS485/Modbus/Zigbee) oferują tę możliwość.
Gorsze, jeśli urządzenie może jedynie mierzyć jakiś chwilowy parametr (na przykład chwilową moc lub prąd), albo po prostu generować impulsy co X watogodzin lub litrów. Wtedy trzeba pomyśleć jak i czym to zintegrować oraz gdzie gromadzić wartości. Istnieje ryzyko, że z jakiegoś powodu przegapimy kolejny raport, a dokładność systemu ogólnie budzi wątpliwości. Oczywiście można to wszystko powierzyć systemowi inteligentnego domu, jak home assistant, ale kwestia liczby zapisów w bazie danych nie znika, a pytanie o częstotliwość odpytywania czujników co więcej niż raz na sekundę też pozostaje bez odpowiedzi (ograniczenie architektury home assistant).
Podejście 1
Na początku przyjrzyjmy się, co home assistant oferuje od razu po wyjęciu z pudełka. Pomiar zużycia w okresie — to bardzo poszukiwana funkcjonalność. Oczywiście, dawno temu zrealizowano to w postaci specjalizowanego komponentu — utility_meter.
Istota komponentu polega na tym, że wewnętrznie tworzy zmienną current_accumulated_value i zeruje ją po upływie zadanego okresu (godzina/tydzień/miesiąc). Komponent sam monitoruje wejściową zmienną (wartość jakiegoś czujnika), sam zapisuje się na zmiany wartości — po prostu otrzymujesz gotowy wynik. Opisuje się to w kilku linijkach w pliku konfiguracyjnym.
utility_meter:
water_cold_hour_um:
source: sensor.water_meter_cold
cycle: hourly
water_cold_day_um:
source: sensor.water_meter_cold
cycle: daily
Tutaj sensor.water_meter_cold to bieżąca wartość licznika w litrach, którą otrzymuję przez mqtt. Konstrukcja tworzy dwa nowe czujniki water_cold_hour_um i water_cold_day_um, które gromadzą dane godzinne i dzienne, resetując je po upływie okresu. Oto wykres godzinnego akumulatora za pół dnia.

Kod dla wykresów godzinnych i dziennych w lovelace-UI wygląda tak:
- type: history-graph
title: 'Godzinne zużycie wody z wykorzystaniem zmiennych'
hours_to_show: 48
entities:
- sensor.water_hour
- type: history-graph
title: 'Dzienna zużycie wody z wykorzystaniem zmiennych'
hours_to_show: 360
entities:
- sensor.water_day
W rzeczywistości to w tym algorytmie kryją się problemy tego podejścia. Jak już wspomniałem, dla każdego wejściowego wartości (obecny odczyt licznika na każdy kolejny litr) generowana jest 1 kB wpisu w bazie. Każdy licznik zużycia również generuje nową wartość, która jest sumowana w bazie. Jeśli chcę zbierać dane godzinowe/dzienne/tygodniowe/miesięczne, na przykład dla kilku pionów wodnych, a do tego dodać kilka liczników elektrycznych — będzie to bardzo dużo danych. Dokładniej, danych nie będzie dużo, ale ponieważ home assistant zapisuje w bazie wiele niepotrzebnych informacji, rozmiar bazy będzie rósł jak na drożdżach. Obawiam się nawet szacować rozmiar bazy dla wykresów tygodniowych i miesięcznych.
Oprócz tego, sam utility meter nie rozwiązuje postawionego zadania. Wykres wartości, które podaje utility meter, to monotonicznie rosnąca funkcja, która co godzinę resetuje się do 0. Potrzebujemy zrozumiałego wykresu zużycia dla użytkownika, czyli ile litrów zostało zużytych w danym okresie. Standardowy komponent history-graph tego nie potrafi, ale może nam pomóc zewnętrzny komponent mini-graph-card.
To kod karty dla lovelace-UI:
- aggregate_func: max
entities:
- color: var(--primary-color)
entity: sensor.water_cold_hour_um
group_by: hour
hours_to_show: 48
name: "Skonsolidowane zużycie wody godzinowe według utility metera"
points_per_hour: 1
show:
graph: bar
type: 'custom:mini-graph-card'Oprócz standardowych ustawień, takich jak nazwa sensora, typ wykresu, kolor (standardowy pomarańczowy mi się nie podobał), warto zwrócić uwagę na 3 ustawienia:
- group_by:hour — wykres będzie generowany z wyrównaniem słupków do początku godziny
- points_per_hour: 1 — jeden słupek na każdą godzinę
- I najważniejsze, aggregate_func: max — branie maksymalnej wartości w ramach każdej godziny. To właśnie ten parametr przekształca ząbkowany wykres w słupki

Nie zwracajcie uwagi na kilka słupków z lewej — to standardowe zachowanie komponentu, gdy brak danych. A danych nie było — włączyłem zbieranie danych z utility metera dopiero przed chwilą tylko na potrzeby tego artykułu (o swoim obecnym podejściu opowiem trochę poniżej).
Na tym obrazku chciałem pokazać, że czasami wyświetlanie danych działa i słupki rzeczywiście odzwierciedlają właściwe wartości. Tylko że nie wszystkie. Wyróżniony słupek za okres od 11 do 12 w nocy pokazuje 19 litrów, chociaż na ząbatego wykresu nieco wyżej za ten sam okres z tego samego czujnika widzimy zużycie 62 litrów. Albo błąd, albo winę ponoszą niewłaściwe ręce. A dlaczego dane po prawej stronie się urwały, na razie nie rozumiem — zużycie tam było normą, co również widać na ząbatym wykresie.
Ogólnie rzecz biorąc, nie udało mi się osiągnąć prawdopodobieństwa tego podejścia — wykres prawie zawsze pokazuje jakieś bzdury.
Analogiczny kod dla czujnika dziennego.
- aggregate_func: max
entities:
- color: var(--primary-color)
entity: sensor.water_cold_day_um
group_by: interval
hours_to_show: 360
name: "Dzienne zużycie wody zebrane przez licznik użyteczności"
points_per_hour: 0.0416666666
show:
graph: bar
type: 'custom:mini-graph-card'
Zwróć uwagę, że parametr group_by jest ustawiony na wartość interval, i rządzi nim cały parametr points_per_hour. W tym tkwi inny problem tego komponentu — points_per_hour działa dobrze na wykresach za godzinę lub mniej, ale strasznie na większych odstępach. Żeby dostać jeden słupek za jeden dzień trzeba wpisać wartość 1/24=0.04166666. Nie mówię już o wykresach tygodniowych i miesięcznych.
Podejście 2
Zaledwie zaczynając z home assistant natknąłem się na to wideo:

Kolega zbiera dane o zużyciu z kilku rodzajów gniazdek Xiaomi. Jego zadanie jest nieco prostsze — po prostu wyświetlać wartość zużycia za dzisiaj, wczoraj i za miesiąc. Żadne wykresy nie są potrzebne.
Odstawmy na bok rozważania o ręcznym integracji chwilowych wartości mocy — o „dokładności” tego podejścia pisałem już powyżej. Niejasne jest, dlaczego nie zdecydował się wykorzystać zgromadzonych wartości zużycia, które już gromadzi to samo gniazdko. Moim zdaniem integracja w środku urządzenia będzie działać lepiej.
Z wideo weźmiemy pomysł ręcznego liczenia zużycia za okres. U tego gościa liczone są tylko wartości za dzisiaj i wczoraj, ale pójdziemy dalej i spróbujemy narysować wykres. Istota zaproponowanej metody w moim przypadku sprowadza się do następujących kwestii.
Utworzymy zmienną wartość_na_początku_godziny, w której zapiszemy bieżące odczyty licznika.
Na koniec godziny (lub na początku następnej) obliczymy różnicę między aktualnym odczytem a zapamiętaną na początku godziny. Ta różnica będzie zużyciem za bieżącą godzinę — zapisujemy tę wartość w czujniku i w przyszłości na jej podstawie będziemy budować wykres.
Należy również „zerować” zmienną wartość_w_początku_godziny, zapisując tam aktualną wartość licznika.
Wszystko to można zrobić za pomocą środków samego home assistant.
Kod będzie trzeba napisać nieco więcej niż w poprzednim podejściu. Na początku utworzymy te „zmienne”. Z pudełka nie mamy encji „zmienna”, ale możemy skorzystać z brokera mqtt. Będziemy tam wysyłać wartości z flagą retain=true — to zapisze wartość w brokerze i w dowolnym momencie można ją stamtąd wyciągnąć, nawet po restarcie home assistant. Zrobiłem od razu liczniki godzinowe i dzienne.
- platform: mqtt
state_topic: "test/water/hour"
name: water_hour
unit_of_measurement: l
- platform: mqtt
state_topic: "test/water/hour_begin"
name: water_hour_begin
unit_of_measurement: l
- platform: mqtt
state_topic: "test/water/day"
name: water_day
unit_of_measurement: l
- platform: mqtt
state_topic: "test/water/day_begin"
name: water_day_begin
unit_of_measurement: lCała magia dzieje się w automatyzacji, która uruchamia się co godzinę i każdej nocy.
- id: water_new_hour
alias: water_new_hour
initial_state: true
trigger:
- platform: time_pattern
minutes: 0
action:
- service: mqtt.publish
data:
topic: "test/water/hour"
payload_template: >
{{ (states.sensor.water_meter_cold.state|int) - (states.sensor.water_hour_begin.state|int) }}
retain: true
- service: mqtt.publish
data:
topic: "test/water/hour_begin"
payload_template: >
{{ states.sensor.water_meter_cold.state }}
retain: true
- id: water_new_day
alias: water_new_day
initial_state: true
trigger:
- platform: time
at: "00:00:00"
action:
- service: mqtt.publish
data:
topic: "test/water/day"
payload_template: >
{{ (states.sensor.water_meter_cold.state|int) - (states.sensor.water_day_begin.state|int) }}
retain: true
- service: mqtt.publish
data:
topic: "test/water/day_begin"
payload_template: >
{{ states.sensor.water_meter_cold.state }}
retain: trueObie automatyzacje wykonują 2 działania:
- Obliczają wartość za interwał jako różnicę między wartością początkową a końcową
- Aktualizują podstawową wartość dla następnego interwału
Budowę wykresów w tym przypadku rozwiązuje zwykły history-graph:
- type: history-graph
title: 'Godzinne zużycie wody z wykorzystaniem zmiennych'
hours_to_show: 48
entities:
- sensor.water_hour
- type: history-graph
title: 'Dzienna zużycie wody z wykorzystaniem zmiennych'
hours_to_show: 360
entities:
- sensor.water_dayWygląda to tak:

W zasadzie to już to, czego potrzebujemy. Zaletą tej metody jest to, że dane są generowane raz za interwał. Tzn. łącznie 24 zapisy na dobę dla wykresu godzinowego.
Niestety, ogólny problem rosnącej bazy danych pozostaje nierozwiązany. Jeśli będę chciał uzyskać wykres miesięcznego zużycia, będę musiał przechowywać dane co najmniej przez rok. A ponieważ home assistant oferuje tylko jedną opcję długości przechowywania dla całej bazy, oznacza to, że WSZYSTKIE dane w systemie muszą być przechowywane przez cały rok. Na przykład w ciągu roku zużywam 200 m³ wody, co oznacza 200000 zapisów w bazie. A jeśli uwzględnić inne czujniki, to liczba ta staje się wręcz nieprzyzwoita.
Podejście 3
Na szczęście, mądrzy ludzie już rozwiązali ten problem, tworząc bazę danych InfluxDB. Ta baza jest w specjalny sposób zoptymalizowana pod kątem przechowywania danych czasowych i idealnie nadaje się do przechowywania wartości różnych czujników. System oferuje również język zapytań podobny do SQL, który pozwala na wydobywanie wartości z bazy, a następnie ich agregowanie na różne sposoby. Wreszcie, różne dane można przechowywać przez różny czas. Na przykład często zmieniające się pomiary, takie jak temperatura czy wilgotność, można przechowywać tylko przez kilka tygodni, podczas gdy dzienne pomiary zużycia wody można przechowywać przez cały rok.
Oprócz InfluxDB, mądrzy ludzie wynaleźli również Grafana – system do tworzenia wykresów na podstawie danych z InfluxDB. Grafana potrafi rysować różne rodzaje wykresów, szczegółowo je dostosowywać, a co najważniejsze, te wykresy można „wstawić” do UI lovelace home assistant.
Inspiracje i . W artykułach szczegółowo opisano proces instalacji i podłączenia InfluxDB oraz Grafany do home assistant. Ja skupię się na rozwiązaniu mojego konkretnego problemu.
Zatem pierwszym krokiem jest rozpoczęcie zapisywania wartości licznika do InfluxDB. Fragment konfiguracji home assistant (w tym przykładzie będę bawić się nie tylko zimną, ale również gorącą wodą):
influxdb:
host: localhost
max_retries: 3
default_measurement: state
database: homeassistant
include:
entities:
- sensor.water_meter_hot
- sensor.water_meter_coldWyłączymy zapisanie tych samych danych do wewnętrznej bazy home assistant, aby nie powiększać jej niepotrzebnie:
recorder:
purge_keep_days: 10
purge_interval: 1
exclude:
entities:
- sensor.water_meter_hot
- sensor.water_meter_coldPrzejdźmy teraz do konsoli InfluxDB i skonfigurujmy naszą bazę. W szczególności musimy ustawić, jak długo będą przechowywane różne dane. Reguluje to tzw. polityka retencji - jest to podobne do baz danych wewnątrz głównej bazy danych, przy czym każda wewnętrzna baza ma swoje ustawienia. Domyślnie wszystkie dane trafiają do polityki retencji o nazwie autogen, te dane będą przechowywane przez tydzień. Chciałbym, aby dane godzinowe były przechowywane przez miesiąc, tygodniowe - przez rok, a miesięczne w ogóle nie były usuwane. Stwórzmy odpowiednie polityki retencji.
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 1Teraz, przechodząc do głównego triku - agregacja danych za pomocą zapytania ciągłego. Jest to mechanizm, który automatycznie uruchamia zapytanie w określonych odstępach czasu, agreguje dane na podstawie tego zapytania, a wynik zapisuje jako nową wartość. Rozbijmy to na przykład (piszę w kolumnach dla lepszej czytelności, ale w rzeczywistości musiałem wprowadzić tę komendę w jednej linii)
CREATE CONTINUOUS QUERY cq_water_hourly ON homeassistant
BEGIN
SELECT max(value) AS value
INTO homeassistant.month.water_meter_hour
FROM homeassistant.autogen.l
GROUP BY time(1h), entity_id fill(previous)
ENDTa komenda:
- Tworzy zapytanie ciągłe o nazwie cq_water_cold_hourly w bazie homeassistant
- Zapytanie będzie wykonywane co godzinę (time(1h))
- Zapytanie będzie pobierać wszystkie dane z miary homeassistant.autogen.l (litry), w tym odczyty zimnej i ciepłej wody
- Agregowane dane będą grupowane według entity_id, co utworzy osobne wartości dla zimnej i ciepłej wody
- Ponieważ licznik litrów to monotonicznie rosnąca sekwencja, w ramach każdej godziny należy wziąć maksymalną wartość, dlatego agregacja będzie przeprowadzana funkcją max(value)
- Nowa wartość zostanie zapisana w homeassistant.month.water_meter_hour, gdzie month to nazwa polityki retencji z okresem przechowywania wynoszącym miesiąc. Przy czym dane dotyczące zimnej i ciepłej wody będą rozdzielone na osobne rekordy z odpowiednim entity_id i wartością w polu value.
W nocy lub kiedy nikogo nie ma w domu, zużycie wody nie występuje, a tym samym nowych rekordów w homeassistant.autogen.l również nie ma. Aby uniknąć braków wartości w zwykłych zapytaniach, można użyć fill(previous). To sprawi, że InfluxDB użyje wartości z poprzedniej godziny.
Niestety, zapytanie ciągłe ma swoją specyfikę: sztuczka fill(previous) nie działa i zapisy po prostu nie są tworzone. To jest pewien nieprzezwyciężony problem, który Z tym problemem poradzimy sobie później, a fill(previous) w zapytaniu ciągłym niech będzie – nie przeszkadza.
Sprawdźmy, co udało się uzyskać (oczywiście trzeba poczekać kilka godzin):
> select * from homeassistant.month.water_meter_hour group by entity_id
...
name: water_meter_hour
tags: entity_id=water_meter_cold
time value
---- -----
...
2020-03-08T01:00:00Z 370511
2020-03-08T02:00:00Z 370513
2020-03-08T05:00:00Z 370527
2020-03-08T06:00:00Z 370605
2020-03-08T07:00:00Z 370635
2020-03-08T08:00:00Z 370699
2020-03-08T09:00:00Z 370761
2020-03-08T10:00:00Z 370767
2020-03-08T11:00:00Z 370810
2020-03-08T12:00:00Z 370818
2020-03-08T13:00:00Z 370827
2020-03-08T14:00:00Z 370849
2020-03-08T15:00:00Z 370921
Zauważ, że wartości w bazie są przechowywane w UTC, więc w tej liście różnią się o 3 godziny — wartości z 7 rano w wynikach InfluxDB odpowiadają wartościom z 10 rano na powyższych wykresach. Zauważ także, że między 2 a 5 rano zapisy po prostu nie istnieją — to jest ta specyfika zapytania ciągłego.
Jak widać, agregowana wartość również jest monotonicznie rosnącą sekwencją, tylko zapisy są rzadsze — raz na godzinę. Ale to nie problem — możemy napisać jeszcze jedno zapytanie, które wyciągnie odpowiednie dane do wykresu.
SELECT difference(max(value))
FROM homeassistant.month.water_meter_hour
WHERE entity_id='water_meter_cold' and time >= now() -24h
GROUP BY time(1h), entity_id
fill(previous)Rozjaśnię:
- Z bazy homeassistant.month.water_meter_hour wyciągniemy dane dla entity_id=’water_meter_cold’ za ostatnie 24 godziny (time >= now() -24h).
- Jak już wspomniałem w sekwencji homeassistant.month.water_meter_hour mogą brakować pewnych zapisów. Te dane wygenerujemy ponownie, uruchamiając zapytanie z GROUP BY time(1h). Tym razem fill(previous) zadziała jak należy, generując brakujące dane (funkcja weźmie poprzednią wartość).
- Najważniejsze w tym zapytaniu jest funkcja difference, która obliczy różnicę między znacznikami czasowymi. Sama w sobie nie działa i wymaga funkcji agregującej. Niech to będzie max() używana wcześniej.
Wynik wykonania wygląda tak:
name: water_meter_hour
tags: entity_id=water_meter_cold
time difference
---- ----------
...
2020-03-08T02:00:00Z 2
2020-03-08T03:00:00Z 0
2020-03-08T04:00:00Z 0
2020-03-08T05:00:00Z 14
2020-03-08T06:00:00Z 78
2020-03-08T07:00:00Z 30
2020-03-08T08:00:00Z 64
2020-03-08T09:00:00Z 62
2020-03-08T10:00:00Z 6
2020-03-08T11:00:00Z 43
2020-03-08T12:00:00Z 8
2020-03-08T13:00:00Z 9
2020-03-08T14:00:00Z 22
2020-03-08T15:00:00Z 72Od 2 do 5 rano (UTC) zużycia nie było. Niemniej jednak zapytanie zwróci tę samą wartość zużycia dzięki fill(previous), a funkcja difference odejmie tę wartość sama od siebie, co da w rezultacie 0, co jest dokładnie tym, co jest wymagane.
Pozostało tylko jedno — zbudować wykres. W tym celu otworzymy Grafana, otworzymy istniejący (lub stworzymy nowy) dashboard, stworzymy nową planszę. Ustawienia wykresów będą następujące.

Będę wyświetlać dane dotyczące zimnej i ciepłej wody na jednym wykresie. Zapytanie jest dokładnie takie samo jak opisałem powyżej.
Parametry wyświetlania są określane w ten sposób. U mnie będzie to wykres liniowy (lines), który idzie ze schodkami (stairs). Parametr Stack wyjaśnię nieco poniżej. Poniżej jeszcze kilka parametrów wyświetlania, ale nie są tak interesujące.

Aby dodać otrzymany wykres do home assistant, należy:
- wyjść z trybu edycji wykresu. Dlaczegoś poprawne ustawienia udostępniania wykresów są proponowane tylko ze strony dashboardu.
- Kliknąć na trójkąt obok nazwy wykresu, w menu wybrać share.
- W otwartym oknie przejść na zakładkę embed.
- Odznaczyć opcję current time range — zakres czasowy będziemy określać przez URL.
- Wybrać potrzebny motyw. W moim przypadku to light.
- Skopiować otrzymany URL do karty ustawień lovelace-UI.
- type: iframe
id: graf_water_hourly
url: "http://192.168.10.200:3000/d-solo/rZARemQWk/water?orgId=1&panelId=2&from=now-2d&to=now&theme=light"
Zwróć uwagę, że zakres czasowy (ostatnie 2 dni) jest określany właśnie tutaj, a nie w ustawieniach dashboardu.
Wykres wygląda tak. Ciepłej wody nie używałem przez ostatnie 2 dni, dlatego rysowany jest tylko wykres zimnej wody.

Nie zdecydowałem, który wykres bardziej mi się podoba, czy schodkowy, czy rzeczywiste słupki. Dlatego po prostu podam przykład dziennego wykresu zużycia, tym razem tylko w formie słupków. Zapytania są budowane analogicznie do opisanych powyżej. Parametry wyświetlania są następujące:

Wykres wygląda tak:

Tak więc o parametrze Stack. W tym wykresie słupek zimnej wody jest rysowany na wierzchu słupka ciepłej. Całkowita wysokość odpowiada całkowemu zużyciu zimnej i ciepłej wody w danym okresie.
Wszystkie przedstawione wykresy są dynamiczne. Można najechać myszką na interesujący punkt i zobaczyć szczegóły i wartość w konkretnej lokalizacji.
Niestety, nie obyło się bez paru łyżek dziegciu. Na wykresie słupkowym (w przeciwieństwie do wykresu liniowego) środek słupka nie znajduje się w środku doby, lecz o 00:00. To znaczy, że lewa połowa słupka jest rysowana na miejscu poprzedniego dnia. Tak więc wykresy za sobotę i niedzielę są nieco przesunięte w lewo od niebieskiej strefy. Na razie nie wymyśliłem, jak to naprawić.
Inny problem polega na niemożności prawidłowego pracy z przedziałami miesięcznymi. Chodzi o to, że długość godziny/dnia/tygodnia jest stała, a długość miesiąca za każdym razem jest różna. InfluxDB może pracować tylko z równymi przedziałami. Na razie wystarczyło mi pomyśleć o stałym przedziale 30 dni. Tak, wykres w ciągu roku będzie trochę się rozjeżdżał, a słupki nie do końca będą odpowiadać miesiącom. Ale ponieważ to dla mnie ciekawe jako wskaźnik, to mi to odpowiada.
Widzę co najmniej dwa rozwiązania:
- Zrezygnować z miesięcznych wykresów i skupić się na tygodniowych. 52 tygodniowe słupki w roku wyglądają całkiem nieźle.
- Samo miesięczne zużycie liczyć jako sposób №2, a Grafana używać tylko do ładnych wykresów. To będzie całkiem dokładne rozwiązanie. Można nawet nałożyć wykresy z zeszłego roku dla porównania — Grafana także to potrafi.
Podsumowanie
Nie wiem dlaczego, ale uwielbiam tego rodzaju wykresy. Pokazują, że życie tętni i wszystko się zmienia. Wczoraj było dużo, dziś mało, a jutro będzie jakoś jeszcze. Zostało tylko popracować z domownikami na temat zużycia. Ale nawet przy obecnych apetytach, duża i niezrozumiała liczba na rachunku już staje się dość zrozumiałym obrazem zużycia.
Mimo prawie 20-letniej kariery programisty, prawie nie miałem styczności z bazami danych. Dlatego instalacja zewnętrznej bazy danych wydawała się czymś tak trudnym i niezrozumiałym. Wszystko zmieniła – okazało się, że podłączenie odpowiedniego narzędzia można zrobić w kilka kliknięć, a z wyspecjalizowanym narzędziem zadanie budowy wykresów staje się trochę łatwiejsze.
W tytule wspomniałem o zużyciu energii elektrycznej. Niestety w tej chwili nie mogę przedstawić żadnych wykresów. Jeden licznik SDM120 mi padł, a drugi ma problemy z komunikacją przez Modbus. Niemniej jednak, nie wpływa to w żaden sposób na temat tego artykułu – wykresy będą budowane w ten sam sposób, co dla wody.
W tym artykule przedstawiłem podejścia, które sam wypróbowałem. Z pewnością istnieją jeszcze inne sposoby organizacji zbierania i wizualizacji danych, o których nie wiem. Podzielcie się nimi w komentarzach, bardzo mnie to zainteresuje. Będę wdzięczny za konstruktywną krytykę i nowe pomysły. Mam nadzieję, że przedstawiony materiał również komuś pomoże.
Źródło: habr.com
