Home Assistant IR, AC & More...

Pour piloter un climatiseur avec Home Assistant il existe plusieurs options, mais vu qu'il n'existe pas de protocole unifié (à par peut être KNX que l'on oublie pour des questions de coûts), ça va bien souvent se terminer en IR, là ou une option MQTT sur les interfaces WI-FI des constructeurs aurait été une bénédiction. En dehors de Dalkin qui dispose d'une intégration et Xiaomi (non importé), ces interfaces sont bien sur propriétaires et fermées.

Alors on se rabat sur l'infrarouge, ce qui n'est pas très sexy en 2020 ! D'ailleurs si vous voulez juste piloter une clim avec une application, plutôt que de payer cher une option WIFI du constructeur, pour 80/90 € offrez vous simplement un Tado ou un Sensibo, ça fera le job en mieux et ce sera compatible Alexa et Google Home, avec des intégrations Home Assistant (Tado | Sensibo) à la clé. Sauf que c’est du cloud et que je veux un pilotage local.

On va donc partir sur un Broadlink qui dispose d'une intégration. A la base ce gadget permet de piloter n’importe quel appareil IR depuis une application mobile. Ici on va faire la même chose, mais de puis Home Assistant, et on va voir qu'il existe plusieurs façons de faire, et accessoirement que j'ai perdu bien du temps en ne trouvant pas la seconde option tout de suite...

Intégration du Broadlink à Home Assistant

Ça c'est la première chose à faire dans tous les cas, sans perdre de vue que votre Broadlink sera sur l'application ou sur Home Assistant, pas les deux. Il est toutefois intéressant de l'ajouter une fois à l'application pour récupérer son IP et son adresse Mac.

  1. On supprime le Broadlink de l'application mobile.
  2. Sans le débrancher on lui fait un reset en appuyant 6 secondes avec un trombone.
  3. On ajoute le Broadlink au réseau avec l'application, mais sans le connecter au cloud Broadlink, pas maintenant, pas plus tard. On sort de l'application et on l'oublie.

Ensuite on ajoute ces quelques lignes au fichier configuration.yaml en reportant les infos précédament récupérées et on redémarre HA. Idéalement on place une réservation DHCP sur cette IP.

remote:
  - platform: broadlink
    name: rm4_mini
    type: rm4c_mini  # Ça c'est très important....
    host: 192.168.210.132
    mac: '24:AF:A2:30:0D:E2'

A partir de là on a deux possibilités. La première va consister à apprendre les codes IR et à les restituer. Pour les apprendre on va faire un petit script et se laisser guider par les notifications de Home Assistant pour presser sur les touches idoines au bon moment...

learn_tv_commands:
  alias: Apprentissage IR
  sequence:
  - data:
      command:
      - turn on
      - turn off
      - volume up
      - volume down
      - chanel up
      - chanel down
      device: television_sam
      entity_id: remote.sam
    entity_id: remote.rm4_mini
    service: remote.learn_command

Et on va se retrouver avec les fichier /config/.storage/broadlink_remote_24dfa2300de2_codes qui va contenir nos codes IR, fichier dans lequel il nous suffira d'aller récupérer les bons codes.

{
    "data": {
        "television_sam": {
            "chanel down": "JgDSAJOUEjcTNxQ1ExIUERMSExITERQ2FDYTNhMSExITEhMSFBETERQRExITEhM2FBETEhMSFDYUNRM3FDYTEhM2EzcTNhQABgKWkhQ2EzcUNRQRExITEhMSFBEUNRM3EzYUERMSFBEUERQRExITERQRExIUNhMSFBEUEBQ2EzYUNhM3FBETNhM3EzYVAAYBlpIUNhQ2FDUTEhQRFBEUERMRFDYUNhM2FBEUERQRExIUERQQFBEUERMSFDUVEBQRExIUNhQ1FDYUNhQQFDYUNhQ1FQANBQAAAAA=",
            "chanel up": "JgAYAZSTEzcTNhM3ExIUERMRFBETEhQ2FDUUNhQRExITERUQFBETEhQ2FBEUEBQ2ExIUERQRFDUTEhQ2FDUUERQ2FDUUNhQABgOUkxQ2EzcUNRMSExIUERQRFBAUNhQ2FDUUERQRFBEUERQRFBAUNhQRExIUNRQRFBETEhM2FRAVNRM3FBETNhQ2FDUVAAYClZIVNRQ2FDUVEBQRFBEUERMSFDUUNhQ2FBAVEBMSFBEUERQRFDUUERQRFDYTERUQFBEUNhMSFDUUNhMSFDUUNhM3FAAGApWTFDUUNhM3FBAUERQRFBETEhQ1FTUUNhQRExEVEBQRFBETEhQ1FRAUERQ2FBETERQREzcUERQ1FDYUERQ1FDYUNhQADQUAAAAAAAAAAAAAAAAAAA==",
            "turn off": "JgBGAJGVEzcTNhQ2ERQTEhMSExEUERM3EzcTNhMSExITEhEUExEUERM3ExITEhMREhMTEhMSEzYUERQ2ETkTNhQ2EjgTNhQADQU=",
            "turn on": "JgCMAJWTEzYTNhQ2FBETEhMSExEUERQ2EzYUNhQRFBETEhMSExEUERQ2ExITEhMRFBEUERQREzYUERQ2FDYTNhQ2FDYTNhQABgOVkhQ2FDYTNhQRFBEUERMSExITNhQ2EzYUERQRFBEUERMSExITNhQRFBETEhMSExEUERQ2ExITNhQ2FDYTNhQ2FDYTAA0FAAAAAAAAAAAAAA==",
            "volume down": "JgDSAJSTEzYTNxM3ExIRExMSExITEhM3ETgSOBMSExITERQRExITNxM2ExITNxEUExISEhMSExITEhM3ERMTNxM3EzYSOBEABgSWkxE5ETgTNxEUExITEhISEhMTNxM2EzcTEhMSExIRExMSEzcTNhITEzcRFBMSExIRExMSExITNxEUEjcTNxE4EzcTAAYDlZMTNxM2EzcTEhMSExITERQREzcTNxI3ExITEhMSExITERQ2EzcTEhM2ExITEhMSExETEhMSEzcTEhM2EzcTNxM2EwANBQAAAAA=",
            "volume up": "JgDSAJKVETgSOBE5ERQRExITEhMRFBE5ETgSOBEUERQRExITEhMRORE4EjgRFBEUERQRExITEhMRFBEUETgSOBE5ETgSOBIABgSVkxQ2EzYUNhMSExITERQRFBEUNhM2FDYUERMSExITERQRFDYTNhQ2FBETEhMSExITERQRFBETEhM2FDYUNhM2FDYUAAYClJQTNxM2FDYTEhITEhMSEhMSEzcSNxM3ExISExEUEhISExM3EjgROBITERQRFBEUERMSExITEhMROBI4EjgROBI4EQANBQAAAAA="
        }
    },
    "key": "broadlink_remote_24aca7a1dce2_codes",
    "version": 1
}

Ensuite on va créer un switch qui pourra d'ailleurs en contenir plusieurs... Et avec lesquels ont pourra interagir depuis des automation, scripts ou l'interface Lovelace...

- platform: broadlink
  host: 192.168.210.132
  mac: '24:AF:A2:30:0D:E2'
  type: rm4c_mini  # Toujours aussi important !
  timeout: 15
  switches:
    tv_on:
      friendly_name: "TV Power"
      command_on: 'JgCMAJWTEzYTNhQ2FBETEhMSExEUERQ2EzYUNhQRFBETEhMSExEUERQ2ExITEhMRFBEUERQREzYUERQ2FDYTNhQ2FDYTNhQABgOVkhQ2FDYTNhQRFBEUERMSExITNhQ2EzYUERQRFBEUERMSExITNhQRFBETEhMSExEUERQ2ExITNhQ2FDYTNhQ2FDYTAA0FAAAAAAAAAAAAAA=='
      command_off: 'JgDSAJKUFDYSOBI3ExITEhITEhMSExI2FDcSNxMSExITEhITEhMSExI3ExISExITEhITEhMSEjgSExI3EzcSOBI3EzcSNxMABgOVkxQ2EzcSOBMRFBETEhITExITNhQ2EjcUERQRFBETEhITExITNhMSExITEhMSExEUERM3ExISNxQ2EzcTNhQ2EzcTAAYClZQTNhQ2EzcTERQRFBETEhMSEzYUNhM3EhMSEhMSFBETEhITEjcTEhMSEhMSExITEhITNxITEjgSNxM3EjcTNxM3EgANBQAAAAA='

Ça c'était la version manuelle, c’est pratique car ça permet d'associer n’importe quel appareil piloté par une télécommande IR. Par contre c'est fastidieux et j'ai passé mon après-midi à me dire qu'il était impossible que quelqu'un n'ai pas fait mieux, à savoir une intégration ou je n'aurais qu'à choisir la référence de mon climatiseur pour le commander avec une carte Climate. Et ça existe !

SmarIR

SmartIR s'appuie sur le Broadlink, ou d'autres interfaces IR, qu'il s'agisse de Xiaomi, d'un ESP DIY ou d'un objet en MQTT. Pour le configurer il nous faut avoir intégré notre interface à HA, ici le Broadlink, et installer le composant depuis HACS.

Pour activer cette intégration on ajoute une entrée dans configuration.yaml :

smartir:

Une entrée dans switch.yaml :

- platform: broadlink
  host: 192.168.210.132
  mac: '24:AF:A2:30:0D:E2'
  type: rm4c_mini  # Toujours aussi important !

Et enfin une dernière dans climate.yaml ou l'on n'oubliera pas de reporter le bon code IR : 

- platform: smartir
  name: Office AC
  unique_id: office_ac
  device_code: 1260  # Ici le code IR pour les climatiseurs Toshiba
  controller_data: 192.168.210.132
  temperature_sensor: sensor.temperature  # Ici on peut associer un capteur de température externe
  humidity_sensor: sensor.humidity  # Ici on peut associer un capteur d'hygrométrie externe
  power_sensor: binary_sensor.ac_power

Un petit redémarrage et on est prêt à piloter notre climatiseur comme on le ferait pour n'importe quel thermostat, sauf qu'ici on dispose de toutes les commandes utiles (mixte, chauffage, refroidissement, de-humidification, ventilation) propre au modèle choisit (on ne voit pas

Mon projet portait sur la climatisation, mais avec SmartIR il est également possible de piloter des ventilateurs ou du matériel Audio / Vidéo, pourquoi pas votre magnétoscope VHS ! Et ce qui ne sera pas possible avec SmartIR le sera en mode manuel.

EDIT HA 0.115 : Broadlink est maintenant intégré à HA, donc on vire ce qui est dans configuration.yaml et switch.yaml et on adapte l'utilisation avec le device ID que l'on trouvera dans l'intégration, ici remote.ir_clim_remote :

- platform: smartir
  name: AC Sejour
  unique_id: ac_sejour
  device_code: 1260
  controller_data: remote.ir_clim_remote
  temperature_sensor: sensor.mi_t_carre
  humidity_sensor: sensor.mi_h_carre
  power_sensor: binary_sensor.ac_power

Sources

Gérer ses VM Azure...

Ceci est un Notepad en évolution... Revenez...

Start / Stop

Un des intérêts d'une VM dans le cloud, c’est de n'en payer que l'usage. Pour la faire courte et pour donner suite à mon article sur les Bureaux à distance, je me suis retrouvé pour ce client avec VM qui n'était pas assez puissante. Sous Azure, c’est facile, en deux clics on change la taille de la VM, on choisit une instance avec plus de CPU, RAM, IO et bien sur le tarif augmente... Et si Azure est fonctionnellement remarquable, les tarifs sont purement stratosphériques si on les compare à une VM sur un serveur dédié chez Scaleway ou OVH (qu'il faudra bien sur géré, ce qui a aussi un coût et n’est pas faisable facilement par tout le monde). Bien sur ont peut facilement imaginer que les grand comptes obtiennent des remises tout aussi conséquents, sauf que pour une PME ça reste bien souvent inaccessible.

Vu que cette VM ne va être utilisée qu'aux heures de bureau, une façon de faire quelques économies va consister à programmer une heure de démarrage et une heure d'arrêt. Pour l’arrêt c’est facile, il suffit de configurer ça dans les paramètres de la VM. Mais allez donc savoir pourquoi il n'y a pas la même facilité pour la démarrer ? Chez Microsoft il appellent ça By Design. Il doit bien y avoir une raison puisque qu'il semblerait que ce soit à peu près le même fonctionnement chez les autres fournisseurs d'instances cloud et même chez OVH ou Scaleway. Bref, ce serait trop facile.

Il y plusieurs solution pour palier à ça !

Azure CLI

On peu bricoler quelque chose avec Azure CLI (Linux, Mac, Windows) et ainsi lancer un script qui va lancer ou stopper a VM. Pas très sexy ni trop sécurisé. 

Attention : Il ne faut pas se contenter d’arrêter la VM depuis l'O/S car si cela arrêtera bien la VM, elle continuera à être facturée, simple, mais sans intérêt. Pour qu'elle ne soit plus facturé il faut la désallouer (Deallocate) et ainsi seul le stockage continuera à être facturée.

Démarrer la VM

az vm start --name MyVM --resource-group MyVMGroup

Arrêter et désallouer

az vm stop --name MyVM --resource-group MyVMGroup
az vm deallocate --name MyVM --resource-group MyVMGroup

Il est également possible de faire ça en utilisant directement les ID des VM

az start --ids "/subscriptions/a35d316a-2a2a-48e2-8834-55481f7bbb48/resourceGroups/WIN16VM/providers/Microsoft.Compute/virtualMachines/Win16VM"
az stop --ids "/subscriptions/a35d316a-2a2a-48e2-8834-55481f7bbb48/resourceGroups/WIN16VM/providers/Microsoft.Compute/virtualMachines/Win16VM"
az deallocate --ids "/subscriptions/a35d316a-2a2a-48e2-8834-55481f7bbb48/resourceGroups/WIN16VM/providers/Microsoft.Compute/virtualMachines/Win16VM"

Azure Automation

Tout ça n'est pas très sexy ni user friendly. Une autre façon de faire va consister pour l'administrateur de programmer le démarrage de la VM et son arrêt en considérant que l'utilisateur s'en servira à heures fixes. Pour l’arrêt on va se servir de la fonction intégrée car elle est capable de base d'envoyer un mail à l’utilisateur lui proposant d'outrepasser l’arrêt programmé sans avoir à se connecter. Pratique.

Pour le démarrage par contre ça va être un peu plus compliqué. On va commencer par créer un compte Automation, simplement en cherchant dans la barre de recherche. Comme tout sur Azure, ce service est également payant, mais dans sa grande bonté Microsoft nous offre (jusqu'à quand ?) un petit forfait gratuit tous les mois qui sera amplement suffisant pour ce qu'on va faire.

Une fois dans notre compte Automation on va créer un Runbook, on lui donne un nom et on choisit un Flux de travail PowerShell (PowerShell Workflow) et on le crée. Une fois créé on va aller l'éditer

workflow Start-Ma-VM
{
# Association to the Azure subscribtion
$Conn = Get-AutomationConnection -Name AzureRunAsConnection
Add-AzureRMAccount -ServicePrincipal -Tenant $Conn.TenantID -ApplicationId $Conn.ApplicationID -CertificateThumbprint $Conn.CertificateThumbprint

# Start the virtual machine
Start-AzureRMVM -ResourceGroupName "MonGroupeDeRessources" -Name "MaVM"
}

Il ne reste plus qu'à aller dans le menu Planifications et d'en ajouter une en configurant l'heure de démarrage et la récurrence, sans oublier de choisir le bon fuseau horaire. Si on veut envoyer une notification par un mail ou faire autre chose il est bien sur possible de le faire dans le script.

A ce stade ça reste toutefois limité, la VM va démarrer tous les jours, même quand personne ne travaille.

Heureusement il est également possible de créer un WebHook et ainsi démarrer la VM depuis une URL. Ici depuis PowerShell mais on peut tout à fait imaginer intégrer ça dans un portail intranet ou un simple raccourcis sur le poste de l'utilisateur... Ou mieux utiliser un planificateur évolué tenant compte des jours fériés et des week-ends, voire interagir avec un agenda Google ou Microsoft 365...

PS C:\Users\Lionel> Invoke-WebRequest -Uri https://6bc1fsdfs-aesdf4c2f-8d31dsfsdf7fsd5f.webhook.we.azure-automation.net/webhooks?token=ZHuMYFqgqgfqsqgdgB%2qsdgfqsdgjc%3d -Method POST

Alternatives

Si la lecture de la documentation n'est pas toujours très lisibles, il faut bien admettre qu'Azure est très complet. Et si cela ne vous suffit pas il existe des fournisseurs de service qui se sont spécialisés dans la planification des VM et plus globalement vous aideront à réduire les coûts du cloud, que ce soit sur Azure, AWS ou GoogleCloud. Je pense par exemple à ParkMyCloud ou MyCloudToolbox, mais il y en d'autres. Se posera alors la question de la confiance que vous accorderez à ces services.

L'accès des clients aux VM

Comme on l'a vu le plus simple consiste à utiliser le client d'accès à distance. Le protocole RDP n'étant pas franchement des plus sur. Sécuriser RDP imposerait de passer par des certificats et une passerelle complexe et coûteuse ou par une alternative non Microsoft du genre Royal TS Server. La logique impose de passer par un VPN. Azure propose bien entendu tout une panoplie de solutions de sécurité, n'ayant ni envie de passer à la caisse ni celle de me compliquer la vie pour ce type d'utilisation, je vais simplement utiliser ZeroTier. C'est plus un SDN qu'un VPN, c'est OpenSource et gratuit et ça fait parfaitement le travail (j'en avais déjà parlé et on trouve maintenant une multitude d'articles).

On donnera une IP fixe au serveur et tous les clients autorisés pourront accéder au serveur RDP. Ensuite on prendra soin de ne plus autoriser l'accès via l'IP externe Azure ou de forcer un ACL sur une IP cliente sure. Easy, d'autant plus que mon client utilise déja cette solution pour sécuriser l'accès à des ressources SMB distantes.

Scénario pratique....

Sur le poste de on prépare un script PowerShell qui va lancer le WebHook pour démarrer la VM Azure, attendre que le serveur soit lancé, tester le port RDP et lancer le Bureau à distance, et au passage notifier un IT si quelque chose se passe mal. S'il doit y revenir dans la journée il ne lancera que le client RDP (Bureau à distance), en fin de journée le serveur sera programmé pour s’arrêter tout seul et si l’utilisateur veut faire des heures supplémentaires il n'aura qu'à cliquer dans le mail reçu pour reprogrammer l’arrêt...

On commence par brouiller à minima le mot de passe du mail...

"P4ssww0rd!" | Convertto-SecureString -AsPlainText -Force | ConvertFrom-SecureString | Out-file C:\Users\User1\admin.txt

Et ensuite on prépare un petit script qui se lancera depuis un raccourcis sur le bureau...

$password = Get-Content C:\Users\User1\admin.txt | Convertto-SecureString # On récupère le mot de passe encrypté dans un fichier...
$username = "[email protected]"
$From = "[email protected]"
$To = "user@prestataire"
$SMTPServer = "mail.gandi.net"
$SMTPPort = 587
$subject = "Client : Utilisateur (Machine)"
$Credential = New-Object System.Management.Automation.PSCredential ($username, $password)

try {
    $response = Invoke-WebRequest -Uri https://6bcghgdfh-afgh-4ghgfdf-8ghfgdh5f.webhook.we.azure-automation.net/webhooks?token=ZHuMYFshghshhh5+fgshs+f5h5+65sh -Method POST
    if($response.StatusCode -eq 202) {
        Write-Host "waiting..."
        sleep 60   # Temps d’attente estimé pour le démarrage de la VM
        if (Test-NetConnection 10.17.22.15 -Port 3389 | ? { $_.TcpTestSucceeded } ) {
            C:\Users\User1\vm.rdp  # On lance le client RDP
        } else {
            cls
	    Write-Output "RDP 1" # Si test NetConnection échoue (pas de réponse)
            Send-MailMessage -To $To -From $From -Subject $subject -SmtpServer $SMTPServer -Body 'Erreur NetConnection Fail' -Credential $Credential  -Port $SMTPPort -UseSsl
	    sleep 15
        }
    } else {
        cls
	Write-Output "Erreur RDP (l'administrateur a recu un mail d'avertissement et va intervenir !)" # Si NetConnection code autre que 202
        Send-MailMessage -To $To -From $From -Subject $subject -SmtpServer $SMTPServer -Body 'Erreur NetConnection 40x ou 500x' -Credential $Credential  -Port $SMTPPort -UseSsl
	sleep 15
    }
} catch {
    cls
    Write-Output "Erreur VM (l'administrateur a recu un mail d'avertissement et va intervenir !)" # Erreur NetConnection code 40x ou 50x
    Send-MailMessage -To $To -From $From -Subject $subject -SmtpServer $SMTPServer -Body 'Erreur au lancement de la VM' -Credential $Credential  -Port $SMTPPort -UseSsl
    sleep 15
}

Ainsi, contrairement à une planification en début de journée, la VM ne sera lancée que quand l'utilisateur en a l'usage afin d'économiser sur la facture Azure. Au besoin on peut également faire un petit script pour l'éteindre.

Migration

Dans un autre contexte je dois migrer une VM Azure vers ESXi. Expérience à venir.

Sources

 

Home Assistant & SSL

Maintenant que votre serveur Home Assistant est en place, il va falloir le faire communiquer avec l'extérieur de la façon la plus sécurisée possible. Sur le principe on ouvre le port (TCP/8123 en général) sur le routeur et on redirige le flux vers le serveur local. Je ne vais pas vous faire un dessin, d'autres l'ont fait. Ensuite il va falloir d'occuper de la mise en place du certificat SSL

Pour faire court deux cas de figure se présentent.

Soit votre fournisseur d'accès vous met à disposition une IP fixe, soit non, et c’est hélas le cas de beaucoup. Certains comme Free fournissent maintenant une IP fixe partagée, dans ce cas il faut demander via l'interface une IP full range. Si vous disposez de votre propre nom de domaine, et ce n'est absolument pas obligatoire pour ce genre de site, par essence privé, on va privilégier Let's Encrypt.

  • IP Fixe + Nom de domaine : Add-on Let'sEncrypt.
  • IP Dhcp avec ou sans nom de domaine : Add-on DuckDNS

L'add-on Let's Encrypt

Il s'installe depuis le superviseur et peut s’appuyer sur les API d'OVH, Gandi et CloudFlare (et bien d'autres). Si tout est bien renseigné comme ci dessous par exemple, en quelques minutes vous obtenez votre certificat et le tour est joué.

email: [email protected]  # Pour Let's Encrypt
domains:
  - ha.domain.tld  #  Domaine don le DNS est chez CloudFlare
certfile: fullchain.pem
keyfile: privkey.pem
challenge: dns
dns:
  provider: dns-cloudflare
  cloudflare_email: [email protected]  # Votre compte Cloudflare
  cloudflare_api_token: vYffF-LzhdhdhdgfhjhjjFGJFDGJJGsfgFGJSgjf

L'add-on DuckDNS

Il s'installe depuis le superviseur et nécessitera la création d'un compte sur www.duckdns.org afin de préparer la configuration. L'avantage de cet add-on c'est qu'il va tenir informé DuckDNS de vos changements d'IP.

lets_encrypt:
  accept_terms: true
  certfile: fullchain.pem
  keyfile: privkey.pem
token: 952ddsfsdf5-b1sfse-43gfsdgsdfg81-10sgsgdsg2e
domains:
  - ha.domain.tld  # Le domaine choisit lors de la création du compte.
aliases: []
seconds: 300

Alternatives

Pourquoi se compliquer la vie ? Il y a qui aiment ça... Vous pouvez donc utiliser un autre service tout autant gratuit, par exemple...

  • La passerelle SSL d'OVH qui va jouer un rôle de reverse proxy également (à condition d'avoir une IP fixe,). Dans ce cas on a la choix localement d'utiliser un certificat auto signé soit pas de SSL.
  • L'intégration CloudFlare qui semble utilisable sans IP fixe. Voir également ici pour utiliser un certificat CloudFlare.

Reverse Proxy

Se protéger avec une reverse proxy est une très bonne idée. On peut bien sur installer en local NGINX (et le configurer...) ou utiliser un reverse proxy existant dans votre infrastructure (Synology, ou HA Proxy dans pfSense par exemple), mais le plus simple est de déléguer la chose à un reverse proxy CDN, je pense bien sur à CloudFlare, en utilisant ou pas leur certificats. Attention toutefois, CloudFlare n'autorise pas tous les ports, et notamment pas le 8123, il va donc falloir ruser, soit utiliser un port externe et un port interne, soit changer le port de Home Assistant.

Que vous utilisiez ou pas un reverse proxy, il faudra vous arranger pour localement outrepasser le reverse ou le port externe de votre routeur. Inutile en effet d'aller faire un tour sur Internet pour utiliser une requête locale. En plus en cas de panne internet ça ne marcherait plus. Pour ça il faut un DNS menteur local, ça peut être un fichier host sur un PC, un DNS local sous Linux ou Windows, mais je vous conseille plutôt d’installer l'excellent add-on AdGuard qui en plus de dépolluer vos pages web fera également ce travail grâce à sa fonction de réécriture DNS. Bien sur dans ce cas il faudra que votre DHCP local pointe sur ce DNS...

Exemple

Ici j'ai configuré de façon à se que l'accès externe passe par le reverse proxy de CloudFlare (sur le port 2096), ce qui protège mes IP, j'ai fait une redirection DNS locale via AdGuard et j'utilise une URL interne sur mon mobile ou mon accès local sur PC.

URL Home Assistant : https://ha.domain.tld:2096 (pour CloudFlare par exemple).

SSID : Mon SSID (c'est sert au client mobile à savoir s'il faut appeler l'URL interne).

URL Interne : https://ha.domain.tld:8123  (ce sera également l'url local depuis un PC.)

N'hésitez pas à commenter si vous avez des questions.

Bureau à distance Windows et imprimantes

En ces temps ou les télétravail s'impose, l'IT doit s'adapter et proposer des solutions à chaque cas. Beaucoup de salariés se sont retrouvés à bosser sur le PC familial au détriment des règles de sécurité les plus élémentaires. Sachant par ailleurs que le télétravail comporte un cadre réglementaire  qui impose la fourniture des équipement nécessaires par l'employeur ainsi qu'un défraiement pour les frais occasionnés.

Au delà de ces considérations, s'il est facile de travailler à distance sur des applications web ou sur des fichiers gérés dans le cloud avec des applications classiques du genre Office, la chose se complexifie dès lors que l'on travaille sur des applications lourdes qui nécessitent une installation spécifiques sur le poste de travail. Je pense ici par exemple à des application de compta ou de paie (Sage pour ne pas les citer, mais ils sont loin d'être les seuls) ou le simple fait de déplacer l'application d'un poste à un autre nécessite l'intervention d'un partenaire agréé pendant plusieurs heures, je ne pense pas trop me tromper en affirmant que la complexité est ici orchestrée dans le seul but de générer du revenu... Un vaste débat, un autre débat que je n'aborderais pas ici ou je vais me contenter de contourner le problème.

Au départ j'avais j'avais naïvement pensé mettre les fichiers de données sur un cloud synchronisé. Impossible car au fil du temps ces solutions reposent souvent sur du SQL même en local. Les éditeurs proposent eux de configurer un serveur, solution coûteuse et surdimensionnée dans les TPE ou il n'y a bien souvent qu'un seul utilisateur. Installer ce genre de solutions sur un laptop que le salarié risque de perdre ou de se faire voler n'est pas non plus une solution.

L'alternative consiste à se servir un d'un logiciel de prise de main à distance, genre TeamViewer ou AnyDesk, ou carrément d'une session RDP (Bureau à distance) qui offre un bien meilleur confort. Reste à s'affranchir de la lenteur possible du poste de travail distant et de sa connectivité pas toujours à la hauteur.

Il existe une alternative plus durable et plus fonctionnelle. Isoler nos logiciels lourds sur un serveur dans une session RDP. L'avantage est que cette session sera accessible par la ou le(s) personne(s) qui en ont besoin, ou qu'elle soient et tout en se basant sur une application mono poste (donc une seule licence et donc une seule utilisation concurrente). Cela permet également à des salariés disposant d'un Mac d'utiliser des applications Windows.

Dans la pratique ce serveur, qui peut être un Windows 10 ou un Windows Serveur, sera de préférence installé dans un espace sécurisé (data center, Azure, AWS, un VPS ou encore un serveur dédié). Sécurisé en terme d'accès, sécurisé physiquement (vol, incendie, etc...), mais également au niveau de sauvegardes.

On a deux choix possibles, soit on installe un Windows 10 Pro, soit un Windows Serveur. L'avantage du Windows Serveur étant qu'il permettra de base une session RDP pour l'administrateur et une autre pour le client. Les coûts ne sont pas identiques. De plus selon la taille de l'entreprise une solution Windows Serveur pourra évoluer avec RDS (et les licences qui vont avec) vers une solution multi-utilisateurs distants.

La pratique

Installer une instance Windows, une VM, sur une infrastructure se fait en quelques clics. Ensuite on crée une session pour le client avec des droits utilisateur, on lui permet l'accès distant et le tour est joué. Il n'y a plus qu'à lancer le client Bureau à distance sur le PC ou le Mac de utilisateur et ça fonctionne, en mode fenêtré ou plein écran selon la taille des on écran. De plus RDP est bien pus confortable au quotidien qu'une prise de main à distance. A partir de là on demande au prestataire Sage (ou autre) de venir y transférer ses logiciels dont lui seul possède le savoir faire nécessaire et le tour est joué.

Sauf que...

Les utilisateurs de ce genre de logiciels génèrent beaucoup d’impressions. C'est prévu, le client Bureau à distance (Windows et Mac, mais pas celui que l'on trouve sur le Windows Store) permet d'utiliser ses imprimantes locales qu'il redirige dans la session RDP. Si dans la pratique si ça peut fonctionner de façon transparente pour quelques imprimantes qui utilisent des pilotes standards fournis par Windows, dans la majorité des cas il faudra installer les pilotes des imprimantes du poste client sur le serveur, (sauf peut être si on installe RDS avec EasyPrint, ça reste à vérifier et je n'ai pas pris cette peine). Hélas il sera impossible d’installer directement certains pilotes sur le serveur, soit leur module d’installation ne peux fonctionner que sur Windows 10, soit parce qu’il nécessitent que l'imprimante soit en ligne. Il va donc falloir ruser un peu pour disposer des pilotes correspondant aux imprimantes de nos clients sur le serveur.

Il existe sous Windows (sauf la version Home) un outil de migration qui va permettre d'exporter les files d'impression d'un client ou serveur et de les réimporter en réinstallant les pilotes et les imprimantes sur la cible. On va donc lancer PrintBrmUi.exe sur le poste des clients et réimporter le fichier obtenu sur le serveur avec le même outil. Cette opération permettra également l’importation de pilotes 32 bits si ce genre de poste client existe toujours...

Attention cet outil n’est pas fait pour ça mais pour migrer des serveurs d’impression… donc il exporte / importe des files d’impression avec les drivers. Il faudra donc prendre soin de ne pas écraser les pilotes existants et ensuite d’aller faire le ménage dans les imprimantes déclarées sur le serveur mais inutiles car seule nous importe la présence des pilotes. Le plus simple étant bien sur de tout faire avec la console printmanagement.msc qui va nous permettre de visualiser les pilotes installés et de retirer les imprimantes inutiles tout en conservant les pilotes.

Sources

 

 

Home Assistant & ZigBee

ZigBee est un protocole de haut niveau permettant la communication d'équipements personnels ou domestiques équipés de petits émetteurs radios à faible consommation ; il est basé sur la norme IEEE 802.15.4 pour les réseaux à dimension personnelle (Wireless Personal Area Networks : WPAN). Wikipédia et Google vous raconteront le reste... Mais sous Home Assistant il y (principalement) trois façons d'approcher Zigbee :

Deconz / Phoscon

La clé Combee II dépend ici de l’environnement logiciel de son éditeur, qu'il soit installé de façon indépendante ou plus ou moins mergé à Home Assistant (ou une autre solution). C'est donc une une solution qui peut s'utiliser de façon autonome, ce qui était leur but de départ, remplacer simplement quelques passerelles propriétaires (Hue, Ikea, etc..). ils fournisse d'ailleurs des images RPI pour se passer de leur boitier et c'’est d'ailleurs ainsi que je l'avait monté sur un RPI2 auquel j'accédait depuis Jeedom, et auquel j'accède maintenant avec Home Assistant.

Le fait que ce soit autonome permet de réaliser des opérations sans passer par une plateforme domotique, tout en disposant des équipements sur une ou plusieurs solutions domotique. Par exemple, pour associer un interrupteur avec une ampoule, il est plus simple de le faire sous Phoscon, sachant que de toutes façons l'état de l'ampoule remontera dans Home Assistant et que je pourrais toujours l'éteindre avec une automation...

Enfin, les équipements sont également accessibles en parallèle depuis Alexa...

C’est donc la solution la plus proche d'une passerelle propriétaire, de ses avantages, de sa façon de fonctionner et d'évoluer. C’est celle que je conseillerais pour installer un HA chez un tiers, la plus grand public qui s'appuie sur des équipements l'ont peut facilement acquérir dans le commerce.

ZHA

Zigbee Home Automation est la solution la plus intégrée à Home Assistant. Elle s'appuie sur à peu près toutes les clés Zigbee du marché et fonctionne plutôt bien. Par contre ici on est sur une intégration fermée dans et pour HA. Elle supporte (à titre expérimental) même la Zigate dont j'avais fini par me débarrasser sous Jeedom...

Rien à ajouter de plus, pour faire simple et rester dans HA c’est la solution à adopter et l'on retrouvera les menus de configurations dans la configuration HA

zigbee2mqtt

C'est la solution à la mode, parce que MQTT est à la mode. Mais au delà de l'effet de mode, MQTT est le protocole qui fait son chemin et risque de devenir un standard bien au delà de nos petits bricolages domotique. Ici on passe par une passerelle que l'on va monter ou on le souhaite (RPI Zero indépendant, HA remote, etc...), qui va transmettre ses infos à un brooker qui lui aussi peut être n'importe ou (LAN/WAN) et notre HA ne sera qu'un client de ce brooker. L'étape suivante étant bien sur que les équipements intègrent ce protocole, ça arrive, notamment sur du DIY avec le firmware Tasmota et on peut imaginer que le marché adopte MQTT, c’est d'ailleurs le cas des équipements Shelly en WI-FI qui proposent (timidement) ce protocole de façon native, ou d'autres passerelles comme ble2mqtt...

Pour revenir à zigbee2mqtt le projet est très actif, plutot stable et c'est par exemple le seul qui propose les mises à jours bios de certains équipements. Pour ce test je l'ai monté sur un HA remote et j'ai utilisé une intégration sous forme d'addon. En fait simplement parce que j'avais ce HA remote sous la main, mais pour le coup la com ne se fait pas en remote mais en MQTT. C'est une solution ultra paramétrable, chaque équipement est ajustable à la source et les infos remonteront vers les clients, je dis bien les clients car on peut tout à fait imaginer qu'in équipement soit utilisable avec plusieurs solutions, un capteur de température vu par HA mais aussi par une solution externe, etc...

A noter que contrairement aux deux précédentes solution j'ai bien la remontée de l'état des piles sur les révisions récentes (fin 2019) des capteurs Aqara, ce n'est toujours pas les cas des deux solutions concurrentes, preuve de plus que le projet est très actif.

Enfin zigbee2mqtt s'appuie sur une clé DIY, ce qui veut dire qu'il faudra bricoler un peu et acheter quelques accessoires. Mais ça reste easy !

EDIT 22/01/2021 : Pensez à mettre à jour le firmware de vos clés, ça fonctionnera mieux avec les dernières versions...

Alternatives...

D'autres continuent à utiliser l'intégration Xiaomi avec la passerelle de première génération. Ça fonctionne, mais pour moi ce n'est pas une solution Zigbee, mais une solution Xiaomi qui repose sur l'utilisation d'un serveur Chinois, dépendant de la volonté de Xiaomi d'en laisser l'usage détourné possible, voire de la Chine de laisser passer ou non les flux. Si le fonctionnement se fait en local, c’est également un potentiel cheval de Troie, mais ça c'est un autre débat. Vous aurez compris que je ne suis pas fan.

EDIT 22/01/2021 : SLS, vous risquez d'en entendre parler cette année...

Conclusion

Conclusion il faut choisir. Choisir n'est jamais simple, et en l'espèce ce n'est pas moi qui vais vous dire quoi choisir ! Il faut tout de même savoir que si les produits les plus courants sont supportés par toutes les solutions, d'autres plus confidentiels ne le seront que par l'une ou l'autre.

Sources

 

Home Assistant et les coupures électriques

Pour surveiller des parties de mon réseau électrique, on va dire chaque rangées de mon tableau, j'utilisais auparavant des modules ITS23 en 433 Mhz. qui fonctionnait sur piles et envoyaient une alerte en cas de coupure. Exit le 433 Mhz, je cherche donc une solution alternative adaptée à Home Assistant, l'idée étant de recevoir un SMS si le différentiel qui alimente le congélateur tombe à cause d'un orage par exemple... Sachant que pour une coupure générale j'ai la remontée SNMP des onduleurs...

Vu que j'ai des modules Shelly un peu partout, je me suis dit que ce serait une bonne piste...

Ping

Avec l'intégration ping: il est facilement possible de surveiller et d'alerter. Hélas, il arrive parfois que ça fasse de faux positifs, peut être si le ping se fait à un moment ou le Shelly fait une maintenance ? Il doit y avoir moyen de consolider ça avec un template pour prendre en compte une période plus large de test. On crée un binary_sensor: et si off on envoie une notification avec une petite automation.

- platform: ping
  host: 192.168.210.31
  name: Switchboard 1
  count: 2

MQTT

On peut activer MQTT et ainsi avoir un retour du status online. Mais activer MQTT fait sauter le Cloud Shelly et devient impossible de piloter les équipements depuis Alexa... Ici aussi un binary_sensor: et si off on envoie une notification avec une petite automation.

- platform: mqtt
  name: "Shelly Plug 2"
  state_topic: "shellies/shellyplug-s-51xx67/relay/0"
  payload_on: "on"
  payload_off: "off"
  availability_topic: "shellies/shellyplug-s-51xx67/online"
  payload_available: "true"
  payload_not_available: "false"
  qos: 1
  device_class: power

Les attributs

Un Shelly, ou d'autres modules, retournent plusieurs attributs qui passeront à indisponible si le Shelly n'est plus alimenté. Reste à faire un template pour palier à une indisponibilité temporaire...

 
shelly_type: Shelly 1 PM
shelly_id: 76xx39
ip_address: 192.168.210.119
protocols:
  - CoAP-msg
  - poll
  - mDns
  - CoAP-discovery
over_temp: false
has_firmware_update: false
cloud_status: connected
switch: false
over_power: false
friendly_name: Shelly 1 PM - 76xx39
 
Ensuite on va bricoler un peu pour tester un des attributs, temporiser et notifier...

Le Graal !

Tout en cherchant je me disais qu'il devait bien y avoir quelque part une solution clé en main sous Home Assistant. J'ai commencé par trouver ce projet qui peut s'avérer très intéressant, mais c’est une brique en plus et je ne sais pas comment il sera maintenu. Et puis je suis tombé sur l'intégration alert: qui sera parfaite pour ce boulot !
tableau_1:
  name: Diff 1 is down
  entity_id: switch.shelly_1_fp
  state: 'unavailable'   # Optional, 'on' is the default value
  repeat:
    - 10
    - 30
    - 60
    - 300
  can_acknowledge: true  # Optional, default is true
  skip_first: true  # Optional, false is the default
  message: "{{ states.sensor.date_time.state}} > ALERTE | Différentiel 1 : OFF"
  done_message: "{{ states.sensor.date_time.state}} > ALERTE | Différentiel 1 : OK"
  notifiers:
    - slack_hass_canaletto
    - free_mobile
Je ne notifie pas tout de suite, j'attends 10 minutes et je répète ensuite quelquefois... Simple et efficace, on peut également s'en servir pour notifier autre chose, en combinaison avec un Template pour surveiller les piles ou pour garder un œil sur un capteurs capricieux...
 
 
 
 

 

 

Home Assistant & Google Agenda

Dans plusieurs articles je vous ai beaucoup parlé de planification. Simplement parce que quand on veut gérer du chauffage électrique c’est une composante obligatoire. J'avais d'abord reproché à HA de ne pas proposer un véritable agenda, j'ai écarté l'agenda Google pour ne pas être dépendant du net et j'ai ainsi pu constater qu'il était possible de définir ses propres planifications, du véritable sur mesure, interface comprise. C’est un peu lourd à faire, mais HA permet de faire absolument tout ce que l'on souhaite.

Et puis je me suis retrouvé sur une planification un peu plus complexe, et comme je suis un peu flemmard et que je voulais laisser à son utilisateur une grande part de liberté, je suis allé explorer Google Agenda. Et ma première surprise a été que cette intégration n'est pas totalement dépendante du net. En effet HA synchronise l'agenda et en conserve les entrées sur une durée configurable. Ce qui veut dire qu'il est possible d'envisager un cache local qui nous permettra une déconnexion plus ou moins longue du cloud. Donc dans ces conditions c'est jouable.

Préparation

Google Agenda sous HA fait partie des intégrations de base. Donc rien à ajouter si ce n'est une carte spécialisée (ou celle-ci) si l'on souhaite un visuel. Par contre il va falloir activer l'API Google Agenda sur la console développeur de votre compte Google comme indiqué sur l'intégration. Une fois que l'on a notre clé API et son secret on crée cette entrée dans le fichier de configuration.

google:
  client_id: !secret google_api_id
  client_secret: !secret google_api_secret
  track_new_calendar: true
google:
  client_id: !secret google_api_id
  client_secret: !secret google_api_secret
  track_new_calendar: true

Après avoir redémarré HA on doit se retrouver avec un fichier google_calendars.yaml qui contiendra lune entrée pour chaque calendriers. On pourra y ajouter une entré max_results: pour choisir le nombre de résultats synchronisés par calendrier qui par défaut est de 5.

- cal_id: [email protected]
  entities:
  - device_id: thermostat_bureau
    ignore_availability: true
    name: Thermostat (Bureau)
    track: true
    max_results: 7

Pour ma part j'ai fait le choix de créer un calendrier Google par thermostat, mais il est possible de les superposer, on peut également n'en utiliser qu'un seul, mais choisissez d'en créer un en plus de celui de base, même si aujourd'hui vous ne l'utilisez pas.

A partir de là on peut ajouter notre carte Lovelace et visualiser nos entrées. On notera le spider présent sur les événements en cours.

Maintenant il va falloir s'en servir pour des automations. Pour chaque événement on va disposer de plusieurs informations qui seront utilisables, soit directement, soit grâce à des templates :

message: Salle d'Eau
all_day: false
start_time: '2020-05-17 02:30:00'
end_time: '2020-05-17 03:00:00'
location: ''
description: '23'
offset_reached: false
friendly_name: Thermostat (SdB)

Ce qui va correspondre à :

Le message: correspond à l'entrée de l'agenda, on va pouvoir y coller plusieurs informations exploitables par un template, ou faire le choix plus esthétique de coller ces information dans le champs description: qui lui ne sera pas visible directement dans l'agenda Google ou sa réplique dans HA. Ici j'ai juste mis "23" pour indiquer que je vais passer mon thermostat à la température de consigne de 23°. Mais j'airais pu ajouter d'autres mots clé pour y associer d'autres actions à déclencher, voire utiliser un lieu, ce qui peut être intéressant pour des interactions liées à la géolocalisation. On est donc face à une solution souple et sans limites.

Exploitation

On va maintenant passer à l’exploitation en tant que trigger dans une automation. On peut le faire de façon simple avec le passage d'un état sur on ou sur off, ou plus complexe en décodant des informations liées grâce à un template, informations utilisables également en tant que condition ou pour des actions, voire pour créer un sensor: ou un binary-sensor: ce qui laisse pas mal de liberté en fonction des besoins et de l'imagination de chacun.

  trigger:
    platform: state
    entity_id: calendar.thermostat_andre
    to: 'on'

Ici par exemple je vérifie si une température est disponible dans le champ d'information avec un simple test > à 0, et dans le cas contraire j'applique la consigne globale définie dans un input_number:

  action:
  - service: climate.set_temperature
    entity_id: climate.thermostat_andre
    data_template:
      temperature: '{% if states.calendar.thermostat_andre.attributes.description | float > 0 %}
                      {{ states.calendar.thermostat_andre.attributes.description }}
                    {% else %}
                      {{ states.input_number.consigne_confort.state }} 
                    {% endif %}'

J'aurais bien entendu pu définir une autre option pour déclencher par exemple un ventilateur... seulement si la température est de de...

Restrictions

Il y a une chose que je n'ai pas vraiment résolue. Les jours fériés. J'aurais voulut appliquer la programmation du dimanche aux jours fériés. Pour ça sous HA on dispose de workday: qui fait très bien son job, mais sans y passer beaucoup de temps je n'ai pas trouvé une façon simple de le faire.

J'ai donc opté pour une solution de contournement. Il est très facile sous Google Agenda de superposer les calendriers, donc le plus simple est de superposer le calendrier des jours fériés à mes autres agendas et ainsi d'aller modifier manuellement les entrés répétés qui tombent sur un jour férié et bien sur de sauvegarder uniquement cette entrée. Le choix d'une tache manuelle à répéter chaque année en attendant de trouver mieux !

Inspirations

Une autre possibilité offerte par cette intégration nous permettre d'automatiser la création d’événement depuis Home Assistant. Pour quoi faire ? Pour l’heure je n'en sait rien, mais ça peut être une possibilité intéressante...

Alternatives

J'ai fait la même chose avec Microsoft 365. Ça fonctionne, un peu moins réactif, mais ça je pense que ça vient du composant. Par contre certains disent que l'API est moins stable, plus jeune, et nécessite plus souvent l'intervention des développeurs du composant. De toutes façons chaque dépendance extérieure affaiblit un système, quel qu'il soit.

Conclusion

Çà fait le job et même bien. Je regrette un peu de ne pas avoir testé avant, mais seulement un peu car faire tous ça à la main m'a permis de beaucoup explorer et d'apprendre plein de choses !

Edit

Il n'y a pas que Google Agenda dans la vie, allez voir ce projet de scheduler très prometteur...

Sources

 

Home Assistant & Energie

Jadis certains justifiaient l'achat d'un ordinateur personnel par la gestion des comptes familiaux, aujourd’hui d'autres viennent à la domotique en espérant faire des économies. Soyons sérieux, la domotique ça coûte de l'argent et il faut y venir dans un objectif de confort ! Il n'en reste pas moins que l'on peut s'amuser à calculer ce que l'on consomme, c’est ce que je vais vous montrer ici.

Home assistant dispose d'une fonctionnalité de base nommée utility_meter: Il s'agit en fait de compteurs qui fonctionnent en cycles (heure, jour, mois, trimestre, année), pour l'instant il manque le mode bi-mensuel et un mode paramétrable. L'idée est de se calquer sur une facturation, ou de créer une facturation, pour une location par exemple. Ce capteur suivra les valeurs d'un capteur source, réinitialisant automatiquement le compteur en fonction du cycle configuré. Lors de la réinitialisation, on stockera la valeur précédente du compteur, fournissant les moyens d'effectuer des opérations de comparaison (par exemple, "ai-je dépensé plus ou moins ce mois-ci?"), ou d'estimation de facturation. Certains fournisseurs de services ont des tarifs différents en fonction du temps / de la disponibilité des ressources / etc. Le compteur de services publics vous permet de définir les différents tarifs pris en charge par votre fournisseur. Lorsque les tarifs sont définis, une nouvelle entité apparaîtra indiquant le tarif actuel. Pour modifier le tarif, l'utilisateur doit appeler un service, généralement via une automatisation qui peut être basée sur le temps ou sur une autre source externe (par exemple un capteur REST).

Je n'ai pas trouvé beaucoup d'informations et j'y ai passé d'autant plus de temps que j'ai voulu l'adapter à mon abonnement électrique. Le plus simple ou j'ai deux tarifs, les Heures Pleines de 05:00 à 21:30, le reste étant des Heures Creuses (HC). Il est toutefois possible d'adapter facilement tout ça à des tarifs plus complexes comme Tempo ou EJP.

La première chose à faire est de décortiquer la facture EdF afin d'obtenir les valeurs de base. Pour cela chacun fera appel à Excel car au delà des tarifs fixes du kWh il existe un certain nombre de taxes locales et régionales qui font qu'en France personne ne paie vraiment son Electricité au même prix.

La liste des courses

Pour compter il va nous falloir un sensor: qui sache nous remonter cette information. J'ai pour ma part utilisé un Shelly EM, il propose deux comptages et on va ici utiliser uniquement le premier que j'ai branché sur l'arrivée EdF de la maison. Il y a plusieurs façons d'en extraire les informations (Intégration, MQTT, ...) ici je vais créer un sensor rest, simplement car c’est le premier exemple que j'ai trouvé et que c'est resté ainsi.

    - platform: rest
      name: Shelly EM EdF # Général
      resource: http://192.168.210.111/status
      # username: !secret shelly_usr
      # password: !secret shelly_pwd
      authentication: basic
      value_template: '{{ (value_json.emeters.0.total | float /1000)|round(3) }}' # Wh to kWh
      scan_interval: 300 # no point updating faster than this. The Shelly only updates every 5 minutes
      unit_of_measurement: kWh

Ensuite on va créer nos utility_meter:, dans mon ça j'en ai créé 5, ce qui va me permettre de gérer le jour, la semaine, le mois, le trimestre et l'année en cours. Ces compteurs se remettrons à zéro en début de cycle et si l'on souhaite traiter les informations au delà il conviendra d'en sauvegarder les valeurs dans un fichier extere (.CSV par exemple) ou une BDD.

utility_meter:
  energy_total_usage_daily:
    source: sensor.shelly_em_edf
    cycle: daily
    tariffs:
      - day
      - night

  energy_total_usage_weekly:
    source: sensor.shelly_em_edf
    cycle: weekly
    tariffs:
      - day
      - night

  energy_total_usage_monthly:
    source: sensor.shelly_em_edf
    cycle: monthly
    tariffs:
      - day
      - night

  energy_total_usage_quarterly:
    source: sensor.shelly_em_edf
    cycle: quarterly
    tariffs:
      - day
      - night

  energy_total_usage_yearly:
    source: sensor.shelly_em_edf
    cycle: yearly
    tariffs:
      - day
      - night

Comme vous le voyez j'ai créé deux tarifs, ne me demandez pas pourquoi je les ai nommés Jour et Nuit, mais en fait cela correspond à HP et HC que l'on retrouvera plus loin. Mais comme j'ai créé deux tarifs il va me falloir faire l'addition des deux comptages pour obtenir la consommation de la journée en kWH si je le souhaite. Dans l'absolu c’est inutile car le but est d'avoir des valeurs finales en Euros...

Pour faire tous nos calculs on va se servir de sensors et de templates, là ou systèmes auraient utilisé des scénarios et des variable. Bref, peu importe, ce qui compte c’est le résultat et que ce résultat soit dynamique. Voici donc de quoi obtenir la conso journalière en kWH dans un sensor, une simple addition qu'il conviendra de dupliquer pour les différentes périodes :

    - platform: template # Consommation journalière HP + HC
      sensors:
        energy_total_daily:
          friendly_name: 'Energie journalière consommée'
          entity_id:
            - sensor.energy_total_usage_daily_day
            - sensor.energy_total_usage_daily_night
          value_template: "{{ (states('sensor.energy_total_usage_daily_day')|float + states('sensor.energy_total_usage_daily_night')|float)|round(3) }}"
          unit_of_measurement: "kWh"

Ensuite on va créer d'autres sensor: afin de passer aux euros... Mais avant on va définir quelques input_number: afin d'y saisir les valeurs du prix du kWh et de l'abonnement au prorata des périodes HP et HC. Valeurs que l'on saisira dans une carte Lovelace que l'on verra plus loin.

  hp_daily_cost:
    name: HP - Cout journalier
    mode: box
    min: 0
    max: 100
    unit_of_measurement: "€/day"
    icon: mdi:currency-eur

  hp_energy_cost:
    name: HP - Coût du Kwh
    mode: box
    min: 0
    max: 100
    unit_of_measurement: "€/kWh"
    icon: mdi:currency-eur

  hc_daily_cost:
    name: HC - Cout journalier
    mode: box
    min: 0
    max: 100
    unit_of_measurement: "€/day"
    icon: mdi:currency-eur

  hc_energy_cost:
    name: HC - Coût du Kwh
    mode: box
    min: 0
    max: 100
    unit_of_measurement: "€/kWh"
    icon: mdi:currency-eur

Maintenant que l'on dispose de valeurs en kWh et de coûts on va pouvoir calculer ce que l'on consomme en HP et HC et additionner le tout pour obtenir une résultat journalier. Pour cela on va créer 3 sensor / template. On reste toujours sur la journée mais il faudra le dupliquer pour les autres cycles.

    - platform: template # Coût journalier HP
      sensors:
        hp_cost_today:
          friendly_name: 'HP Cost Today'
          entity_id:
            - sensor.energy_total_usage_daily_day
            - input_number.hp_daily_cost
            - input_number.hp_energy_cost
          value_template: "{{ (states('sensor.energy_total_usage_daily_day')|float * states('input_number.hp_energy_cost')|float + states('input_number.hp_daily_cost')|float)|round(2) }}"
          unit_of_measurement: "€"

    - platform: template # Coût journalier HC
      sensors:
        hc_cost_today:
          friendly_name: 'HC Cost Today'
          entity_id:
            - sensor.energy_total_usage_daily_night
            - input_number.hc_daily_cost
            - input_number.hc_energy_cost
          value_template: "{{ (states('sensor.energy_total_usage_daily_night')|float * states('input_number.hc_energy_cost')|float + states('input_number.hc_daily_cost')|float)|round(2) }}"
          unit_of_measurement: "€"

    - platform: template # Coût journalier HP + HC
      sensors:
        cost_today:
          friendly_name: "Aujourd'hui"
          entity_id:
            - sensor.hp_cost_today
            - sensor.hc_cost_today
          value_template: "{{ (states('sensor.hp_cost_today')|float + states('sensor.hc_cost_today')|float)|round(2) }}"
          unit_of_measurement: "€"

Je la fait un peu à l'envers, mais maintenant qu'on a nos bases de calculs on va avoir besoin de quelques automations, les deux premières vont nous servir à basculer nos UM entre HP et HC, un peu comme le faisait le fil signal fourni par EdF pour actionner le chauffe eau en heures creuses...

- id: set_night
  alias: 'Set Night'
  initial_state: true
  trigger:
    platform: time
    at: '21:30:00'
  action:
  - service: utility_meter.select_tariff ###### On passe en HC (nuit)
    data:
      entity_id:
      - utility_meter.energy_total_usage_daily
      - utility_meter.energy_total_usage_weekly
      - utility_meter.energy_total_usage_monthly
      - utility_meter.energy_total_usage_quarterly
      - utility_meter.energy_total_usage_yearly
      tariff: night

- id: set_day
  alias: 'Set Day'
  initial_state: true
  trigger:
    platform: time
    at: '05:00:00'
  action:
  - service: utility_meter.select_tariff ###### On passe en HP (jour)
    data:
      entity_id:
      - utility_meter.energy_total_usage_daily
      - utility_meter.energy_total_usage_weekly
      - utility_meter.energy_total_usage_monthly
      - utility_meter.energy_total_usage_quarterly
      - utility_meter.energy_total_usage_yearly
      tariff: day

Ensuite on va créer deux input_number: que l'on utilisera avec l'automation suivante pour conserver le coût cumulé et les coûts de la journée précédente. Encore une fois ce ne sont que des exemples, le reste étant à votre imagination...

  cumulative_energy_cost:
    name: Cumulative Energy Cost
    mode: box
    min: 0
    max: 5000
    unit_of_measurement: "€"
    icon: mdi:currency-eur

  yesterday_energy_cost:
    name: Yesterday Energy Cost
    mode: box
    min: 0
    max: 5000
    unit_of_measurement: "€"
    icon: mdi:currency-eur

Et comme un input_number: c’est pas joli sur Lovelace, on va leur associer des sensor:

    - platform: template # pour lovelace display à partir de l'input_number mis à jour à 23.59
      sensors:
        cost_yesterday_display:
          friendly_name: "Hier"
          entity_id:
            - input_number.yesterday_energy_cost
          value_template: "{{ (states('input_number.yesterday_energy_cost')|float)|round(2) }}"
          unit_of_measurement: "€"

L'automation finale que vous adapterez à vos envies et que l'on déclenchera en fin de journée va nous servir à plusieurs choses :

  • Stocker le cumul de consommation
  • Stocker le coût de la journée précédente
  • Envoyer une notification anxiogène sur ce que l'on vient de consommer...
  • Enregistrer un log avec toutes les valeurs possibles et imaginables...
- id: daily_energy_use_message
  alias: '000 - Daily Energy Use Message'
  trigger:
    platform: time
    at: '23:59:50'
  action:
  - service: input_number.set_value # Stocker le cumul de consommation...
    data_template:
      entity_id: input_number.cumulative_energy_cost
      value: "{{ states('input_number.cumulative_energy_cost')|float + states('sensor.cost_today')|float }}"

  - service: input_number.set_value # Stocker le coût de la journée précédente
    data_template:
      entity_id: input_number.yesterday_energy_cost
      value: "{{ states('sensor.cost_today')|float }}"

  - service: notify.slack_hass_canaletto
    data_template:
      title: '*Information*' # Envoyer un message en fin de journée...
      message: "La consommation de la journée écoulée est de {{ states('sensor.energy_total_daily') }} kWh., soit {{ states('sensor.cost_yesterday_display') }} €"

  - service: notify.file_notify_power # Ecrire un log exploitable dans un fichier .csv
    data_template:
      message: "{{ states('sensor.date') }},{{ states('sensor.energy_total_usage_daily_day') }},{{ states('sensor.energy_total_usage_daily_night') }},{{ states('sensor.energy_total_daily') }},{{ states('sensor.cost_today') }}"

Et ainsi recevoir un message du genre...

La consommation de la journée écoulée est de 25.561 kWh., soit 4.56 €

Voila pour le code. Maintenant on dispose de plein de nouveaux sensor: que l'on va pouvoir exploiter dans d'autres automations à imaginer, mais on va commencer par afficher ces informations sur une carte Lovelace et faire quelques graphiques... Comme c'est riche en formation j'ai choisi une carte qui se déplie, Fold-Entity-Row, et Multiple-Entity-Row pour compacter un peu les informations et dont voici le code :

entities:
  - entity: utility_meter.energy_total_usage_daily
    name: Tarif actuel
  - label: 'Coûts HP / HC :'
    type: section
  - entities:
      - entity: sensor.hp_cost_today
        name: HP
      - entity: sensor.hc_cost_today
        name: HC
    entity: sensor.cost_today
    icon: 'mdi:currency-eur'
    name: Journée en cours
    secondary_info: last-changed
    show_state: false
    type: 'custom:multiple-entity-row'
  - entities:
      - entity: sensor.hp_cost_weekly
        name: HP
      - entity: sensor.hc_cost_weekly
        name: HC
    entity: sensor.cost_weekly
    icon: 'mdi:currency-eur'
    name: Semaine en cours
    secondary_info: last-changed
    show_state: false
    type: 'custom:multiple-entity-row'
  - entities:
      - entity: sensor.hp_cost_monthly
        name: HP
      - entity: sensor.hc_cost_monthly
        name: HC
    entity: sensor.cost_monthly
    icon: 'mdi:currency-eur'
    name: Mois en cours
    secondary_info: last-changed
    show_state: false
    type: 'custom:multiple-entity-row'
  - entities:
      - entity: sensor.hp_cost_quarterly
        name: HP
      - entity: sensor.hc_cost_quarterly
        name: HC
    entity: sensor.cost_quarterly
    icon: 'mdi:currency-eur'
    name: Trimestre en cours
    secondary_info: last-changed
    show_state: false
    type: 'custom:multiple-entity-row'
  - entities:
      - entity: sensor.hp_cost_yearly
        name: HP
      - entity: sensor.hc_cost_yearly
        name: HC
    entity: sensor.cost_yearly
    icon: 'mdi:currency-eur'
    name: Année en cours
    secondary_info: last-changed
    show_state: false
    toggle: true
    type: 'custom:multiple-entity-row'
  - label: 'Coût cummulé :'
    type: section
  - entities:
      - entity: sensor.cost_today
        name: Aujourd'hui
      - entity: sensor.cost_yesterday_display
        name: Hier
    entity: sensor.cost_yearly
    icon: 'mdi:currency-eur'
    name: Journées
    secondary_info: last-changed
    show_state: false
    toggle: true
    type: 'custom:multiple-entity-row'
  - label: 'Coût :'
    type: section
  - entity: sensor.cost_yearly
    icon: 'mdi:currency-eur'
  - entities:
      - entity: input_number.hp_daily_cost
      - entity: input_number.hp_energy_cost
      - entity: input_number.hc_daily_cost
      - entity: input_number.hc_energy_cost
    head:
      label: Tarifs EDF
      type: section
    padding: 0
    type: 'custom:fold-entity-row'
  - entities:
      - entity: utility_meter.energy_total_usage_daily
        name: Tarif actuel
      - label: Journée en cours
        type: section
      - entity: sensor.energy_total_usage_daily_day
        name: Tarif HP
      - entity: sensor.energy_total_usage_daily_night
        name: Tarif HC
      - entity: sensor.energy_total_daily
        name: Total
      - label: Semaine en cours
        type: section
      - entity: sensor.energy_total_usage_weekly_day
        name: Tarif HP
      - entity: sensor.energy_total_usage_weekly_night
        name: Tarif HC
      - entity: sensor.energy_total_weekly
        name: Total
      - label: Mois en cours
        type: section
      - entity: sensor.energy_total_usage_monthly_day
        name: Tarif HP
      - entity: sensor.energy_total_usage_monthly_night
        name: Tarif HC
      - entity: sensor.energy_total_monthly
        name: Total
      - label: Trimestre en cours
        type: section
      - entity: sensor.energy_total_usage_quarterly_day
        name: Tarif HP
      - entity: sensor.energy_total_usage_quarterly_night
        name: Tarif HC
      - entity: sensor.energy_total_quarterly
        name: Total
      - label: Année en cours
        type: section
      - entity: sensor.energy_total_usage_yearly_day
        name: Tarif HP
      - entity: sensor.energy_total_usage_yearly_night
        name: Tarfif HC
      - entity: sensor.energy_total_yearly
        name: Total
      - type: divider
      - entity: input_number.cumulative_energy_cost
      - entity: input_number.yesterday_energy_cost
    head:
      label: Compteurs d'énergie
      type: section
    padding: 0
    type: 'custom:fold-entity-row'
  - entities:
      - entity: sensor.shelly_shem_0560de_1_current_consumption
        name: 'Maison : Consommation instantanée'
      - entity: sensor.shelly_shem_0560de_1_total_consumption
        name: 'Maison : Consommation cummulée'
    head:
      label: Equipements surveillés
      type: section
    padding: 0
    type: 'custom:fold-entity-row'
show_header_toggle: false
style: |
  ha-card {
    border: solid 2px var(--primary-color);
  }
theme: teal
title: Consommation électrique
type: entities

Encore une fois c'est a adapter à vos besoins. Coté graphiques je n'ai rien inventé non plus, j'ai juste adapté ce que j'ai trouvé en me servant de Mini-Graph-Card. Pour les graphiques suivants je suis allé chercher les informations MQTT après avoir configuré le Shelly EM en MQTT vers mon broker. Mais c’est juste pour l'exemple car j'aurais tout aussi bien put utiliser les valeurs du sensor: créé par l'intégration Shelly...

    - platform: mqtt
      name: "Shelly EM : Arrivée EdF"
      state_topic: "shellies/shellyem-0560DE/emeter/0/power"
      value_template: '{{ value|round(1) }}'
      qos: 1
      unit_of_measurement: "W"
      icon: mdi:gauge

    - platform: mqtt
      name: "Shelly EM : Baie Informatique"
      state_topic: "shellies/shellyem-0560DE/emeter/1/power"
      value_template: '{{ value|round(1) }}'
      qos: 1
      unit_of_measurement: "W"
      icon: mdi:gauge

    - platform: mqtt
      name: "Shelly EM : Voltage"
      state_topic: "shellies/shellyem-0560DE/emeter/0/voltage"
      qos: 1
      unit_of_measurement: "V"
      icon: mdi:flash

Pour mettre en évidence les heures creuses, on se fait un petit binary_sensor: dédié (on pourrait s'en servir dans l'automation plus haut...).

- platform: tod
  name: Heures Creuses
  after: '21:30'
  before: '05:00'

Et pour terminer, le code de la carte qui va générer mes 3 graphiques. C’est un peu compliqué car je suis parti d'un exemple ou l'auteur, bien plus chevronné que moi s'est servi de la luminosité en temps réel pour générer des valeurs Jour et Nuit afin de calculer sa productivité en énergie solaire.

cards:
  - color_thresholds:
      - color: '#039BE5'
        value: 0
      - color: '#0da035'
        value: 600
      - color: '#e0b400'
        value: 1200
      - color: '#e45e65'
        value: 2400
    color_thresholds_transition: hard
    entities:
      - sensor.shelly_em_arrivee_edf
      - color: 'rgba(0,0,255,1)'
        entity: binary_sensor.dark_outside
        show_line: false
        y_axis: secondary
    group: false
    hour24: true
    hours_to_show: 24
    line_width: 4
    name: Total
    points_per_hour: 4
    show:
      extrema: true
      fill: fade
      icon: true
      labels: false
      name: true
      state: true
    state_map:
      - label: Day
        value: 'off'
      - label: Night
        value: 'on'
    style: |
      ha-card {
        border: solid 2px var(--primary-color);
      }
    type: 'custom:mini-graph-card'
  - color_thresholds:
      - color: '#039BE5'
        value: 0
      - color: '#0da035'
        value: 600
      - color: '#e0b400'
        value: 1200
      - color: '#e45e65'
        value: 2400
    color_thresholds_transition: hard
    entities:
      - sensor.shelly_em_baie_informatique
      - color: 'rgba(0,0,255,1)'
        entity: binary_sensor.dark_outside
        show_line: false
        y_axis: secondary
    group: false
    hour24: true
    hours_to_show: 24
    line_width: 4
    name: Baie Informatique
    points_per_hour: 4
    show:
      extrema: true
      fill: fade
      icon: true
      labels: false
      name: true
      state: true
    state_map:
      - label: Day
        value: 'off'
      - label: Night
        value: 'on'
    style: |
      ha-card {
        border: solid 2px var(--primary-color);
      }
    type: 'custom:mini-graph-card'
  - color_thresholds:
      - color: '#039BE5'
        value: 0
      - color: '#0da035'
        value: 600
      - color: '#e0b400'
        value: 1200
      - color: '#e45e65'
        value: 2400
    color_thresholds_transition: hard
    entities:
      - sensor.shelly_em_voltage
      - color: 'rgba(0,0,255,1)'
        entity: binary_sensor.dark_outside
        show_line: false
        y_axis: secondary
    group: false
    hour24: true
    hours_to_show: 24
    line_width: 4
    points_per_hour: 4
    show:
      extrema: true
      fill: fade
      icon: true
      labels: false
      name: true
      state: true
    state_map:
      - label: Day
        value: 'off'
      - label: Night
        value: 'on'
    style: |
      ha-card {
        border: solid 2px var(--primary-color);
      }
    type: 'custom:mini-graph-card'
type: vertical-stack

Voilà, à vous de jouer !

Bonus

Suite à une demande : Extraction en .csv et envoie par mail.

automation:
- id: '1901208243990'
  alias: 'Energy - Sensor values to file - Day'
  description: ''
  trigger:
  - platform: time 
    at: '23:59:55'     
  condition: []
  action:
  - service: notify.file_csv_month
    data_template:
      message: '{{now().strftime("%d.%m.%Y")}};{{now().strftime("%H:%M:%S")}};{{ states.sensor.linky_3a99d205_smartenergy_metering_summation_delivered.state}};kW/h'
  mode: single

- id: '1708208243990'
  alias: 'Energy - Sensor values to file - Mouth'
  description: ''
  trigger:
  - platform: time 
    at: '23:59:55'     
  condition:
  - condition: state
    entity_id: binary_sensor.dernier_jour_du_mois
    state: 'on'  
  action:
  - service: notify.file_csv_day
    data_template:
      message: '{{now().strftime("%d.%m.%Y")}};{{now().strftime("%H:%M:%S")}};{{ states.sensor.linky_3a99d205_smartenergy_metering_summation_delivered.state}};kW/h'
  - service: notify.mail
    # data_template:
    data:
      target: 
        - [email protected]
      message: 'Linky {{ now().strftime(''%H:%M:%S'')}}'
      title: Mise à jour Linky
      data:
        images:
          - '/share/linky_month.csv'

        html: >
          <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
          <html lang="en" xmlns="http://www.w3.org/1999/xhtml">
              <head>
                  <meta charset="UTF-8">
                  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
                  <meta name="viewport" content="width=device-width, initial-scale=1.0">
                  <title>Intruder alert</title>
                  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.5/css/bootstrap.min.css">
                  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css">
                  <style type="text/css">
                      @font-face {
                        font-family: 'Open Sans';
                        font-style: normal;
                        font-weight: 300;
                        src: local('Open Sans Light'), local('OpenSans-Light'), url(http://fonts.gstatic.com/s/opensans/v13/DXI1ORHCpsQm3Vp6mXoaTZS3E-kSBmtLoNJPDtbj2Pk.ttf) format('truetype');
                      }
                      h1,h2,h3,h4,h5,h6 {
                          font-family:'Open Sans',Arial,sans-serif;
                          font-weight:400;
                          margin:10px 0
                      }
                  </style>
              </head>
              <body>
                <div class="jumbotron jumbotron-fluid" style="background-color: white; color: blue;">
                    <div class="container py-0">
                        <h3>Mise à jour mensuelle : \\192.168.66.150\share
                        <br>
                        Pour plus de détails : https://ha.xx.xx:8123/energy</h3>
                    </div>
                </div>
              </body>
              <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
              <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.5/js/bootstrap.min.js"></script>
          </html>
  mode: single                  

Sources

 

 

De ISA/TMG à pfsense / HA Proxy

Il y a 20 ans Microsoft s’était mis en tête de proposer un firewall / reverse proxy maison. A l’époque on ne parlait pas encore de cloud à toutes les sauces, mais juste de permettre aux itinérants de se connecter aux applications d’entreprise, quelques une en web, mais surtout des applications lourdes via le VPN maison (PPTP). Mais surtout Microsoft a réussit à imposer ISA Serveur à beaucoup de ses clients disposant de serveurs Exchange, qui succédait alors à MS Mail, en saupoudrant le tout d’un peu de complexité plus ou moins utile et en imposant de coûteux certificats SAN. Ainsi nous avons eu ISA 2000, ISA 2004, ISA 2006, au passage Microsoft a même produit des appliances avec HP pour terminer par TMG 2010 qui était la dernière itération de la série.

Et après ? Et bien Microsoft a (quasiment) du jour au lendemain décidé que la sécurité d’accès ce n’était pas leur job et en gros ils ont planté là tout le monde. Démerdez vous. Bien sur d’autres éditeurs et constructeurs ont pris le relais (Kemp par exemple) mais la transition n’a pas été facile pour tout le monde, principalement pour des questions de coût. Résultat on trouve encore aujourd’hui des installations basées, dans le meilleur des cas, sur la dernière itération du produit Microsoft, qui dans le meilleur des cas tourne sur un Windows 2008R2 qui comme toute la famille W7 ne reçoit plus de mises à jour, sans passer à la caisse.

Il arrive un moment ou il faut se résoudre à détruire ces vieilleries et se tourner vers quelque chose de plus moderne, et ce d'autant plus qu'on parle ici de sécurité, en gros la porte d'entrée numérique de l'entreprise. Si les grands comptes ont les moyens de leur sécurité en s’offrant des pare-feu et autres équilibreurs de charge à plusieurs milliers d’Euros, voire dizaine de millier d’euros, les PME, quand elles sont malines, peuvent se tourner vers l’open source.

Après avoir un peu cherché, il existe plusieurs pistes, j’ai choisit une base pfsense (on peut partir aussi sur OPNsense) avec HA Proxy comme composant reverse proxy. On peut également utiliser Squid, l’avantage de HA Proxy étant qu’il s’agit d’un bon équilibreur de charge et qu'il sera également possible de s'en servir pour publier d'autres sites sur une ou plusieurs IP publiques. L’autre avantage de pfsense est le support total de Acme et qu’il est ainsi possible de générer automatiquement un certificat SAN nécessaire à Exchange.

En version eco point d’appliance pfsense, je l’ai donc installé sur une VM ESXi selon les recommandation de l’éditeur, avec un WAN et un LAN. Je ne vais pas vous décrire, c’est très bien expliqué ici.

Une fois notre pfsense installé on va lui ajouter quelques packages en allant dans System/Packages ou est disponible tout un catalogue de composant additionnels. Encore une fois la règle est la même, moins on installe de choses, mieux on se porte...

  • Open VMWare Tools : pour faire le lien avec l'hyperviseur
  • Acme : pour générer des certificats SSL Let'Encrypt en se basant sur les API des DNS (chez nous Gandi ou OVH par exemple).
  • HA Proxy : car on est venu pour lui !

Je passe sur Acme qui est assez intuitif et on va voir comment publier un serveur Exchange. Dans cet exemple je l'ai fait sans répartition de charge, mais il est tout à fait possible de publier un (ou plusieurs) FrontEND et coté BackEND répartir la charge sur plusieurs serveurs Exchange selon des règles à définir, soit en spécialisant les serveurs (EAS, Mapi, etc...) soit en hiérarchisant leur importance et leur besoin de disponibilité en fonction du type d'utilisateurs (petit peuple, VIP, etc...). Ce ne sont que des exemples et les serveurs peuvent êtres (ou pas) multipliés (plusieurs serveurs, plusieurs hyperviseurs, plusieurs SAN, etc...) selon la sensibilité de l'infrastructure.

Alors vous allez me dire, pourquoi encore maintenir des serveurs Exchange à l'heure du cloud ? Si pour de petites structures ça n'a pas trop de sens, pour certaines entreprise sensibles il peut s'agit d'une question de confidentialité qu'aucun cloud ne pourra offrir, et pour d'autres simplement pour une question de coût, car même en additionnant le coût des licences (prohibitif, genre 80% d'un projet), le matériel, l'infrastructure, l’exploitation et de bons consultants, au delà d'un certain seuil c'est très rentable par rapport à des licences 365 à 15 € / mois.

Mais au lieu de polémiquer, revenons à notre HA Proxy...

HA Proxy se décompose en deux parties principales. Le FrontEND, c'est ce qui va recevoir les connections entrantes, et le BackEND ou seront connecté les serveurs internes et ou on redirigera les flux après les avoir isolés. Je dis bien isolés car ici on ne fait pas de routage mais du proxy. D'ailleurs pour s'en rendre compte, le serveur Exchange n'a pas besoin d'avoir le serveur pfsense / HA Proxy en passerelle par défaut...

Dans les paramètres généraux on déterminera le nombre de connexions maximum, ce qui a une incidence directe sur l'occupation mémoire, mais également un serveur SysLog si on veut deboguer un peu la chose...

Sur le FrontEND on va configurer une entrée en utilisant une des IP publique disponibles et on va la configurer en type http/https (offloading). Logique et important.

En option et pour plus de sécurité on va fixer des ACL sur les chemins utilisés par Exchange. Çà fonctionne bien sur sans, mais c’est une sécurité supplémentaire recommandable. C'est également la possibilité de rediriger des chemins (path), donc des services vers des serveurs différents via plusieurs BackEND, par exemple le web mail /OWA vers un serveur et les mobiles /Microsoft-Server-ActiveSync vers un ou plusieurs autres serveurs via un second BackEND.

Sur cette même page de configuration au chapitre SSL Offloading on choisira notre certificat (préalablement créé avec Acme ou simplement importé) et surtout on cochera la bonne case afin de restreindre les requêtes aux seul domaines et sous domaines déclarés dans en Subjet Alternative Name de notre certificat SAN, cela nous évite d'avoir à déclarer d'autres ACL plus haut ou en des entrées en SNI.

Il est bien sur possible d'ajouter d'autres sécurités, comme des certificats client, mais comme je ne bosse pas pour un service secret je me suis abstenu.

Passons maintenant au BackEND. On va ici définir le ou les serveur(s) qui seront utilisés pour nos différents services. En l’occurrence je n'en ai qu'un seul sans quoi j'aurais créé plusieurs entrées ou plusieurs BackEND si je souhaitait répartir les services.

Je vais identifier mon serveur par son adresse IP et son port. Ensuite je vais choisir si ce serveur répond en SSL et s'il est nécessaire de vérifier la validité du certificat. Exchange répond en SSL, mais s'agissant de trafic interne je pourrais très bien à l'avenir choisir de ne pas vérifier la validité de son certificat ou utiliser un certificat ausigné. Le poids n'est à renseigner que si on a plusieurs serveurs en BackEND à équilibrer.

Pour d'autres types de serveurs web je peux également y accéder seulement en HTTP en interne, sachant que le client externe lui le verra en HTTPS. C'est un choix lié au niveau de sécurité de l'infrastructure, mais dans bien des cas on ne se préoccupe pas de chiffrer l'interne.

Le cas échéant on réglera plus loin la méthode d'équilibrage, un Timeout à passer 10000 pour Exchange, une méthode pour tester la disponibilité des serveurs et si l'on souhaite ou pas des statistiques, statistiques très utiles pour valider une répartition / équilibrage de charge.


Le FrontEND


Le BackEND

Voilà, à partir de là on a quelque chose de fonctionnel pour tous les ports HTTPS. Il va nous reste à traiter les ports TCP nécessaires au fonctionnement d'un serveur de messagerie (SMTP, POP, IMAP). Si POP et IMAP sont peu en vogue dans les entreprises qui utilisent Exchange, le port SMTP (25) sera nécessaire pour le trafic entrant. Cependant Exchange assurant un service minimum au niveau de la protection certains choisiront de faire transiter le trafic entrant par une passerelle de sécurité (Anti Spam, Anti Virus, etc...).

Dans l'absolu il est possible d'utiliser HA Proxy pour répartir la charge du trafic TCP en configurant un FrontEND TCP et en lui associant un ou plusieurs BackEND.

J'ai du louper quelque chose car le trafic sur ces ports n'était pas stable, alors que d'autres l'ont fait avec de bons résultats. J'ai donc fait un rétropédalage rapide sur ce point et pour l’instant je me suis contenté de faire du NAT classique, donc sans possibilité de répartition de charge. A suivre prochainement.

DNS public

Pour ce qui est du DNS public j'ai fait le choix le plus simple consistant à utiliser uniquement mon nom de domaine. D'autres feront le choix d'utiliser un sous domaine du genre mail.domaine.tld...

@ 3600 IN A 55.235.45.30
@ 3600 IN MX 10 domaine.tld. 
@ 3600 IN TXT "v=spf1 mx mx:domaine.tls~all" 
_autodiscover 3600 IN SRV 10 10 443 domaine.tld. 
autodiscover 3600 IN CNAME  domaine.tld.

Il ne reste plus qu'à tester en configurant les trois types de clients utilisant HTTPS :

  • Un client Outlook sur Mac ou PC sous MAPI qui va utiliser le processus AutoDiscovery pour trouver son serveur en se basant uniquement sur l'adresse mail de l'utilisateur.
  • Un client mobile IOS ou Android qui utilisera ActiveSync (EAS) et AutoDiscovery pour s'y retrouver. A noter que l'application Courrier proposée sur Windows 10 utilise ce même protocole.
  • Et enfin un client web mail en pointant sur domaine.tld/owa

Si tout se passe bien on valide le SMTP entrant / sortant, éventuellement IMAP et POP et il est possible de facturer le client.

Sources

 

Home Assistant & Planification

ARTICLE EN COURS d'ECRITURE, N’HÉSITEZ PAS A REVENIR...

 

Comme je l'ai déjà dit une des forces de Jeedom était son agenda, ici il n'y en a pas pour l'instant. Il existe bien Schedy mais j'ai le choix de ne pas y recourir. J'avais commencé à ébaucher la chose dans l'article sur le chauffage, on va continuer ici de façon plus poussée, sachant que si je poursuit avec mes thermostats, cette partie pourra s'appliquer à d'autres choses comme l’arrosage ou la gestion d'une piscine par exemple.

Si on utilise Home Assistant tout seul et que l'on maîtrise son code, dans l'absolu il n'y a pas besoin de ce que je vais expliquer. Par contre si on déploie pour un tiers, ou si l'ont veut que toute la famille puisse ajuster le fonctionnement, il vaut mieux penser à une interface graphique.

J'ai donc commencé à dessiner cette interface et ensuite je me suis attaqué au codage correspondant. Dans ce projet j'ai plusieurs thermostats à gérer, il suffira simplement de dupliquer le premier ainsi que toutes les ressources qui lui sont nécessaire. Je vous conseille de bien finaliser le premier thermostat, de tester dans tous les sens avant de de dupliquer.

Dans le cas présent je vais travailler sur le HA de mon frère afin de remplacer son vieux Jeedom, il me faut donc à minima que le résultat final ne souffre d'aucune régression et si possible que ça apporte plus de confort et de facilité d'usage. Par exemple sous Jeedom il devait aller changer ses consignes manuellement, jouer avec l'agenda, facile pour moi mais pas vraiment à la portée de l'utilisateur final. L'objectif ici c’est de lui limiter l'accès, mais qu'il puisse pour autant faire tout ce qui a été défini lui même. En plus il m'a collé un challenge, faire en sorte que sa salle de bain soit à la bonne température quand son réveil sonne. Sauf que son réveil est un EchoShow 5 avec Alexa, qu'il change l'heure avant de se coucher et qu'il n’est pas question de payer pour la passerelle Alexa officielle... A Suivre !

On ne parlera pas ici de capteur de température, ni d'actionneur, ça a déjà été évoqué dans d'autres articles.

On va gérer 3 journée type, les jours de semaine, le samedi car il travaille le samedi matin, et le dimanche ou jour férié. L’application des conditions utilisera le sensor Workday afin de pouvoir affecter dynamiquement les jours fériés.

Liste des courses

    • des input_datetime: un START un STOP afin de définir le début et la fin d'une plage horaire.
      # input_datetime: SEMAINE - THERMOSTAT SEJOUR
       sejour_start_semaine_matin:
       has_date: false
       has_time: true
       sejour_stop_semaine_matin:
       has_date: false
       has_time: true
       sejour_start_semaine_midi:
       has_date: false
       has_time: true
       sejour_stop_semaine_midi:
       has_date: false
       has_time: true
       sejour_start_semaine_soir:
       has_date: false
       has_time: true
       sejour_stop_semaine_soir:
       has_date: false
       has_time: true
      ...
    • des input_number: un par consigne de température. Afin de simplifier je suis parti sur des consignes globales, mais il est tout à fait possible d'avoir des consignes indépendantes pour chaque thermostat à gérer. Je n'ai pas de Hors Gel car celui ci est géré statiquement au niveau de chaque thermostat. J'ai ajouté Confort+ et Boost, le mode Boost servira essentiellement à surchauffer la salle de bain, comme on le verra plus loin.
      consigne_confort_plus:
       name: Confort +
       min: 14
       max: 24
       step: 0.1
       unit_of_measurement: °C
      ...
    • des input_boolean: un par plage horaire, ils serviront de drapeau pour chaque plage horaire si on souhaite activer la consigne Confort+ ou Boost pour une plage spécifique.
      boost_sejour_semaine_matin:
      boost_sejour_semaine_midi:
      boost_sejour_semaine_soir:
      ...
    • des binary_sensor: un par plage horaire, que je vais utiliser sur les conseils d'un collègue afin de convertir une heure de début de plage horaire, les utiliser en trigger: et récupérer celui qui est la source du déclenchement afin d'y affecter un flag pour activer le mode Confort+ ou Boost à une plage spécifique. On trouve également ici workday: pour gérer les jours fériés.
      - platform: template
        sensors:
          sejour_start_semaine_matin:
          friendly_name: 'Séjour : Start Semaine Matin'
          delay_off:
            minutes: 2
          value_template: >-
            {%- if states.sensor.time.state == states.input_datetime.sejour_start_semaine_matin.state[0:5] %}
              true
            {% else %}
             false
           {%- endif %}
      ...
      - platform: workday
        country: FRA
        workdays: [mon, tue, wed, thu, fri]
        excludes: [sun, holiday]
        add_holidays:
        - '2020-02-24'
      
    • des sensor: en fait surtout time_date: que l'on va utiliser pour comparer la date et l'heure courante avec nos interactions. J'y ai ajouté Season pour éviter de chauffer en été (...joke).

      - platform: time_date
        display_options:
        - 'time'
        - 'date'
        - 'date_time'
        - 'date_time_utc'
        - 'date_time_iso'
        - 'time_date'
        - 'time_utc'
        - 'beat'
      - platform: season

Définitions des cartes

On commence par le code de la carte entities:. Ce thermostat gère 3 convecteurs dans une pièce ouverte. La carte reprend également les détecteurs d'ouverture qui serviront à désactiver le thermostat quand on aère... (j'ai volontairement coupé le code sur la partie semaine).

entities:
  - entity: climate.thermostat_sejour
  - entity: switch.ipx800_1_sejour
  - entity: switch.ipx800_5_cuisine
  - entity: switch.ipx800_4_hall
  - entity: binary_sensor.window_door_sensor
  - label: SEMAINE
    type: section
  - entities:
      - entity: input_boolean.boost_sejour_semaine_matin
        name: false
        toggle: true
      - entity: input_datetime.sejour_start_semaine_matin
        name: false
      - entity: input_datetime.sejour_stop_semaine_matin
        name: false
    entity: input_fake
    name: Matin
    show_state: false
    type: 'custom:multiple-entity-row'
  - entities:
      - entity: input_boolean.boost_sejour_semaine_midi
        name: false
        toggle: true
      - entity: input_datetime.sejour_start_semaine_midi
        name: false
      - entity: input_datetime.sejour_stop_semaine_midi
        name: false
    entity: input_fake
    name: Midi
    show_state: false
    type: 'custom:multiple-entity-row'
  - entities:
      - entity: input_boolean.boost_sejour_semaine_soir
        name: false
        toggle: true
      - entity: input_datetime.sejour_start_semaine_soir
        name: false
      - entity: input_datetime.sejour_stop_semaine_soir
        name: false
    entity: input_fake
    name: Soir
    show_state: false
    type: 'custom:multiple-entity-row'
show_header_toggle: false
state_color: true
theme: teal
title: Thermostat Séjour
type: entities

Automation

Pour gérer l'ensemble des plages (3 ici) d'une journée type il nous faut 1 automation START et une automation STOP. On pourrait bien sur utiliser un delay:, mais ce n'est vraiment pas conseillé car ça obligerait l'automation à rester active et ça résisterait mal à une coupure / redémarrage (encore que...). J'aurais pu choisir de gérer toutes les journées type dans une même automation, mais ça voudrait dire de se passer des conditions: pour tout gréer dans un monstrueux template: dans action:. Simplifions sans complexifier.

Automation Start Confort Séjour Semaine :

  • Un bloc trigger: avec 3 binary_sensor:
  • Un bloc condition: en mode and avec :
    • Un input_boolean: qui va déterminer si le chauffage est ON ou OFF dans la maison.
    • Un input_boolean: qui va déterminer le mode absent (auquel cas les thermostats sont en mode hors gel et on ne poursuit pas). Le mode absent peut être géré manuellement ou par une règle liée de géolocalisation.
    • Une condition time: pour poursuivre uniquement les jours de semaine dans le cas de cette journée type.
    • Une condition workday: afin de s'assurer que l'on n'est pas en présence d'un jour férié.
  • Un bloc action avec :
    • Un sevice: basé sur un data_emplate: pour affecter la bonne consigne de température de Confort ou Confort+ si la plage horaire comporte ce flag.
    • Un notify: basé sur un data_emplate: de notification que je redirige vers un canal Slack et qui constituera le journal de mon thermostat.
- alias: '100 : START CONFORT Séjour Semaine'
  description: 'Confort et Confort+ Séjour Semaine sauf jours fériés'
  trigger:
    - entity_id: binary_sensor.sejour_start_semaine_matin
      platform: state
      to: 'on'
    - entity_id: binary_sensor.sejour_start_semaine_midi
      platform: state
      to: 'on'
    - entity_id: binary_sensor.sejour_start_semaine_soir
      platform: state
      to: 'on'

  condition:
    condition: and
    conditions:
      - condition: state
        entity_id: input_boolean.thermostats_on_off
        state: 'on'
      - condition: state
        entity_id: input_boolean.thermostats_away
        state: 'off'

      - condition: time
        weekday:
          - mon
          - tue
          - wed
          - thu
          - fri

      - condition: state
        entity_id: binary_sensor.workday_sensor
        state: 'on'

  action:
  - service: climate.set_temperature
    entity_id: climate.thermostat_sejour
    data_template:
      temperature: '{% if trigger.entity_id == "binary_sensor.sejour_start_semaine_matin" and is_state("input_boolean.boost_sejour_semaine_matin", "on") %}
                      {{ states.input_number.consigne_confort_plus.state }}
                    {% elif trigger.entity_id == "binary_sensor.sejour_start_semaine_midi" and is_state("input_boolean.boost_sejour_semaine_midi", "on") %}
                      {{ states.input_number.consigne_confort_plus.state }}
                    {% elif trigger.entity_id == "binary_sensor.sejour_start_semaine_soir" and is_state("input_boolean.boost_sejour_semaine_soir", "on") %}
                      {{ states.input_number.consigne_confort_plus.state }}
                    {% else %}
                      {{ states.input_number.consigne_confort.state }}
                    {% endif %}'

  - service: notify.slack_hass_canaletto
    data_template:
      message: >
        {% if trigger.entity_id == "binary_sensor.sejour_start_semaine_matin" and is_state("input_boolean.boost_sejour_semaine_matin", "on") %}
          {{ states.sensor.date_time.state}} > Confort+ ({{ states.input_number.consigne_confort_plus.state }}°), Séjour : {{ states('sensor.mi_t_clock') }}°, Extérieur : {{ states('sensor.ext_temperature') }}°
        {% elif trigger.entity_id == "binary_sensor.sejour_start_semaine_midi" and is_state("input_boolean.boost_sejour_semaine_midi", "on") %}
          {{ states.sensor.date_time.state}} > Confort+ ({{ states.input_number.consigne_confort_plus.state }}°), Séjour : {{ states('sensor.mi_t_clock') }}°, Extérieur : {{ states('sensor.ext_temperature') }}°
        {% elif trigger.entity_id == "binary_sensor.sejour_start_semaine_soir" and is_state("input_boolean.boost_sejour_semaine_soir", "on") %}
          {{ states.sensor.date_time.state}} > Confort+ ({{ states.input_number.consigne_confort_plus.state }}°), Séjour : {{ states('sensor.mi_t_clock') }}°, Extérieur : {{ states('sensor.ext_temperature') }}°
        {% else %}
          {{ states.sensor.date_time.state}} > Confort ({{ states.input_number.consigne_confort.state }}°), Séjour : {{ states('sensor.mi_t_clock') }}°, Extérieur : {{ states('sensor.ext_temperature') }}°
        {% endif %}

Améliorations à envisager : EJP ou Tempo à gérer dans le template d'affection des consignes afin de réduire la température de 1° les jours rouges et de démontrer ainsi la nécessité de la domotique. Prévoir un boolean global afin d'activer ou pas cette option.

Automation Stop Confort Séjour Semaine :

  • Un bloc trigger: avec 3 templates afin de récupérer les heures STOP. Inutile de créer des binary_sensor ici car on n'a pas besoin de connaitre la source de déclenchement dans action: ou on se contenter d'affecter la consigne Eco dans tous les cas.
  • Un bloc condition: ou l'on reprend les mêmes conditions que pour START.
  • Un bloc action: avec
    • Une action ou on défini la consigne de température Eco.
    • Une action de notification pour le journal.

INSERT CODE

Autre méthode

En fait la méthode classique consiste à créer un trigger: basé sur un template qui va comparer une ou plusieurs fois l'heure locale avec l'heure de l'input_datetime:

  trigger:
  - platform: template
    value_template: '{{ states.sensor.time.state == states.input_datetime.pompe_stop_p1.state[0:5] }}'
  - platform: template
    value_template: '{{ states.sensor.time.state == states.input_datetime.pompe_stop_p2.state[0:5] }}'

Ça fonction très bien, mais imaginons que nous ayons défini plusieurs plages et qu'on décide de ne pas utiliser certaines.

Si on les laisse bêtement à 00:00 00:00 on pourrait imaginer qu'il ne se passe rien. En fait si, on se retrouve avec un ON/OFF et parfois même un OFF/ON ce qui est plus gênant. Pour y remédier, et ça m'a pris un peu de temps car je n'ai aucune notion de développement, j'ai fini par améliorer mon template en associant une condition AND, en gros, si l'heure locale est égale à l'heure définie ET si l'heure définie pour le START est différente de l'heure définie en STOP, alors TRUE...

  trigger:
  - platform: template
    value_template: '{{ (states.sensor.time.state == states.input_datetime.pompe_stop_p1.state[0:5]) and (states.input_datetime.pompe_start_p1.state != states.input_datetime.pompe_stop_p1.state) }}'
  - platform: template
    value_template: '{{ (states.sensor.time.state == states.input_datetime.pompe_stop_p2.state[0:5]) and (states.input_datetime.pompe_start_p1.state != states.input_datetime.pompe_stop_p2.state) }}'

Alternatives

Home Asssitant ne dispose toujours pas du véritable agenda qui fait la force de Jeedom. En général je préfère le faire à la main ce qui permet de réaliser exactement ce que l'on souhaite. Mais j'ai trouvé deux embryons d'agenda. Pour l'instant ça ne va pas très loin, mais ça permettra de planifier des choses simples sans se plonger dans le code...

Conclusion

Voilà, il ne vous reste plus qu'à dupliquer ça à l'infini pour gérer autant de journées type, plages horaires et thermostats. Merci à Phillip et Mathieu dans notre fil Telegram pour leurs idées et aide, notamment sur les templates qui m'ont pris pas mal de temps.

Sources