Enphase : DĂ©mystifier les API locales avec des projets open source en Python 🐍

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

Exemple de code en utilisant le projet pyenphase et le résultat dans la section du bas.

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

Exemple de code en utilisant le projet enphase-api et le résultat dans la section du bas.

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.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *