L'hiver approche et on ouvre à nouveau ce sujet...
Pour mémoire, afin de planifier un peu finement pièce par pièce un chauffage électrique par convecteurs, on a principalement trois possibilités dans Home Assistant :
- Faire une multitude de d'automations en YAML, j'ai fait lors de mes débuts avec Home Assistant et j'en ai parlé ici et là.
- Utiliser le Scheduler de Niels, j'en ai parlé ici et c'est une bonne solution GUI, mais il y a des lacunes (pas de replanification à la volée, pas de gestion ECO entre les plages).
- Et enfin Schedy dont j'ai parlé plusieurs fois ( 1 | 2 ), qui lui ne dispose pas d'interface. On va donc s'en servir de moteur et lui construire une interface Lovelace. En fait j'ai géré ma clim avec tout l'été sur la base de ce que j'avais fait et c'est parfait. Je vais maintenant l'adapter afin d'en faire profiter mon frère, avec d'autres contraintes et le souhait qu'il soit le plus autonome possible.
Cahier des charges
On veut pouvoir gérer :
- 4 plages d'horaires par jour pour 3 types de journées :
- Jour de semaine travaillé.
- Samedi matin travaillé.
- Dimanche, jours fériés et vacances à la maison...
- Activation / désactivation possible de chacune des plages et faire en sorte qu'elles ne soient affichées que si elles sont actives. Le but étant de ne pas afficher les plages inutiles pour ne pas prêter à confusion. Par exemple, le dimanche si on chauffe le séjour de 10:00 à minuit, on a besoin que d'une seule plage.
- Une température différente pour chacune des plages, en opposition à un classique ECO/CONFORT à la française.
- Une température ECO commune à toutes les périodes entre les plages; mais propre à chaque pièce et une température ABSCENT (HG).
- La présence de l'occupant de la pièce (en entrant ses dates d'arrivée et de départ ou par géolocalisation, présence dans un rayon de...). Cette fonction ne sera utile que pour les chambres des enfants qui vont et viennent...
- La planification des vacances de l'occupant principal, avec passage en mode week-end si présent.
- Boost dynamique : température t pendant n minutes. Utile pour une dérogation temporaire, genre un occupant ne va pas travailler un lundi après-midi, il serait dommage qu'il se gèle...
- Désactivation en cas d'ouverture d'une fenêtre pendant n minutes. Inutile de chauffer quand on aère les pièces...
- Un mode télétravail, je ne l'ai pas encore vraiment défini, mais il s'agira probablement d'un script qui va apporter quelques dérogation sur le principe du boost.
- Un mode dormir (utile pour passer en ECO les pièce de vie quand on va se coucher plus tôt que ce qui est programmé, s'insère dans une automation plus globale qui désactive également les éclairages).
- Un mode absence temporaire (on ne passe pas le chauffage en mode ABSCENT HG comme une absence de longue durée, mais en mode ECO (binary sur géo loc ou via les fonction de l'alarme).
Philosophie
Toutes les plages sont gérés par des binary_sensor via différentes sources (input_datetime, input_number, input_boolean, et d'autres binary_sensor...) et Schedy ne fera qu'affecter la température de consigne correspondant à la plage au thermostat (climate:) quand le binary passera à ON. Il nous faut donc 1 binary par plage, ce qui fait 3 groupes de 4 = 12 pour une pièce. Ces binary constituent la partie centrale, ils risquent donc d'évoluer et je publierait les mise à jour sur GitHub. Ne cherchez pas à recopier ces bouts de code, il ne sont là que pour expliquer la philosophie et proviennent de plusieurs tests, vous trouverez mes sources sur GitHub. et ici pour un second projet plus complet. (je vais essayer de les tenir à jour).
Si pour gérer ces binary_sensor les variables que sont les input_boolean sont simple, il faut préparer les binary_sensor qui vont nous servir à déterminer le type de journée :
binary_sensor:
- platform: template
sensors:
workday_saturday_working:
friendly_name: "Samedi travaillé"
value_template: >
{% if now().isoweekday() in (6,) and states.binary_sensor.workday_sensor.state == 'on' and states.input_boolean.holidays_andre.state == 'off' %}
true
{%else%}
false
{% endif %}
workday_weekday_working:
friendly_name: "Jour de semaine travaillé"
value_template: >
{% if now().isoweekday() in (1,2,3,4,5,) and states.binary_sensor.workday_sensor.state == 'on' and states.input_boolean.holidays_andre.state == 'off'%}
true
{%else%}
false
{% endif %}
workday_weekend_holidays:
friendly_name: "Week-End, Vacances ou jour Férié"
value_template: >
{% if states.binary_sensor.workday_sensor.state == 'off' or states.input_boolean.holidays_andre.state == 'on'%}
true
{%else%}
false
{% endif %}
Mais à y réfléchir on peut envisager quelque chose de plus simple avec un template sensor :
template:
- sensor:
- name: "Day Type"
state: >
{% if now().isoweekday() in (6,) and states.binary_sensor.workday_sensor.state == 'on' and states.input_boolean.holidays_andre.state == 'off' %}
saturday_working
{% elif now().isoweekday() in (1,2,3,4,5,) and states.binary_sensor.workday_sensor.state == 'on' and states.input_boolean.holidays_andre.state == 'off'%}
weekday_working
{% elif states.binary_sensor.workday_sensor.state == 'off' or states.input_boolean.holidays_andre.state == 'on'%}
weekend_and_holidays
{% else %}
failed
{% endif %}
Ce qui va nous donner :
binary_sensor:
heating_antoine_2_s:
entity_id: sensor.time <<< IL FAUT SUPPRIMER CETTE LIGNE DESORMAIS OBSOLETE
friendly_name: Mode Confort
value_template: >
{% set t = states('sensor.time') %}
{% set start = states('input_datetime.heating_antoine_start_2_s') [0:5] %}
{% set stop = states('input_datetime.heating_antoine_end_2_s') [0:5] %}
{{ (start <= t < stop if start < stop else (start <= t or t < stop)) == true # Slot horaire
and states('sensor.day_type') == 'saturday_working' # Type de journée
and states('input_boolean.presence_antoine') == 'on' # Présence de l'occupant
and states('input_boolean.heating_antoine_enabled_2_s') == 'on' # Activation de la plage
}}
Ce qui va se traduire dans Schedy par :
- x: state("input_number.heating_antoine_temperature_confort_2_s") if (is_on("binary_sensor.heating_antoine_2_s")) else Next()
Et bien sur dans Schedy on va gérer d'autres contraintes plus globales :
- x: "Break() if is_off('input_boolean.thermostats_on_off') else Next()" # Etat général du chauffage
- x: "Break() if is_on('binary_sensor.antoine_window_delayed') else Next()" # Fenêtre ouverte
Ainsi que le mode ECO si aucune plage confort n'est active :
- x: state("input_number.heating_antoine_temperature_eco") if (is_on("input_boolean.presence_antoine")) else Next()
Et ABSCENT si les contraintes générales son validées :
- x: state("input_number.heating_antoine_temperature_away")
Et il ne faut pas oublier l'entête, avec deux types de thermostats. A noter que dans le cas du climatiseur on va gérer deux modes HVAC : off et heat_cool.
schedy_heating:
module: hass_apps_loader
class: SchedyApp
actor_type: thermostat
actor_templates:
ac:
# send_retry_interval: 15
send_retries: 20
supports_hvac_modes: true
# off_temp: 17
# delta: 0
hvac_mode_off: "off"
hvac_mode_on: heat_cool # J'ai demandé à pouvoir gérer ce mode via un input_select: mais le développeur ne semble pas très chaud...
# max_temp: 95
# min_temp: 45
convecteur:
send_retry_interval: 30
send_retries: 10
supports_hvac_modes: true
Exemples
Et voici la programmation d'une chambre avec un convecteur :
schedule:
- rules:
- rules:
# CONTRAINTES
- x: "Break() if is_off('binary_sensor.marie_home') else Next()" # Absence temporaire, on passe en ECO
- x: "Break() if is_off('input_boolean.thermostats_on_off') else Next()" # ON / OFF global du chauffage, on passe le thermostat à OFF
- x: "Break() if is_on('input_boolean.thermostats_away') else Next()" # Absence globale prolongée (on ferme la maison), on passe en consigne AWAY
- x: "Break() if is_on('binary_sensor.marie_window_delayed') else Next()" # Contrainte ouverture de fenêtre, on passe en consigne AWAY
# CONFORT SEMAINE
- x: state("input_number.heating_marie_temperature_confort_1") if (is_on("binary_sensor.heating_marie_1")) else Next()
- x: state("input_number.heating_marie_temperature_confort_2") if (is_on("binary_sensor.heating_marie_2")) else Next()
- x: state("input_number.heating_marie_temperature_confort_3") if (is_on("binary_sensor.heating_marie_3")) else Next()
- x: state("input_number.heating_marie_temperature_confort_4") if (is_on("binary_sensor.heating_marie_4")) else Next()
# CONFORT SAMEDI
- x: state("input_number.heating_marie_temperature_confort_1_s") if (is_on("binary_sensor.heating_marie_1_s")) else Next()
- x: state("input_number.heating_marie_temperature_confort_2_s") if (is_on("binary_sensor.heating_marie_2_s")) else Next()
- x: state("input_number.heating_marie_temperature_confort_3_s") if (is_on("binary_sensor.heating_marie_3_s")) else Next()
- x: state("input_number.heating_marie_temperature_confort_4_s") if (is_on("binary_sensor.heating_marie_4_s")) else Next()
# CONFORT DIMANCHE & FERIE
- x: state("input_number.heating_marie_temperature_confort_1_d") if (is_on("binary_sensor.heating_marie_1_d")) else Next()
- x: state("input_number.heating_marie_temperature_confort_2_d") if (is_on("binary_sensor.heating_marie_2_d")) else Next()
- x: state("input_number.heating_marie_temperature_confort_3_d") if (is_on("binary_sensor.heating_marie_3_d")) else Next()
- x: state("input_number.heating_marie_temperature_confort_4_d") if (is_on("binary_sensor.heating_marie_4_d")) else Next()
# ECO
- x: state("input_number.heating_marie_temperature_eco") if (is_on("input_boolean.presence_marie")) else Next()
- x: "Break(2)"
# REGLES LIES AUX CONTRAINTES (Attention à l'ordre des règles)
- x: Mark(OFF, Mark.OVERLAY) if (is_off("input_boolean.thermostats_on_off")) else Next()
- x: state("input_number.heating_marie_temperature_away") if (is_on("input_boolean.thermostats_away")) or (is_on('binary_sensor.marie_window_delayed')) else Next()
- x: state("input_number.heating_marie_temperature_eco") if (is_on("input_boolean.presence_marie")) and (is_off("binary_sensor.marie_home")) else Next()
La contrainte fenêtre ouverte est gérée par un binary_sensor:
avec une temporisation :
binary_sensor:
- platform: template
sensors:
marie_window_delayed:
friendly_name: "Fenêtre (delayed)"
device_class: window
#window_room: bedroom
delay_on:
seconds: 300
delay_off:
seconds: 300
value_template: >-
{{ is_state('binary_sensor.fenetre_marie', 'on') }}
icon_template: >-
{% if is_state('binary_sensor.fenetre_marie', 'on') %}
mdi:window-open
{% else %}
mdi:window-closed
{% endif %}
Quant à l'absence temporaire on va la gérer avec l'intégration proximity:
qui va facilement nous donner la distance entre la maison et ma fille, ainsi et surtout sa direction. L'idée est de passer en ECO lorsque ma fille est ici pour quelques jours mais qu'elle est de sortie. Il me faudra temporiser la sortie afin de ne pas passer en ECO trop rapidement (genre elle va chercher du pain...), mais également le retour afin que sa chambre soit chauffée lors du retour effectif...
- id: '5e1b6bdd-dee7-4a63-b8cb-3b9d01719b2b'
alias: GEO - Lionel en depart
mode: single
trigger:
- platform: numeric_state
entity_id:
- proximity.lionel
above: 3
condition:
- condition: and
conditions:
- condition: template
value_template: '{{ trigger.to_state.attributes.dir_of_travel == "away_from" }}'
action:
- service: input_boolean.turn_off
target:
entity_id: input_boolean.presence_lionel_geo
- service: notify.slack_hass_canaletto
data:
message: "{{ states.sensor.date_time.state}} > Lionel s'éloigne de sa maison | Distance : {{ states.proximity.lionel.state }} Km."
Et le même en approche. On bascule un input_boolean:
que l'on pourra basculer manuellement en cas de défaillance...
- id: 'a6bbcd6c-8c18-4ea5-8992-8dae0d63c24c'
alias: GEO - Lionel en approche
mode: single
trigger:
- platform: numeric_state
entity_id:
- proximity.lionel
below: 3
condition:
- condition: and
conditions:
- condition: template
value_template: '{{ trigger.to_state.attributes.dir_of_travel == "towards" }}'
action:
- service: input_boolean.turn_on
target:
entity_id: input_boolean.presence_lionel_geo
- service: notify.slack_hass_canaletto
data:
message: "{{ states.sensor.date_time.state}} > Lionel s'approche de sa maison | Distance : {{ states.proximity.lionel.state }} Km."
Et pour plus d'élégance et pour ne pas afficher le boolean dans le Lovelace de l'utilisateur on en fait un binary (pour les test le boolean reste plus pratique que d'aller faire un tour en voiture...) :
binary_sensor:
- platform: template
sensors:
lionel_geo:
friendly_name: Lionel (Présence relative sur Géoloc)
device_class: presence
icon_template: >-
{% if is_state('binary_sensor.antoine_away_from_home','on') %} mdi:home-account
{% else %} mdi:home-outline
{% endif %}
value_template: >-
{{ is_state('input_boolean.presence_lionel_geo', 'on') }}
Un autre exemple est celui du climatiseur. Dans mon cas il est dans un large couloir et arrose plus ou moins toutes les pièces. C'est bien sur à adapter à la région, l'installation et au mode de vie, mais dans mon cas on va interagir différemment selon la saison .Afin de ne pas tout dupliquer il sera nécessaire d'adapter les consignes en début d'été et en début d'hiver.
- En été on va avoir des périodes de refroidissement et des périodes ou le climatiseur sera OFF.
- En hiver on va avoir des périodes CONFORT et des périodes ECO.
En fait Schedy est fait pour faire fonctionner les climatiseurs en mode Heat/Cool. Ca ne me convient pas car en mode demie saison parfois il se mets en route alors que c'est inutile. J'ai demandé au développeur de faire des modifications, mais ce n'est pas sa préoccupation du moment. J'ai donc dupliqué deux rooms pour lui en faisant de sorte que seule l'une puisse fonctionner. C'est un pis aller en attendant mieux. Ca génère une erreur dans le log, mais elle n'est qu'indicative.
ac_heat:
allow_manual_changes: true
rescheduling_delay: 1
actors:
climate.daikin:
send_retries: 20
supports_hvac_modes: true
hvac_mode_off: "off"
hvac_mode_on: heat
watched_entities:
- input_number.heating_ac_temperature_confort_1
- input_number.heating_ac_temperature_confort_2
- input_number.heating_ac_temperature_confort_3
- input_number.heating_ac_temperature_confort_4
- input_number.heating_ac_temperature_confort_1_d
- input_number.heating_ac_temperature_confort_2_d
- input_number.heating_ac_temperature_confort_3_d
- input_number.heating_ac_temperature_confort_4_d
- input_number.heating_ac_temperature_eco
- input_number.heating_ac_temperature_away
- binary_sensor.heating_ac_1
- binary_sensor.heating_ac_2
- binary_sensor.heating_ac_3
- binary_sensor.heating_ac_4
- binary_sensor.heating_ac_1_d
- binary_sensor.heating_ac_2_d
- binary_sensor.heating_ac_3_d
- binary_sensor.heating_ac_4_d
- input_boolean.presence_ac
- binary_sensor.life_windows_and_doors_delayed
- input_boolean.heating_enabled
- input_boolean.cooling_enabled
- input_boolean.thermostats_ac_on_off
schedule:
- rules:
- x: "Break() if is_off('input_boolean.thermostats_ac_on_off') else Next()" # Etat général du chauffage
- x: "Abort() if is_off('input_boolean.heating_enabled') else Next()" # On stop toute planification si on n'est pas dans ce mode
- rules:
# CONTRAINTES
- x: "Break() if is_off('input_boolean.presence_ac') else Next()" # Présence au sens callendrier actif
- x: "Break() if is_on('binary_sensor.life_windows_and_doors_delayed') else Next()" # Fenêtre ouverte
- rules:
- x: "Break() if is_on('input_boolean.to_sleep') else Next()" # Je vais dormir...
- x: "Break() if is_off('binary_sensor.lionel_geo') else Next()" # Je vais au ciné...
# CONFORT SEMAINE
- x: state("input_number.heating_ac_temperature_confort_1") if (is_on("binary_sensor.heating_ac_1")) else Next()
- x: state("input_number.heating_ac_temperature_confort_2") if (is_on("binary_sensor.heating_ac_2")) else Next()
- x: state("input_number.heating_ac_temperature_confort_3") if (is_on("binary_sensor.heating_ac_3")) else Next()
- x: state("input_number.heating_ac_temperature_confort_4") if (is_on("binary_sensor.heating_ac_4")) else Next()
# CONFORT WEEK-END & FERIE
- x: state("input_number.heating_ac_temperature_confort_1_d") if (is_on("binary_sensor.heating_ac_1_d")) else Next()
- x: state("input_number.heating_ac_temperature_confort_2_d") if (is_on("binary_sensor.heating_ac_2_d")) else Next()
- x: state("input_number.heating_ac_temperature_confort_3_d") if (is_on("binary_sensor.heating_ac_3_d")) else Next()
- x: state("input_number.heating_ac_temperature_confort_4_d") if (is_on("binary_sensor.heating_ac_4_d")) else Next()
# ECO
- x: state("input_number.heating_ac_temperature_eco") if (is_on("input_boolean.presence_ac")) and (is_on("input_boolean.heating_enabled")) else Next()
- x: "Break(2)"
# REGLES LIES AUX CONTRAINTES
- x: state("input_number.heating_ac_temperature_eco") if (is_on("input_boolean.to_sleep")) and (is_on("input_boolean.heating_enabled")) else Next()
- x: state("input_number.heating_ac_temperature_eco") if (is_off("binary_sensor.lionel_geo")) and (is_on("input_boolean.heating_enabled")) else Next()
- x: Mark(OFF, Mark.OVERLAY) if (is_off("input_boolean.thermostats_ac_on_off")) else Next()
- x: Mark(OFF, Mark.OVERLAY) if (is_off("input_boolean.presence_ac")) else Next()
- x: Mark(OFF, Mark.OVERLAY) if (is_on("binary_sensor.life_windows_and_doors_delayed")) else Next()
ac_cool:
allow_manual_changes: true
rescheduling_delay: 1
actors:
climate.daikin:
send_retries: 20
supports_hvac_modes: true
hvac_mode_off: "off"
hvac_mode_on: cool
watched_entities:
- input_number.heating_ac_temperature_confort_1
- input_number.heating_ac_temperature_confort_2
- input_number.heating_ac_temperature_confort_3
- input_number.heating_ac_temperature_confort_4
- input_number.heating_ac_temperature_confort_1_d
- input_number.heating_ac_temperature_confort_2_d
- input_number.heating_ac_temperature_confort_3_d
- input_number.heating_ac_temperature_confort_4_d
- input_number.heating_ac_temperature_eco
- input_number.heating_ac_temperature_away
- binary_sensor.heating_ac_1
- binary_sensor.heating_ac_2
- binary_sensor.heating_ac_3
- binary_sensor.heating_ac_4
- binary_sensor.heating_ac_1_d
- binary_sensor.heating_ac_2_d
- binary_sensor.heating_ac_3_d
- binary_sensor.heating_ac_4_d
- input_boolean.presence_ac
- binary_sensor.life_windows_and_doors_delayed
- input_boolean.heating_enabled
- input_boolean.cooling_enabled
- input_boolean.thermostats_ac_on_off
schedule:
- rules:
- x: "Break() if is_off('input_boolean.thermostats_ac_on_off') else Next()"
- x: "Abort() if is_off('input_boolean.cooling_enabled') else Next()"
- rules:
# CONTRAINTES
- x: "Break() if is_off('input_boolean.presence_ac') else Next()"
- x: "Break() if is_on('binary_sensor.life_windows_and_doors_delayed') else Next()"
- rules:
- x: "Break() if is_on('input_boolean.to_sleep') else Next()"
- x: "Break() if is_off('binary_sensor.lionel_geo') else Next()"
# CONFORT SEMAINE
- x: state("input_number.heating_ac_temperature_confort_1") if (is_on("binary_sensor.heating_ac_1")) else Next()
- x: state("input_number.heating_ac_temperature_confort_2") if (is_on("binary_sensor.heating_ac_2")) else Next()
- x: state("input_number.heating_ac_temperature_confort_3") if (is_on("binary_sensor.heating_ac_3")) else Next()
- x: state("input_number.heating_ac_temperature_confort_4") if (is_on("binary_sensor.heating_ac_4")) else Next()
# CONFORT WEEK-END & FERIE
- x: state("input_number.heating_ac_temperature_confort_1_d") if (is_on("binary_sensor.heating_ac_1_d")) else Next()
- x: state("input_number.heating_ac_temperature_confort_2_d") if (is_on("binary_sensor.heating_ac_2_d")) else Next()
- x: state("input_number.heating_ac_temperature_confort_3_d") if (is_on("binary_sensor.heating_ac_3_d")) else Next()
- x: state("input_number.heating_ac_temperature_confort_4_d") if (is_on("binary_sensor.heating_ac_4_d")) else Next()
# ECO
- x: Mark(OFF, Mark.OVERLAY) if (is_on("input_boolean.presence_ac")) and (is_on("input_boolean.cooling_enabled")) else Next()
- x: "Break(2)"
# REGLES LIES AUX CONTRAINTES
- x: Mark(OFF, Mark.OVERLAY) if (is_on("input_boolean.to_sleep")) and (is_on("input_boolean.cooling_enabled")) else Next()
- x: Mark(OFF, Mark.OVERLAY) if (is_off("binary_sensor.lionel_geo")) and (is_on("input_boolean.cooling_enabled")) else Next()
- x: Mark(OFF, Mark.OVERLAY) if (is_off("input_boolean.thermostats_ac_on_off")) else Next()
- x: Mark(OFF, Mark.OVERLAY) if (is_off("input_boolean.presence_ac")) else Next()
- x: Mark(OFF, Mark.OVERLAY) if (is_on("binary_sensor.life_windows_and_doors_delayed")) else Next()
Les deux modes du climatiseur sont gérés par deux input_boolean:
et deux automations de façon à ce qu'un seul mode puisse être ON à un instant t.
- id: 'd1bdf1ac-4d43-4f04-9b09-03e7a5e9ff59'
alias: 'CLIMATE - AC Chaud'
description: ''
trigger:
- platform: state
entity_id: input_boolean.heating_enabled
to: 'on'
- platform: state
entity_id: climate.daikin
# attribute: hvac_mode
to: 'heat'
action:
- service: input_boolean.turn_off
target:
entity_id: input_boolean.cooling_enabled
- service: input_boolean.turn_on
target:
entity_id: input_boolean.heating_enabled
- service: climate.set_hvac_mode
target:
entity_id: climate.daikin
data:
hvac_mode: heat
- id: '8fbf1387-04db-44e8-a210-472e4ff80501'
alias: 'CLIMATE - AC Froid'
description: ''
trigger:
- platform: state
entity_id: input_boolean.cooling_enabled
to: 'on'
- platform: state
entity_id: climate.daikin
# attribute: hvac_mode
to: 'cool'
action:
- service: input_boolean.turn_off
target:
entity_id: input_boolean.heating_enabled
- service: input_boolean.turn_on
target:
entity_id: input_boolean.cooling_enabled
- service: climate.set_hvac_mode
target:
entity_id: climate.daikin
data:
hvac_mode: cool
On pourrait compléter ces deux automations avec pour chacune 12 input_number.set_value
afin de d'adapter les consigne à la saison.... On pourrait
service: input_number.set_value
target:
entity_id: input_number.heating_ac_temperature_confort_1
data:
value: 23
Et tant qu'à y être on pourrait aussi déclencher tout ça avec un trigger basé sur la saison ou appliquer un condition...
trigger:
- platform: state
entity_id: sensor.season
to: summer # A vérifier, je ne suis pas certain que le "to" passe
# Ou en condition....
condition:
- condition: state
entity_id: sensor.season
state: summer
Ne cherchez pas à recopier ces bouts de code, il ne sont là que pour expliquer la philosophie, vous trouverez mes sources sur GitHub. (je vais essayer de les tenir à jour).
L'interface
C'est là toute la difficulté, créer une interface simple pour un utilisateur lambda. Personnellement taper dans le YAML de Schedy me va bien, mais pour mon frère que je souhaite autonome dans sa maison il n'en est pas question.
Il ne faut pas que cette interface soit compliquée. J'ai donc choisit de masquer tout ce que n'est pas utile au quotidien. Pour y parvenir j'ai utilisé des cartes conditionnelles, l'intégration Fold-Entity et bien sur Multiple-Entity-Row que j'utilise régulièrement.. Un onglet sera dédié à chaque pièce, onglet dans lequel on trouve deux cartes Vertical Stack. Une première pour les fonctions et informations générales et la seconde ou vont se déplier les plages actives
Outre la visualisation du thermostat graphique, on va retrouver ici des informations telles une fenêtre ouverte ou la puissance de l'appareil :
Et au passage un sensor pour obtenir le graphe des consignes successives :
sensor:
- platform: template
sensors:
temp_up_th_ac: # Pour History Graph
friendly_name: "TH AC"
value_template: >
{% if is_state('climate.daikin', 'heat') %}
Heat {{state_attr ('climate.daikin', 'temperature')}}°
{% elif is_state('climate.daikin', 'cool') %}
Cool {{state_attr ('climate.daikin', 'temperature')}}°
{% else %}
Off
{% endif %}
Ensuite si on déroule les options, on retrouve les consignes ECO et ABSENCE, le retour Schedy n'est là que pour du debug, il nous indique l'information reçue par Schedy. Les 3 commutateurs suivant seront des contraintes générales que nous pourrons utiliser ultérieurement dans Schedy.
On va ensuite déplier le boost. On choisit ici la température et la durée. Un reset est prévu afin d'annuler un boost lancé malencontreusement.
Le boost est géré par un script qui va demander à Schedy de passer outre les planifications prévues pendant la durée choisie. De base j'ai demandé à Schedy de replanifier toute les minutes, en actionnant ce script on va mettre en pause la replanification interne.
script:
heating_antoine_boost:
alias: Boost
icon: mdi:timer-outline
sequence:
- event: schedy_set_value
event_data:
app_name: schedy_heating
room: chambre_antoine
v: '{{ states.input_number.heating_antoine_temperature_boost.state }}'
rescheduling_delay: '{{ states.input_number.heating_antoine_time_boost.state }}'
La carte suivante de notre stack est consacrée à la présence de l'occupant (avec ses dates d'arrivée et de départ cachées, sachant que ces dates, principalement liées aux chambres des enfants, pourront être gérées autrement (calendrier scolaire, géoloc sur approche, ...) ainsi que le choix des plages à activer pour chaque type de journée :
Ensuite on passe à la Vertical Stack des différentes plages. Il s'agit ici d'une suite de 12 cartes conditionnelles qui ne s'affichent que lorsqu'elle sont choisies dans la carte précédente. J'ai nommé ces plages NUIT, MATIN, MIDI et SOIR, mais c'est purement informel, 1, 2, 3, et 4 aurait pu faire l'affaire et rien n'empêche d'en créer plus ou moins... Si une plage n'est pas activée elle ne sera ni visible, ni fonctionnelle, et quand une plage passe en mode confort un indicateur est présent.
Attention : Je n'ai pas fait de contrôle pour prévenir une éventuelle superposition des plages. On part donc du principe qu'elles sont consécutives (plus pratique visuellement, mais sans conséquence), mais surtout qu'elles ne se superposent pas. Il doit être possible de gérer ça, mais ça me semble bien compliqué...
Déploiement
J'ai déjà expliqué comment installer Schedy dans les articles précédents.
Pour le reste j'ai mis le code sur GitHub. Il y a d'une part le fichier de configuration schedy_heating.yaml
qui trouvera sa place dans le répertoire /config/appdaemon
et la partie HA avec un fichier global et fichier propre à chaque pièce fait l'objet d'un fichier dans le répertoire /packages
. On y trouvera donc heating_global.yaml
et heating_antoine.yaml
pour cette première pièce.
Pour créer une nouvelle pièce on va dupliquer heating_antoine.yaml
en heating_cuisine.yaml
par exemple, et ensuite faire un cherche et remplace (case sensitive) dans un éditeur afin de remplacer toutes occurrences antoine
par cuisine
et Antoine
par Cuisine
. Et vous l'aurez compris, même punition pour les cartes Lovelace.
Je vous conseille toutefois de bien fignoler et debugger une première pièce avant de déployer les suivantes. J'ai joué la carte du fonctionnel sans trop me préoccuper de l'esthétique, et chaque pièce va comporter des particularités. Inutile par exemple de déployer les fonctions Arrivée / Départ sur les pièces communes. On fait ici du sur mesure, ça sous entend également d'y passer un peu de temps....
Voilà ! Et j'espère ne pas y revenir....