Templating - Biorytm - problem z konwersją typów

Dzień dobry,
Kontynuuję ten wątek z postu, który zainicjowałem w dziale Node-RED, ale teraz dotyczy on wyłącznie templatingu HA (Node-RED nie dał rady…)…

Utknąłem bowiem na próbie konwersji daty na czysty integer…
Buduję następującą templatkę:

{% set today = now() %}
{% set birthday = state_attr('sensor.anniversary_urodziny_aga', 'date') %}
{% set days = (today - birthday) %}
{{today}}
{{birthday}}

{{days}}

Oto jej wynik:

2023-08-19 11:59:00.140739+02:00
1974-12-23 00:00:00+01:00

17771 days, 11:59:00.140739

…i wszystko jest OK.

Teraz muszę przeprowadzić matetmatyczne obliczenia wykorzystując zmienną days jako parametr numeryczny w funkcji sin(x), np. sin (17771) = 0.8358291413129607.

Niestety, kod: {{ sin (days) }} wywala następujący błąd:
ValueError: Template error: sin got invalid input '17771 days, 11:59:00.140739' when rendering template.

Próbowałem wielu różnych funkcji i metod konwersji obiektu 17771 days, 11:59:00.140739 na czysty integer 17771 , ale żadna z nich nie zwraca oczekiwanego wyniku.

Proszę, czy możecie mi pomóc?

Chyba Ty? …; :stuck_out_tongue_winking_eye:

Musisz obciąć string do pierwszej spacji, ale tu ja nie dam rady.

Już to ćwiczyłem na różne sposoby. Nawet zwykły {{ days[a:b] }} nie działa, bo days nie jest stringiem, lecz raczej datą (choć na datę nie wygląda)

{{ (days[1:6]) }}

wywala błąd: TypeError: 'datetime.timedelta' object is not subscriptable.

Dlatego nie robię nic w HA dla mnie to są czary :wink:

Normalnie w każdym języku programowania podobne problemy rozwiązuje się operując na znaczniku czasu (timestamp - TS).
Dwie daty konwertujesz na znacznik, który jest unikatową liczbą.
Wszystkie operacje robisz jak na zwykłych liczbach.
W przypadku biorytmu:
Delta = TSdzis - TSurodz - wyrażone w sek lub msek.
Dni = Delta / ilośćjednostekczasunadobę
Faza = sin(Dni)

Spróbuj tak

{% set today = now() %}
{% set birthday = as_datetime('1974-12-23 00:00:00+01:00') %}
{% set days = (today - birthday).days %}
{% set seconds = days * 24 * 60 * 60 %}
{% set sin_result = seconds | sin %}
{{ sin_result }}

{% set today = now() %}
{% set birthday = state_attr('sensor.anniversary_urodziny_aga', 'date') %}
{% set days = (today - birthday).days %}
{% set seconds = days * 24 * 60 * 60 %}
{% set sin_result = seconds | sin %}
{{ sin_result }}

Nie wim czy pomogę, bo nie jestem programistą ale idąc za dokumentacją:

sensor:
  # Minimal configuration of the standard time and date sensor
  - platform: time_date
    display_options:
      - 'date_time_iso'
  # Build on the standard sensor to produce one that can be customized
template:
  - sensor:
      - name: "Date and time"
        state: "{{ as_timestamp(states('sensor.date_time_iso')) | timestamp_custom('%A %B %-d, %I:%M %p') }}"
        icon: "mdi:calendar-clock"

Ta notacja {% set days = (today - birthday).days %} bardzo pomogła!
Teraz days jest liczbą, a przecież o to właśnie chodziło.
Bardzo dziękuję za ten pomysł :heart: … i jadę dalej z pełnym algorytmem.

EDIT:
Temat pięknie ogarnięty. Ostatecznie templatka wygląda tak (sensor przechowuje w swoich atrybutach dane biorytmiczne dla każdego z czterech cykli):

sensor:
  - name: Biorytm Aga
    state: "{{ (state_attr('sensor.anniversary_urodziny_aga', 'date')) }}"
    attributes:
      Biorytm fizyczny: >
        {% set today = now() %}
        {% set birthday = (state_attr('sensor.anniversary_urodziny_aga', 'date')) %}
        {% set days = (today - birthday).days %}
        {% set cycle_days = 23 %}
        {% set fiz = sin ( 2 * pi * days / cycle_days) %}
        {{ fiz | float () }}
      Biorytm emocjonalny: >
        {% set today = now() %}
        {% set birthday = (state_attr('sensor.anniversary_urodziny_aga', 'date')) %}
        {% set days = (today - birthday).days %}
        {% set cycle_days = 28 %}
        {% set emo = sin ( 2 * pi * days / cycle_days) %}
        {{ emo | float() }}
      Biorytm intelektualny: >
        {% set today = now() %}
        {% set birthday = (state_attr('sensor.anniversary_urodziny_aga', 'date')) %}
        {% set days = (today - birthday).days %}
        {% set cycle_days = 33 %}
        {% set inl = sin ( 2 * pi * days / cycle_days) %}
        {{ inl | float () }}
      Biorytm intuicyjny: >
        {% set today = now() %}
        {% set birthday = (state_attr('sensor.anniversary_urodziny_aga', 'date')) %}
        {% set days = (today - birthday).days %}
        {% set cycle_days = 38 %}
        {% set inc = sin ( 2 * pi * days / cycle_days) %}
        {{ inc | float () }}

Wykres biorytmu właśnie zaczął się tworzyć od dnia dzisiejszego, teraz musi minąć klika dni zanim na wykresie pojawią się wartości historyczne.

Chciałbym teraz pójść o krok dalej… Skoro sin(x) jest funkcją okresową, to jej wartość jest znana dla każdej daty z przeszłości i przyszłości. Fajnie byłoby widzieć taki wykres:

obraz

Na razie nie mam pomysłu jak zbudować wykres dla przedziału dat [dzisiaj-15dni, dzisiaj+15dni] na karcie custom:mini-graph-card, bo sama karta korzysta tylko z bieżących wartości sensorów z templatki… a nie widzę sensu stworzenia 30-tu nowych, osobnych sensorów (dla każdej daty z przedziału) dla każdego cyklu, skoro wszystkie wartości oblicza ta sama funkcja okresowa, której jedynym parametrem zmiennym jest data urodzin, czyli ilość przeżytych dni…

1 polubienie

Dla zainteresowanych - biorytm można obliczyć na stronie http://biorytm.net

Breaking News:

Mam wrażenie, że wpadłem na pomysł jak stworzyć wykres biorytmu dla przedziału [dziś-15, dziś+15].
Prawdopodobnie wystarczy:

  1. przedefiniować templatkę sensora, dodając zmienną zakres w następujący sposób:
sensor:
  - name: Biorytm Aga
    state: "{{ (state_attr('sensor.anniversary_urodziny_aga', 'date')) }}"
    attributes:
      Biorytm fizyczny: >
        {% set today = now() %}
        {% set birthday = (state_attr('sensor.anniversary_urodziny_aga', 'date')) %}
        {% set zakres = states('input_number.biorytmy_zakres').state %}  #// szerokość przedziału wyrażona liczbą dni
        {% set days = (today - birthday).days + zakres / 2 | int() %}
        {% set cycle_days = 23 %}
        {% set fiz = sin ( 2 * pi * days / cycle_days) %}
        {{ fiz | float () }}
        ...

  1. w kodzie karty custom:mini-graph-card wystarczy - o ile to się uda - odpowiednio zdefiniować parametr show_hours: ustawiając go na wartość zmiennej z numerycznego sensora pomocniczego (states['input_number.biorytmy_zakres'].state * 24), (np. 30, co daje 30 * 24 = 720 godzin).

Wartość parametru zakres (np. 30) w templatce powinna spowodować ‘wyprzedzenie w czasie’ wartości na wykresie o połowę tej wartości (np. dla 30 mamy: 30 / 2 = 15 dni). Zatem w rzeczywistości ilość dni days jest dodatkowo zwiększana o połowę szerokości przedziału, aby w dniu bieżącym przebieg wykresu zaczynał się od dnia dziś+15.
Dzięki temu, (niestety dopiero za miesiąc) powinienem otrzymać upragniony wykres dla zakresu dat:
(-15dni <= dziś => +15dni).

Mam nadzieję, że intuicja mnie nie zawiedzie… Zobaczymy!

Z ostatniej chwili…

Właśnie rozkminiłem, jak sparametryzować wartość opcji show_hours w karcie custom:mini-graph-card. Należy w tym celu posłużyć się nadrzędną kartą custom:config-template-card, która umożliwia zamianę zakodowanej na sztywno wartości parametru w karcie podrzędnej na wartość dowolnej encji. Wystarczy jedynie zdefiniować zmienną w definicji karty nadrzędnej custom:config-template-card i odwołać się do niej jako wartości parametru show_hours w karcie podrzędnej custom:mini-graph-card

Fragment kodu karty wyglądałby tak:

type: custom:config-template-card
variables:
  biorytmy_zakres: 24 * states['input_number.biorytmy_zakres'].state 
  # w celu zachowania wymaganej jednostki (h) mnożę zakres wyrażony w dniach przez ilość godzin w dniu (24)
entities:
  - input_number.biorytmy_zakres

card:
    type: custom:mini-graph-card
    name: Biorytm Aga
    ...
    hours_to_show: '${biorytmy_zakres}'

Kartą custom:config-template-card można zatem posługiwać się np. wszędzie tam, gdzie chcemy w jakikolwiek sposób ‘uzmienniać’ wartości parametrów innych kart.

Pomysł do wykorzystania! :slight_smile: