Documenting your Smart Home

I did some searching, but it’s quite hard to search for “document” and not mean HA documentation :smiley:

I was wondering whether anyone has any guidelines/templates/tips on how they document their smart home setups? I’m only just getting started, and I think working on a good documentation from the start will help me in the long run. I’m thinking that the following things would be important to document:

  • What devices I have
  • What input/output sources I have
  • What scenes/automations do I have?

Etc.

1 Like

If the information exists in HA then I try not to document it. I have some post-it notes that contains templates for writing automations and sensors that I have collected off the forum. I attach the URL to the template if I want to go back to the discussion. I also keep a document with all the important API keys, z-wave keys, watchman etc so I can rebuild if necessary. I write automations as files (not through UI) and when I have a “dead” automation, I always save the automation so I can go backwards if desired. I store electronic copies of any manuals on devices I own. All of this takes up about 600mb.

Below is my post-it-notes.

trigger:
  - platform: state
    entity_id: switch.half_bath_light
    from: 'off'
    to: 'on'

------------------------------------------------------------------------------
  - condition: state
    entity_id: light.study_cans_current_value
    state: 'off'

------------------------------------------------------------------------------

action:
  - service: switch.turn_off
    target:
      entity_id:
      - switch.s_outside_lights
      - switch.outside_back_patio_light

------------------------------------------------------------------------------

alias: Estufa - 20h On, 4 off
description: ''
trigger:
  - id: 'off'
    platform: time
    at:
      - '12:00:00'
      - '20:00:00'
      - '04:00:00'
  - id: 'on'
    platform: time
    at:
      - '16:00:00'
      - '00:00:00'
      - '08:00:00'
condition: []
action:
  - service: 'switch.turn_{{ trigger.id }}'
    target:
      entity_id: switch.0x847127fffeae874f
mode: single

---------------------------------------------------------------------------------------------------


So if you have a light, and it’s entity_id is light.living_room, access any of the properties outside the attributes (or state), would be:

{{ states.light.living_room.entity_id }} # for the entity_id 'light.living_room'
{{ states.light.living_room.domain }} # for the domain 'light'
{{ states.light.living_room.object_id }} # for the object_id 'living_room'
{{ states.light.living_room.name }} # for the friendly_name 'Living Room'
{{ states.light.living_room.last_updated }} # for the datetime object in UTC when it was last updated
{{ states.light.living_room.last_changed }} # for the datetime object in UTC when the state last changed

---------------------------------------------------------------------------------------------------

alias: Example Sunrise Sunset Light
description: ''
trigger:
  - id: '100'
    platform: sun
    event: sunset
    offset: '-01:00:00'
  - id: '30'
    platform: time
    at: '23:30:00'
  - id: '0'
    platform: sun
    event: sunrise
    offset: '-02:00:00'
condition: []
action:
  - service: light.turn_on
    target:
      entity_id: light.outdoor_front
    data:
      brightness_pct: '{{ trigger.id }}'
mode: single

---------------------------------------------------------------------------------------------------

alias: activate light for 930pm till 10pm
description: ''
trigger:
- id: 'on'
  platform: time
  at: '21:30:00'
- id: 'off'
  platform: time
  at: '22:00:00'
condition: []
action:
- choose:
  - conditions:
    - "{{ trigger.id == 'on' }}"
    - "{{ is_state('input_boolean.garage_light', 'on') }}"
    sequence:
    - service: switch.turn_on
      target:
        entity_id: switch.m_garage_light
  - conditions: "{{ trigger.id == 'off' }}"
    sequence:
    - service: switch.turn_off
      target:
        entity_id: switch.m_garage_light
  default: []
mode: single

---------------------------------------------------------------------------------------------------

trigger:
  platform: template
  value_template: "{{ states('input_datetime.test_last_timestamp')|as_datetime + timedelta(days = 10) > now() }}"

---------------------------------------------------------------------------------------------------

# chopping off first and last character

{% set ev = "'66.5'" %}
{{ ev | int }} # will = 0
{{ ev')[1:-1] | int }}  # will = 66
---------------------------------------------------------------------------------------------------

- id: '1586455192037'
  alias: School - Robin Wake Music
  description: ''
  trigger:
  - platform: time
    at: '05:55:00'
  condition:
  - "{{ is_state('calendar.noschool', 'off') }}"
  - "{{ is_state('binary_sensor.workday_sensor', 'on') }}"
  - "{{ 1 <= now().isoweekday() <= 5 }}"
  action:
  - variables:
      entities:
        - switch.echo_spare_room
        - switch.robin_tree_1
        - light.robin
        - light.robin_closet
        - light.robin_chandelier
      songs:
        - 'Cover me in Sunshine'
        - 'See it, say it, sign it by Jack Hartman'
        - 'overweight hedgehog'
        - 'where are you Christmas'
        - '90 days'
        - 'hurt by johnny cash'
        - 'Fishy on Me'
        - 'someone you loved'
        - 'frantic by Metallica'
        - 'Enter sandman'
        - 'We built this city'
        - 'Welcome to the Jungle'
  - service: homeassistant.turn_on
    target:
      entity_id: '{{ entities }}'
  - repeat:
      until: "{{ now().hour == 6 and now().minute == 30 }}"
      sequence:
      - service: notify.alexa_media
        data:
          data:
            type: announce
          message: "It is {{ now().strftime('%H:%M')}}, time To Wake up"
          target:
          - media_player.robin_echo
      - service: media_player.play_media
        target:
          entity_id: media_player.robin_echo
        data:
          media_content_type: AMAZON_MUSIC
          media_content_id: '{{ songs | random }}'
      - wait_for_trigger:
        - platform: state
          entity_id: media_player.robin_echo
          from: 'playing'
  mode: single

https://community.home-assistant.io/t/how-to-trigger-automation-if-another-automations-actions-ran-to-many-times-in-a-given-time-frame/384689/9

---------------------------------------------------------------------------------------------------

{% set month = now().month + 6 %}
{{ month }}
{{ month in [1,12] }} FALSE
{{ (now() + timedelta(days=150)).month }}
{{ (now() + timedelta(days=150)).month in [5,6,7,8,9] }} TRUE (in january)

---------------------------------------------------------------------------------------------------
{% set date_string = "[“Jan 23 2022”]" %}
{{ date_string }}
{{ as_timestamp(strptime(date_string[2:-2], "%b %d %Y")) | timestamp_local}}
---------------------------------------------------------------------------------------------------

Alternative to calculating duration
Up {{ states("sensor.ha_uptime") | as_datetime | relative_time }}

vs this

{% set boot = as_timestamp(states('sensor.ha_uptime'))|int %}
{% set duration = as_timestamp(utcnow())|int - boot %}
{{ timedelta(seconds=duration) }}

---------------------------------------------------------------------------------------------------
https://community.home-assistant.io/t/updating-templates-with-the-new-default-values-in-2021-10-x/346198#2-arguments

Using them as a function.
2 arguments
breakdown

{{ function(value_for_argument1, value_for_argument2) }}

examples

# returns acos of 1, if fails returns 0
{{ acos(1, 0) }}

# returns as_timestamp of 1, if fails returns 0
{{ as_timestamp(1, 0) }}

# returns asin of 1, if fails returns 0
{{ asin(1, 0) }}

# returns atan of 1, if fails returns 0
{{ atan(1, 0) }}

# returns cos of 1, if fails returns 0
{{ cos(1, 0) }}

# returns 1.0, if fails returns 0
{{ float('1', 0) }}

# returns sin of 1, if fails returns 0
{{ sin(1, 0) }}

# returns sqrt of 1, if fails returns 0
{{ sqrt(1, 0) }}

# returns tan of 1, if fails returns 0
{{ tan(1, 0) }}

===========================================================
3 arguments
breakdown

{{ function(value_for_argument1, value_for_argument2, value_for_argument3) }}

examples

# returns time @ 10 am, if fails returns 0
{{ strptime("10:00", "%H:%M", 0) }}

# returns log of 1 with base 10, if fails returns 0
{{ log(1, 10, 0) }}

# returns atan2 of 1 at 2, if fails returns 0
{{ atan2(1, 2, 0) }}

# returns rounds 1.23 down precision of 0, if fails returns 0
{{ round(1.43, "floor", 0) }}

===========================================================
3 arguments but omitting the 2nd argument
breakdown

{{ function(value_for_argument1, argument3 = value_for_argument3) }}

examples

# returns log of 1 with base e because base (2nd argument) defaults to e, if fails returns 0
{{ log(1, default = 0) }}

# returns rounds 1.43 to even with a precision of 0 
# because function (2nd argument) defaults to round-to-even, if fails returns 0
{{ round(1.43, default = 0) }}

===========================================================
Using them as filters

2 arguments
breakdown

{{ value_for_argument1 | function(value_for_argument2) }}

examples

# returns as_timestamp of 1, if fails returns 0
{{ 1 | as_timestamp(0) }}

# returns 1.0, if fails returns 0
{{ '1' | float(0) }}

# returns fully formatted time string 1 second past 
# midnight January 1st 1970, if fails returns 0
{{ 1 | timestamp_local(0) }}

# returns fully formatted time string 1 second past 
# midnight January 1st 1970, if fails returns 0
{{ 1 | timestamp_utc(0) }}
===========================================================

3 arguments
breakdown

{{ value_for_argument1 | function(value_for_argument2, value_for_argument3) }}

examples

# returns time @ 10 am, if fails returns 0
{{ "10:00" | strptime("%H:%M", 0) }}

# returns rounds 1.23 down precision of 0, if fails returns 0
{{ 1.43 | round("floor", 0) }}

===========================================================

3 arguments but omitting the 2nd argument
breakdown

{{ value_for_argument1 | function(argument3 = value_for_argument3) }}

examples

# returns rounds 1.23 down precision of 0, if fails returns 0
{{ 1.43 | round(default=0) }}

===========================================================

4 arguments
breakdown

{{ value_for_argument1 | function(value_for_argument2, value_for_argument3, value_for_argument4) }}

examples

# returns "00:00:01", which is 1 second past 
# midnight January 1st 1970 in local time, if fails returns 0
{{ 1 | timestamp_custom("%H:%M:%S", True, 0) }}

===========================================================
4 arguments omitting the 3rd argument
breakdown

{{ value_for_argument1 | function(value_for_argument2, value_for_argument3, value_for_argument4) }}

examples

# returns "00:00:01", which is 1 second past 
# midnight January 1st 1970 in local time (local_time defaults to True), if fails returns 0
{{ 1 | timestamp_custom("%H:%M:%S", default = 0) }}

---------------------------------------------------------------------------------------------------

Virtual Power Sensors based on on-off/% brightness

https://community.home-assistant.io/t/powercalc-virtual-power-sensors/318515

---------------------------------------------------------------------------------------------------
https://community.home-assistant.io/t/only-allow-automation-to-trigger-once-a-day/390097

  - condition: template
    # Only run if more than 6 hours (21,600 sec) since it last ran
    value_template: '{{(as_timestamp(now()) - as_timestamp(state_attr("automation.<automation ID>", "last_triggered") | default(0)) | int > 21600 )}}'

---------------------------------------------------------------------------------------------------

{% set directions = [ 'N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW', 'N'] -%}
{{ directions[(states('sensor.dwd_wind_bearing') | int(0) / 22.5) | round(0) | int] }}

---------------------------------------------------------------------------------------------------

Could somebody help me trying to understand how could I extract information from curl query bellow.
Im intrested only the price value

curl -X GET “https://dashboard.elering.ee/api/nps/price/EE/current 3” -H “accept: /”

sensor:
  - platform: rest
    resource: https://dashboard.elering.ee/api/nps/price/EE/current
    name: Elering
    value_template: "{{ value_json['data'][0]['price'] }}"
    unit_of_measurement: '€'

---------------------------------------------------------------------------------------------------

id: '1643570261869'
alias: play tv in the kitchen in the morning when sensor sees me
description: ''
trigger:
  - type: motion
    platform: device
    device_id: 7a19b2c869a28edaf40ba6c0d0d1a925
    entity_id: binary_sensor.kitchen_sensor_motion
    domain: binary_sensor
condition:
  - condition: time
    after: '05:00:00'
    before: '09:30:00'
action:
  - service: remote.send_command
    data:
      device: kitchen tv
      command: power
    target:
      device_id: 1276592bc45474738b30066b2516c4f0
  - service: androidtv.adb_command
    data:
      command: am force-stop de.cyberdream.iptv.tv.player
    target:
      device_id: 1276592bc45474738b30066b2516c4f0
  - delay:
      hours: 0
      minutes: 0
      seconds: 0
      milliseconds: 500
  - service: media_player.select_source
    data:
      source: de.cyberdream.iptv.tv.player
    target:
      entity_id: media_player.kitchen_gtv
mode: restart
Basically, you want it to execute its actions only if the value of the automation’s last_triggered is before 05:00 today. If the value is after 05:00 that means it has already triggered once today.

The condition to achieve this is simple:

condition:
  - condition: time
    after: '05:00:00'
    before: '09:30:00'
  - "{{ state_attr(this.entity_id, 'last_triggered') | default(today_at(), true) < today_at('05:00') }}"

---------------------------------------------------------------------------------------------------
TIME
https://community.home-assistant.io/t/the-epic-time-conversion-and-manipulation-thread/85786/3


as_timestamp() - converts a properly formatted date/time string to a Unix Epoch representation.
timestamp_custom() - converts a UNIX Epoch timestamp into a custom date/time string = unix_epoch_timestamp|timestamp_custom(format)


Directive	Meaning	Example	Notes

%a		Weekday as locale’s abbreviated name.	
		Sun, Mon, …, Sat (en_US);
		So, Mo, …, Sa (de_DE)(1)
%A		Weekday as locale’s full name.	
		Sunday, Monday, …, Saturday (en_US);
		Sonntag, Montag, …, Samstag (de_DE)
%w		Weekday as a decimal number, where 0 is Sunday and 6 is Saturday.	0, 1, …, 6	 
%d		Day of the month as a zero-padded decimal number.	01, 02, …, 31
%-d		Day of the month as a decimal number.	1, 2, ...	31
%b		Month as locale’s abbreviated name.	
		Jan, Feb, …, Dec (en_US);
		Jan, Feb, …, Dez (de_DE)
%B		Month as locale’s full name.	
		January, February, …, December (en_US);
		Januar, Februar, …, Dezember (de_DE)
%m		Month as a zero-padded decimal number.	01, 02, …, 12
%-m		Month as a decimal number.  1, 2, ... 12	 
%y		Year without century as a zero-padded decimal number.	00, 01, …, 99	 
%Y		Year with century as a decimal number.	0001, 0002, …, 2013, 2014, …, 9998, 9999
%H		Hour (24-hour clock) as a zero-padded decimal number.	00, 01, …, 23
%-H		Hour (24-hour clock) as a decimal number. 0, 1, 2, ... 23	 
%I		Hour (12-hour clock) as a zero-padded decimal number.	01, 02, …, 12
%-I		Hour (12-hour clock) as a decimal number. 1, 2, ... 12	 
%p		Locale’s equivalent of either AM or PM.	
		AM, PM (en_US);
		am, pm (de_DE)
%M		Minute as a zero-padded decimal number.	00, 01, …, 59
%-M		Minute as a decimal number. 0, 1, 2, .. 59	 (can go to 61 to account for leap seconds)
%S		Second as a zero-padded decimal number.	00, 01, …, 59  (can go to 61 to account for leap seconds)
%-S		Second as a decimal number. 0, 1, 2, .. 59
%f		Microsecond as a decimal number, zero-padded on the left.	000000, 000001, …, 999999
%z		UTC offset in the form +HHMM or -HHMM (empty string if the the object is naive).	(empty), +0000, -0400, +1030
%Z		Time zone name (empty string if the object is naive).	(empty), UTC, EST, CST	 
%j		Day of the year as a zero-padded decimal number.	001, 002, …, 366
%-j		Day of the year as a decimal number.   1, 2, ... 366	 
%U		Week number of the year (Sunday as the first day of the week) as a zero padded decimal number. All days in a new year preceding the first Sunday are considered to be in week 0.	00, 01, …, 53
%W		Week number of the year (Monday as the first day of the week) as a decimal number. All days in a new year preceding the first Monday are considered to be in week 0.	00, 01, …, 53
%c		Locale’s appropriate date and time representation.	
		Tue Aug 16 21:30:00 1988 (en_US);
		Di 16 Aug 21:30:00 1988 (de_DE)
%x		Locale’s appropriate date representation.	
		08/16/88 (None);
		08/16/1988 (en_US);
		16.08.1988 (de_DE)
%X		Locale’s appropriate time representation.	
		21:30:00 (en_US);
		21:30:00 (de_DE)
%%		A literal '%' character.	%	

The following work also but they aren't listed in the official python documentation:

%D		date in month/day/year (ex Jan 1st, 2008 = 01/01/08) ?
%s		date & time as a UNIX Epoch timestamp ? 
%C		century ??
%e		same as %d??
%F		date in year-month-day
%g 		year in what format? same as %y?
%G 		same as %Y?
%h 		abbreviated month. same as %b?
%k 		same as %-H or %-I?
%l 		 "  "  "
%P 		Locale’s equivalent of either am or pm like %p above but lowercase ?
%r 		time as hour:minute:second AM/PM
%R 		time as hour:minute
%T 		time as hour:minute:second
%u 		UTC hour
%V 		week number of the year. same as %U or %W ??

For the items marked with a ? without further testing I can't figure out which value it is.

--

The following flag characters are permitted between the '%' character and the conversion specifier character:

_      (underscore) Pad a numeric result string with spaces.

-      (dash) Do not pad a numeric result string.

0      Pad a numeric result string with zeros even if the conversion specifier character uses space-padding by default.

^      Convert alphabetic characters in result string to uppercase.

#      Swap the case of the result string.  (This flag works only with certain conversion specifier characters, and of these, it is only really useful with %Z.)

---------------------------------------------------------------------------------------------------
Trigger on a counter.    https://community.home-assistant.io/t/how-to-create-a-trigger-with-every-counter-increment/395059

- alias: Google TV Counter
  trigger:
  - platform: state
    entity_id: counter.google_tv_counter
    to:
  condition: "{{ trigger.from_state.state | int < trigger.to_state.state | int }}"

---------------------------------------------------------------------------------------------------

{{ states.light.study_cans_current_value.last_changed }}
{{ states.light.study_cans_current_value.last_changed | as_timestamp }}
{{ ((utcnow() | as_timestamp - (states.light.study_cans_current_value.last_changed | as_timestamp)) / 60) | round(0) }}
{{ as_timestamp(20220415, 0) }}

{{ as_local(now() | as_timestamp | as_datetime) }}
{{ states('input_number.zone1') | int }}
{{ as_local( (now() | as_timestamp + states('input_number.zone1') | float * 3600 )| as_datetime ) }}

{{ states('sensor.sunrise') }}
{{ as_local((states('sensor.sunrise') | as_timestamp + 3600|int ) | as_datetime)}}
{{ as_local((states('sensor.sunrise') | as_timestamp + 5400|int ) | as_datetime)}}
{{ as_local((states('sensor.sunrise') | as_timestamp + 9000|int ) | as_datetime)}}
{{ as_local((states('sensor.sunrise') | as_timestamp + 10800|int ) | as_datetime)}}
{{ as_local((states('sensor.sunrise') | as_timestamp + 14400|int ) | as_datetime)}}
{{ as_local((states('sensor.sunrise') | as_timestamp + 16200|int ) | as_datetime)}}
{{ as_local((states('sensor.sunrise') | as_timestamp + 19800|int ) | as_datetime)}}
---------------------------------------------------------------------------------------------------

1.- Create a sensor which accounts for the time some binary sensor is 'on’

sensor:
  - platform: history_stats
    name: Time Boiler On
    entity_id: binary_sensor.led_boiler
    state: 'on'
    type: time
    start: '{{ now().replace(hour=0).replace(minute=0).replace(second=0) }}'
    end: '{{ now() }}'

2.- Create a utility meter to reset the values daily, weekly, monthly,…

utility_meter:
  boiler_daily_usage:
    source: sensor.time_boiler_on
    cycle: daily

3.- Customize the utility so that you can benefit from the long-term statistics

homeassistant:
  [...]
  customize:
    # Add an entry for each entity that you want to overwrite.
    sensor.boiler_daily_usage:
      state_class: measurement


---------------------------------------------------------------------------------------------------

https://community.home-assistant.io/t/statistics-fix-issue-large-amount-of-data/419728

For displaying data, I used the SQLite Web Add-on (GitHub - hassio-addons/addon-sqlite-web: SQLite Web - Home Assistant Community Add-ons) :

SELECT 
	CASE WHEN entity_id IS NULL
		THEN 'Orphaned'
		ELSE 'OK'
	END Issue,
	entity_id, 
	statistic_id 
FROM	(
		SELECT 
			b.entity_id, 
			a.statistic_id				
		FROM 
			statistics_meta a
		LEFT JOIN 'states' b 
			ON a.statistic_id = b.entity_id
		GROUP BY 
			a.statistic_id
		)
WHERE 
	entity_id IS NULL 


DELETE FROM "statistics_meta"
WHERE id IN (
	SELECT 
		id
	FROM	(
			SELECT 
				a.id,
				b.entity_id, 
				a.statistic_id				
			FROM 
				statistics_meta a
			LEFT JOIN 'states' b 
				ON a.statistic_id = b.entity_id
			GROUP BY 
				a.statistic_id
			)
	WHERE 
		entity_id IS NULL 
	);


 /* NULL means the statistic_id is orphaned */
---------------------------------------------------------------------------------------------------


    now() inside a template will cause the template to update on the minute, no rate_limit.
    entities inside a template will cause to update when the entity changes state, no rate_limit.
    using states without a domain will cause the template to update on all state changes, however it’s rate limited to 1 update a minute.
    using states.<domain> will cause the template to update on all state changes, however it’s rate limited to 1 update a second

---------------------------------------------------------------------------------------------------

https://community.home-assistant.io/t/dew-point-temperature-sensor-my-configuration-yaml/432445

Dew from temperature/humidity sensor.

sensor:
  - platform: template
    sensors:
      temp_dew_XY: 
        value_template: >-
          {% set T, RH = states('sensor.temperature_X'), states('sensor.humidity_Y') %}
          {% if not T or RH == 'unknown' -%}
            'unknown'
          {%- else %}
            {% set T = T | float %}
            {% set RH = RH | float %}
            {% set a = 6.1121 | float %}
            {% set b = 18.678 | float %}
            {% set c = 257.14 | float %}
            {% set d = 234.5 | float %}
            {% set e = 2.718281828 | float %}

            {% set P = a*e**((b-T/d)*(T/(c+T))) | float %}
            {% set gamma = log((RH / 100)*e**((b-T/d)*(T/(c+T)))) | float %}
            {% set dew_temp = ((c * gamma) / (b - gamma))|round(3) %}
            {{ dew_temp}}
          {% endif %}
        unit_of_measurement: "°C"
        friendly_name: "Dew Temperature"
---------------------------------------------------------------------------------------------------
entities:
  battery:
    consumption: sensor.daily_kwh_from_battery
    production: sensor.daily_kwh_to_battery
  battery_charge: sensor.powerwall_charge
  grid:
    consumption: sensor.daily_kwh_from_grid
    production: sensor.daily_kwh_to_grid
  solar: sensor.daily_kwh_produced

---------------------------------------------------------------------------------------------------


---------------------------------------------------------------------------------------------------


---------------------------------------------------------------------------------------------------
2 Likes

Regarding documentation or structuring HA, here is what I did (I am writing/updating all the files in yaml directly using the file editor of HA as I started to use HA a long time ago and all gui interfaces where not existing at that time):

  • I am splitting my automations based on some kind of domains: one file for each domain. For example, I am naming my files containing Electrical Vehicule automations: EV_electrical_vehicule.yaml… Same approach for heaters, airconditioning, pool, etc…All these files are in a subdirectory of /config called automations.
    In these automation files and for each automation, I am trying to document what the automation is doing, when it is triggered and what are the conditions to fullfill to be executed… adding some remarks if necessary to understand the reasons for some conditions, triggers or actions… For some automations, it is obvious what the automation is doing, but some can be very complex or tricky and needs to be documented. So I decided to document all the same way. example:
#=Done================================================================================================
#
# Calcul température de fonctionnement de l'electrolyseur et de la pompe de la piscine:
#   - en été: de mars à octobre
#   - suivant le mode de fonctionnement de refroidissement automatique et température piscine
# quand:
#   - à l'heure d'extinction de la pompe pour calculer planning du lendemain
#   - si changement de mode de refroidissement automatique
# conditions:
#   - si le refroidissement automatique de la piscine est activé
#   - et si la température de la piscine à l'entrée de la pompe à chaleur est inférieure ou égale à la température target pour le refroidissement
#   - et si la température de la piscine (pour le calcul: voir sensors) est connue (comprise entre 5 et 50°C, la valeur doit être au moins 6°C pour faire tourner la piscine minimum 3 heures par jour)
#   - ou si le refroidissement automatique de la piscine est désactivé
#   - et on est en été (de mars à octobre)
#   - et si la température de la piscine (pour le calcul: voir sensors) est connue (comprise entre 5 et 50°C, la valeur doit être au moins 6°C pour faire tourner la piscine minimum 3 heures par jour)
#
# les limites de fonctionnement sont calculées en partant de l'heure de milieu de plage et en retirant/ajoutant une nombre d'heures égal à la température de la piscine divisée par 4.
# Donc si la température de la piscine est de 6°C, la plage se situe donc entre la plage du milieu - 1,5 heure et la plage du milieu + 1,5 h...
#
- alias: calculate_time_montreal_ete
  initial_state: true
  trigger:
    - platform: time
      at: input_datetime.h_ext_pompe_m
    - platform: state
      entity_id: input_boolean.poolstage_refroidissement_auto
  condition: 
    condition: or
    conditions:
      - condition: and
... etc etc ...

For each automation, I am writing some comments linked with steps into a message file (that I am closing everyday at midnight, keeping 7 version/days of those files)… So I can track easily by reading this file what steps where executed during a specific day and when… example:

2022-07-19 05:58:00   **** DM-014.1 Allumage Pompe Piscine
2022-07-19 05:58:00   **** DM-016.1 Allumage Electrolyseur Piscine Montréal
2022-07-19 06:00:00   **** DM-019.1 Extinction chauffe-eau Salle de Bain (haut) Montréal
2022-07-19 06:15:02   **** EV-001.1 Chargeur Voiture: demande extinction...
2022-07-19 06:15:11   **** AG-015.1 Theme set to light (day)
2022-07-19 06:18:11   **** MQ-015.1 Ecriture Heures Lever et Coucher du Soleil à Montréal (remote via mqtt)
2022-07-19 07:00:00   **** EM-001.1 Change Energy Tariff to the next one
2022-07-19 09:03:51   **** EV-003.1 Chargeur Voiture éteint...

As you can see, every record in the file contains the time the step of the automation was executed, a reference to the file where the automation was executed (EV for the last one, so related to EV_electrical_vehicule.yaml) a number+step to find the automation/step having generated the message and a comment about the step(s) executed…

  • regarding dashboard, I splitted also all the tabs/paths by creating one file per tab/path. regrouping those files into a subdirectory called lovelace in /config directory… If for some reasons I have group of identical cards existing in multiple tabs, I am creating specific files for those cards. Those files are stored in another subdirectory of /config called tiles. I am incorporating the reference of that card into the lovelace yaml files for building one tab/paths… example:
#==============================================================================================
#**********************************************************************************************
#**                                                                                          **
#**                               Tableau de bord                                            **
#**                                                                                          **
#**********************************************************************************************
#==============================================================================================

- title: Tableau de bord Montréal
  path: montreal
  icon: mdi:billboard
  cards:
#
# Left Stack
#
      - type: vertical-stack
        cards:

#
# Cards for Alarm Off Display Status - MONTREAL
#
        - !include /config/tiles/Alarme-Montreal-Titre.yaml
        - !include /config/tiles/Alarme-Montreal-Status.yaml
        - !include /config/tiles/Alarme-Montreal-Timer.yaml
        - !include /config/tiles/Alarme-Montreal-Code.yaml

#

disadvantage: less flexibility in creating the layout of your different tabs.
advantage: If I change one file in the tiles directory, automatically all the tabs/path of lovelace using it are updated (instead of updating each file in the lovelace directory). I didn’t do this at the beginning but I decided to change the approach when the lovelace files where very long/difficult to maintain… This is a consequence of my decision to split my dashboard into multiple tabs…

  • I am using a lot of button-cards (installed using HACS) in lovelace, therefore I am using a lot of template cards to simplify the creation of repetitive cards (like lights cards, switches cards, alarms, heaters, airconditioning, etc…). This is simplifying the writing of those cards.

  • documenting sensors (in fact everywhere you use templates) is also a good idea as some templates could be very complex and difficult to understand when you come back a few months later and have to change it… for that one I have some home work to do to develop a good documentation…

This is the way I have organized my HA files, this is just of course examples and a feedback… I have two sites managed the same way with a dashboard with 27 tabs/paths in one location (the other one has only 15 tabs/paths). I have 335 automations on one site and 131 on the other one… Pretty big configurations, where a good documentation helps to maintain it.

2 Likes

if you want to save and look backwards, why not use github for that? It is an integral part of HA’s file editor…

As well as VisualStudioCode:

That is the whole point of a versioning system :wink:

I have used git but not recently and it is just easier to back up.

Lots of comments in yaml files.
I also maintain a notebook (MediaWiki) for everyhing. My PC, my Home Assistant, my Ubuntu, really anything want to recall later.

Packages look interesting, but I haven’t been able to grok.

As others have said, the “how” is contained in the current configuration files, and should be backed up.

What I have tried to focus on is the “why?”. HA has meant one learning curve after another after another … too many things to even try to fully understand any one thing … which makes it too easy to forget how and why i got to the current point. I buy another device, and its “how the heck did I end up configuring the last device in Localtuya ?”

I have a word document (currently 52 pages with index) reminding me of the basics (with links to the tutorials or web documents I actually used) and the "gotcha"s i worked around, the naming convention I adopted, etc.

OK, I tend to get verbose and write like it’s a tutorial, but that’s mostly me getting my thoughts straight :wink:

1 Like

Thanks for all the good ideas here. I’m surprised they are all “coding documentation” advice, but happy for it, as it’s a clever way to do so.

I was myself referring more to things like:

  • I have 10 devices
  • There’s a smart plug under the kitchen
  • These devices are linked to service X
  • etc.

And then the same for routines/automations, etc.

This last one seems to be something everyone logs in the code/config files, so that’s an interesting way to go.

1 Like

I use Mediawiki. Because it’s database driven indexing is very convenient. I don’t know how many pages it represents, but the database is currently 557 mb.

https://github.com/hassio-addons/addon-bookstack ??

1 Like

A very interesting topic close to my heart.

After testing our various solutions like Bookstack, GitBook and Notion I decided to use a private GitHub repo. I wanted something that would look after itself and be somewhat detached from my self-hosting while also being simple to work with and allow me to unwind by writing some Markdown.

I use issues to document anything from an appliance, Docker container config through to where the shut off valves are for the gas and water. Think of this as our family and home Wiki. The contents that some people print in those lovely bound books. The repo is shared with my SO and she knows to head there to try to answer her own questions first.

For me it was about having that one place where something was documented and over time it has become more of a discipline and more detailed. With issue comments I capture additional information for that given “thing”. The GitHub mobile app works great, issues can be created and edited on the go as well as uploading pictures straight from the phone.

I have taken it a little beyond the basics by creating some Actions that create index pages with links to the issues. This leaves me with a Markdown file of all Appliances for example and another with all my Docker config. I have a number of these Markdown files. Another great benefit is GitHub has a great rest API which you can integrate with Home Assistant to create issues or even add comments.

Staying tuned to learn from others.

1 Like

I use DokuWiki for this (and other things)