Il y a quelques mois, je vous avais déjà parlé des API locales Enphase Envoy-S/IQ Gateway. On avait alors abordé ensemble le fonctionnement de base de tout ce schmilblick et on avait cassé certains mythes sur leur fonctionnement (surtout depuis le firmware en version 7.x.x). Le but de ce premier article était de voir un peu en détail le fonctionnement de base pour une utilisation simple, dans du scripting Bash par exemple. Si vous n’aviez pas déjà lu cette introduction de base, je vous laisse découvrir tout ça ici :
Néanmoins, si vous êtes déjà un peu à l’aise avec tout ça, vous avez probablement l’envie d’aller plus loin… Pour ce faire, nous allons découvrir ci-dessous deux des principaux projets open source qui vous amènent un outillage tout prêt pour exploiter les API avec du Python. À vos claviers, on attaque !
GitHub : promenade dans le souk
Tout d’abord, si vous êtes déjà arrivé jusqu’ici, je ne pense pas avoir besoin de vous présenter GitHub. Clairement, c’est un lieu de merveille et de… désenchantement ou l’on peut trouver tout et son contraire ! Néanmoins, il contient de vraies pépites et nous allons ici vous aider à mettre le doigt sur deux jolis projets pour vous aider à démarrer (mais sans aller dans le détail #RTFM).
Cela signifie, que j’ai évité de parler des projets que j’estime moins intéressant ou qui semble gentiment abandonné. Je pense en particulier à sarnau/EnphaseEnergy ou le gars est un peu parti en vrille, porté par sa frustration de devoir réaliser des adaptations pour le nouveau firmware 7.x.x (c’est tellement plus simple quand il n’y a pas de sécurité 😜).
C’est donc en partant de l’hypothèse que vous connaissez déjà GitHub et que vous êtes aussi à l’aise avec les standards que l’on peut y trouver que nous allons continuer. La parenthèse étant désormais fermée, on peut aller de l’avant. Si vous n’avez rien compris, c’est probablement que cet article ne vous est pas destiné, désolé 😊 !
pyenphase/pyenphase
Introduction
Commençons par le projet pyenphase/pyenphase, le plus intéressant, mais pas encore forcément le plus complet. Alors, pourquoi le plus intéressant vous allez encore une fois me reprendre ?! Mais tout simplement que c’est le cœur de l’implémentation dans Home Assistant et c’est d’ailleurs en commençant à m’amuser avec ce dernier que je suis tombé là-dessus. Il s’agit donc d’une pièce importante du marché qui ne sera que portée à se développer toujours plus à l’avenir.
« pyenphase » n’est pas un outil que l’on peut télécharger et utiliser directement sans avoir à écrire un peu de code tout d’abord. Le but de ce projet étant de préparer tout le nécessaire à être utilisé dans vos scripts.
Comme la majorité des projets récents, celui-ci est en Python et devra être appelé sous la forme d’un module. Il est possible de trouver toutes les parties qui sont déjà implémentées dans sa documentation. Celle-ci est disponible sur Read The Docs comme beaucoup de projets aujourd’hui… quand ils sont documentés.
Installation
Nous sommes dans un projet entièrement réalisé pour du Python 3, donc il n’y a rien de bien magique. Il faudra passer par PIP pour lancer l’installation de votre futur joujou :
pip install pyenphase
Je ne vous cache pas que le truc qui m’a un peu fais c#### cassé les pieds au début, c’est que je n’avais pas une version de Python suffisamment à jour (car je le testais directement sur mon Mac). Donc pensez qu’il vous faut bien un Python > 3.10 pour qu’il fonctionne correctement… Sans quoi vous allez partager ma perte de temps.
Configuration de base
Avant de parler de produire le premier bout de code, il faut savoir que pyenphase requiert l’utilisation du module « asyncio« . Il s’agit d’un module Python qui fournit des facilités pour écrire du code asynchrone. Bon dans ce cas précis, c’est surtout un prérequis pour Home Assistant sauf erreur. Pour rappel pour ceux qui lisent de travers, ce projet est à la base construit pour Home Assistant.
Donc, asyncio permet d’exécuter des opérations concurrentes sans avoir besoin d’utiliser ou spécifier des processus. C’est particulièrement utile pour les applications réseau, les services web, et d’autres situations où vous souhaitez gérer plusieurs tâches en même temps de manière efficace… Comme récupérer des données sur un appareil IoT tel que notre Envoy IQ Gateway/Envoy-S Metered.
Donc, pour l’initialisation de base, il faudra pondre les lignes se trouvant ci-dessous. On va commencer par importer les modules pyenphase et asyncio. Puis indiquer qu’on veut utiliser les classes « Envoy » et « EnvoyData » depuis le module pyenphase :
import pyenphase
import asyncio
from pyenphase import Envoy, EnvoyData
Si après ça, vous pouvez exécuter le code sans erreur c’est que vous êtes bien parti ! Si ce n’est pas le cas, c’est qu’il y a une c####### dans le potage comme dirait l’expression. Donc, il faudra reprendre plus haut et attaquer la lecture des docs un peu plus sévèrement.
Fonction asynchrone
La deuxième partie du code, tout aussi importante, consiste à définir une fonction async qu’on a vue plus haut et qui s’appellera main() pour l’exemple :
async def main():
Adresse IP, initialisation et authentification
Attention, après avoir défini la fonction ci-dessus, tout ce qui se trouve plus bas sera donc indenté correctement 😉. La première partie définit l’adresse IP ou le hostname que l’on désire utiliser à l’avenir lors de tous nos appels aux API. Dans l’exemple ci-dessous, je laisse son hostname par défaut à Envoy.local (via mDNS) :
envoy = Envoy("Envoy.local")
Ensuite, nous devons utiliser la classe permettant de réaliser l’initialisation de base. Celle-ci va vérifier la version de l’Envoy-S/IQ Gateway et utiliser ensuite la bonne méthode d’authentification en fonction de ce qu’elle découvre. On ajoute ensuite un petit « print » pour afficher le résultat de ce qu’il a découvert, histoire d’être certain que le truc fonctionne :
await envoy.setup()
print(f'Envoy {envoy.host} running {envoy.firmware}, sn: {envoy.serial_number}')
En partant du principe que votre IQ Gateway/Envoy-S est à jour dans une version 7.x.x. Il faudra ensuite fournir les informations d’authentification au service Enphase Enlighten pour générer un token comme nous l’avons vu dans le précédent article traitant du sujet :
username = "trop@cool.com"
password = "monBeauMotdePassetRopbien!"
On prépare ensuite ces informations pour les envoyer à la classe Envoy :
await envoy.authenticate(username=username, password=password)
Et on finit tout ça, par préparer les données à la collecte en utilisant la méthode probe() :
await envoy.probe()
Utilisation
C’est bien beau, mais au final nous n’avons toujours pas récupéré la moindre donnée ! Pour savoir ce que vous désirez récupérer, il est nécessaire de passer du temps dans la documentation du projet, dans la section dédiée à la classe EnvoyData.
On commence par créer une variable « data », pour aller chercher les informations sur l’Envoy-S ou l’IQ Gateway. Pour ce faire, nous appelons la méthode update() de la classe envoy, couplée à await d’asyncio :
data = await envoy.update()
On va demander ensuite d’afficher les valeurs qui nous intéressent et qui sont tirées de notre variable data. Dans l’exemple ci-dessous, je sors la production actuelle et la puissance totale aujourd’hui :
print(f'Production: {data.system_production.watts_now}')
print(f'Production du jour : {data.system_production.watt_hours_today}')
Mais, je vous rappelle que nous avons mis tout ça dans une fonction appelée main() plus haut. Donc, il faut revenir au premier niveau d’indentation pour sortir de cette dernière et tout simplement l’exécuter sans paramètre :
asyncio.run(main())
Bloc de démo au complet
Voilà, étant donné que je sais que vous être grave fainéant et que vous attendez que ça, voici le code complet à tester (pour les passerelles en version < 7.x.x) :
import pyenphase
import asyncio
from pyenphase import Envoy, EnvoyData
async def main():
envoy = Envoy("Envoy.local")
await envoy.setup()
print(f'Envoy {envoy.host} running {envoy.firmware}, sn: {envoy.serial_number}')
username = "trop@cool.com"
password = "monBeauMotdePassetRopbien!"
await envoy.authenticate(username=username, password=password)
await envoy.probe()
data = await envoy.update()
print(f'Production: {data.system_production.watts_now}')
print(f'Production du jour : {data.system_production.watt_hours_today}')
asyncio.run(main())
Matthew1471/Enphase-API
Introduction
Autre projet très intéressant et forcément digne d’intérêt, Matthew1471/Enphase-API est aussi disponible sur la plateforme GitHub. Enphase-API se présente comme un projet non officiel permettant de mettre en avant toutes les fonctionnalités offertes par les API Enphase locales de la passerelle Envoy-S/IQ Gateway, mais aussi du service cloud Enphase Enlighten.
Nous ne savons pas beaucoup plus sur l’origine de ce projet, mais il est certain que l’utilisateur Matthew1471 est un développeur passionné que nous pouvons remercier pour le partage de son immense travail. Pourquoi immense travail ? Parce qu’il a réalisé une documentation exhaustive des API locales Enphase. On y apprend beaucoup de choses que je n’avais jamais vues ailleurs jusqu’à présent. Il s’agit donc de quelqu’un de bien informé à voir.
Globalement, à l’utilisation c’est très complet. Après mes quelques essais, j’ai trouvé tout de même un peu moins sexy à l’utilisation de tous les jours que le projet pyenphase présenté plus haut. Cependant, il intègre plus de fonctions et sait aussi gérer la partie Enphase Enlighten pour tirer des données historiques. Ce que ne fait pas pyenphase.
Installation
Tout comme le projet précédent, nous sommes ici aussi avec du Python. Donc il est nécessaire de passer à nouveau par l’outil PIP pour réaliser l’installation :
pip3 install Enphase-API
Configuration de base
La configuration de base ne nécessite pas de dépendance particulière. Pas besoin de joueur avec du asyncio comme nous l’avons vu précédemment. Nous allons simplement importer le module de base enphase_api, puis indiquer que depuis ce module nous allons utiliser les classes « cloud.authentication » et « local.gateway » et leurs méthodes « Authentication » et « Gateway ».
import enphase_api
from enphase_api.cloud.authentication import Authentication
from enphase_api.local.gateway import Gateway
Comme pour le premier projet, si vous rencontrez déjà une erreur d’exécution après cette première partie, il est nécessaire de reprendre depuis la documentation officielle.
Adresse IP et authentification
Pour continuer notre configuration, nous allons devoir à nouveau préparer quelques variables importantes. Ici, il n’y a pas un système permettant d’extraire tout seul les informations comme le numéro de série. Du moins, je ne l’ai pas vu dans les exemples et je ne cacherais pas que je n’ai pas passé des heures non plus dans les documentations… Il est donc nécessaire de renseigner ces informations sous forme de variable quelque part :
ENPHASE_USERNAME = 'trop@cool.com'
ENPHASE_PASSWORD = 'monBeauMotdePassetRopbien!'
IQ_GATEWAY_SERIAL_NUMBER = '12345678910'
IQ_GATEWAY_HOST = 'https://x.x.x.x'
Utilisation
Après avoir préparé toutes les informations nécessaires, vous pouvez débuter le premier bout de code permettant de récolter quelques données… Mais nous devons commencer par nous authentifier et recevoir le premier token d’authentification. Il faut donc commencer par déclarer une instance pour la classe « Authentication » :
authentication = Authentication()
Ensuite nous passons les paramètres username/password à la méthode authenticate, qui utilisera l’instance que l’on vient de créer :
authentication.authenticate(ENPHASE_USERNAME, ENPHASE_PASSWORD)
Et pour terminer, nous y ajoutons la génération du token depuis le numéro de série et le stockons dans une variable dédiée :
TOKEN = authentication.get_token_for_commissioned_gateway(IQ_GATEWAY_SERIAL_NUMBER)
La partie la plus « compliquée » est alors terminée. Nous avons terminé avec la méthode d’authentification à ce stade et nous pouvons extraire les premières données. Pour commencer, nous devons créer un objet de la classe « Gateway » en passant l’adresse de l’Envoy-S/IQ Gateway via la variable IQ_GATEWAY_HOST en tant que paramètre. Cet objet « gateway » sera utilisé pour interagir avec la passerelle Enphase :
gateway = Gateway(IQ_GATEWAY_HOST)
Ensuite, nous vérifions si nous avons bien un token disponible pour l’authentification avant de commencer à récolter des données via gateway.api_call(). L’information est stockée dans une variable, puis affichée à l’écran. Dans l’exemple, nous allons chercher tout le contenu du fichier production.json.
if gateway.login(TOKEN):
response = gateway.api_call('/production.json')
print(response)
Voilà, vous avez terminé, mais c’est clair qu’il y a beaucoup de choses à tirer et vérifier dans ce projet. Il est donc important de passer du temps dans la documentation.
Bloc de démo au complet
Comme tout à l’heure te que vous êtes toujours grave fainéant, voici le code complet à tester (pour les passerelles en version < 7.x.x) :
import enphase_api
from enphase_api.cloud.authentication import Authentication
from enphase_api.local.gateway import Gateway
ENPHASE_USERNAME = 'trop@cool.com'
ENPHASE_PASSWORD = 'monBeauMotdePassetRopbien!'
IQ_GATEWAY_SERIAL_NUMBER = '12345678910'
IQ_GATEWAY_HOST = 'https://x.x.x.x'
authentication = Authentication()
authentication.authenticate(ENPHASE_USERNAME, ENPHASE_PASSWORD)
TOKEN = authentication.get_token_for_commissioned_gateway(IQ_GATEWAY_SERIAL_NUMBER)
gateway = Gateway(IQ_GATEWAY_HOST)
if gateway.login(TOKEN):
response = gateway.api_call('/production.json')
print(response)
S’il fallait conclure…
Pas certain qu’il est nécessaire de conclure ce type d’article, mais j’ai apprécié ces deux projets pour des raisons différentes. L’approche pyenphase me convient mieux et je la trouve plus facile à utiliser dans mes bricolages quotidiens. De l’autre côté, j’ai apprécié la très grande quantité de documentation détaillée dans le projet enphase-api. Je n’utilise pas les API Enligthen (et c’est pour ça que je n’en parle pas), mais clairement s’il y en a besoin, le choix se portera obligatoirement sur le second projet…
L’avantage de tout ça, c’est que nous pouvons combiner les informations de ces deux projets pour couvrir tous les besoins et qu’ensemble nous avons désormais une base très solide à l’avenir pour de nombreux besoins domotiques ou de gestionnaires d’énergie !
En passant, je profite aussi de remercier catsmanac du projet pyenphase 🙏🏻. Avec son aide, j’ai pu facilement débuter ces premiers travaux avec les API et il est à la base de l’ensemble de la très bonne documentation du projet. Sans ça, je n’aurai pas fait grand-chose aussi rapidement.