Cette semaine, on va faire un peu de réchauffé. Il y a quelques années, on a fabriqué un système pour contrôler les Velux d’un local. Nous avions écrit une application en Python qui contrôlait une interface Velux KLF 200 à l'aide de Yocto-MaxiPowerRelay. Ce système fonctionne toujours, mais cette semaine, nous avons décidé de le mettre à jour pour qu'il utilise notre nouvelle librairie Python typée.
Pour rappel, dans les précédents articles, nous avions utilisé deux Yocto-MaxiPowerRelay, un Yocto-CO2-V2 et un YoctoHub-Ethernet pour piloter l'interface Velux KLF 200 qui contrôlait l'ouverture et la fermeture des fenêtres de la pièce. Nous avions ensuite ajouté un panneau de contrôle avec deux boutons et un Yocto-MaxiDisplay qui affichait la température de la pièce, le taux de CO2, ainsi que deux boutons permettant de forcer l’ouverture et la fermeture de tous les Velux.
La migration
La première étape pour migrer l’application consiste à installer notre nouvelle librairie à l’aide de la commande pip.
pip install yoctolib
Il faut ensuite modifier le code pour qu'il utilise les nouvelles classes de la librairie Typed Python. Comme les deux librairies sont compatibles et implémentent les mêmes objets, il faut simplement changer les imports pour charger la version de notre nouvelle librairie.
from yoctolib.yocto_anbutton import *
from yoctolib.yocto_carbondioxide import *
from yoctolib.yocto_display import *
from yoctolib.yocto_relay import *
from yoctolib.yocto_temperature import *
Et ensuite.... c'est tout en fait!
Comme les deux versions de notre librairie implémentent la même API, il n'est pas nécessaire de changer le reste du code. En changeant les imports, on peut passer d'une librairie à l'autre très facilement. Notez que ce n'est pas toujours le cas. Dans notre exemple, l'application se connecte à deux YoctoHub distants, mais, si elle utilisait un module branché sur un port USB, cela ne fonctionnerait pas. En effet, comme la librairie Typed Python est écrite en Python à 100%, l'accès aux ports USB n’est (pour l'instant) pas supporté.
Quelques améliorations
Vu qu'on a remis les mains dans le cambouis, on a quand même profité de l'occasion pour modifier légèrement l'application et typer le code. Si vous décidez de modifier le code de cette application, cela devrait vous aider à comprendre le code et vous évitez de petits bugs lors de l'exécution.
Par exemple, la propriété _closeRelay est de type YRelay et est initialisé avec la méthode YRelay.FindRelay. On sait que l’on aura toujours un objet YRelay valide (même si aucun relais n’est branché). On n’a pas besoin de tester si _closeRelay est None.
_desription: str
_closeRelay: YRelay
_openRelay: YRelay
_is_open: bool
_type: str
def __init__(self, zonename: str, descr: str, hwid_close: str, hwid_open: str):
self.name = zonename
self._desription = descr
self._closeRelay = YRelay.FindRelay(hwid_close)
self._openRelay = YRelay.FindRelay(hwid_open)
self._is_open = False
def open(self, force: bool) -> bool:
if force or not self._is_open:
if self._openRelay.isOnline():
self._openRelay.pulse(200)
self._is_open = True
return True
else:
print("Unable to open zone %s (%s):%s" %
(self.name, self._desription, ex.errorMessage))
return False
A contrario, la propriété _co2sensor est initialisée seulement si le fichier de configuration contient le nom du senseur. Dans ce cas, la propriété est de type YCarbonDioxide ou None. Dans ces cas, avant d'utiliser cette propriété, il faut tester si elle est None ET si le senseur est présent.
def __init__(self, config_file: str, verbose: bool):
...
self._co2sensor = None
with open(config_file, "r") as f:
config: dict[str, Any] = json.load(f)
if 'co2' in config:
co_sensor_id = config['co2']['sensor_id']
self._co2sensor = YCarbonDioxide.FindCarbonDioxide(co_sensor_id)
else:
print("No CO2 parameter defined in config file")
def auto(self) -> None:
if self._co2sensor != None and self._co2sensor.isOnline():
value:float = self._co2sensor.get_currentValue()
if value > self.co2_open_limit:
self.open([])
elif value < self.co2_close_limit and not self.manually_open:
self.close([])
Notez que la même construction doit être utilisée si vous utilisez une fonction FirstXXXX, car ces fonctions ne retournent un objet valide que si un module correspondant est présent au moment de l'exécution.
if co2sensor != None and co2sensor.isOnline():
value:float = self.co2sensor.get_currentValue()
print("CO2: %f ppm" % value)
Conclusion
Nous avons aussi ajouté une version asynchrone de cette application. Ici le but est purement pédagogique. Il s'agit de monter qu'il est possible d'utiliser notre librairie de programmation en version synchrone ou asynchrone.
Nous avons mis à jour le repository Git du précédent article avec les trois versions (legacy, typed_sync et typed_async) de cette application.
Le tout est disponible à cette adresse: https://github.com/yoctopuce-examples/velux_controler.
