This week, we're going to revisit an old project. A few years ago, we built a system to control the Velux skylight windows in a room. We wrote a Python application that controlled a Velux KLF 200 interface using Yocto-MaxiPowerRelays. This system still works, but this week, we decided to update it to use our new Typed Python library.
As a reminder, in previous posts, we used two Yocto-MaxiPowerRelays, a Yocto-CO2-V2, and a YoctoHub-Ethernet to control the Velux KLF 200 interface, which controlled the opening and closing of the room's skylight windows. We then added a control panel with two buttons and a Yocto-MaxiDisplay that displayed the room temperature, the CO2 level, and two buttons to force all the Velux skylight windows to open and close.
Migrating
The first step in migrating the application is to install our new library using the pip command.
pip install yoctolib
Next, we had to modify the code for it to use the new classes from the Typed Python library. Since the two libraries are compatible and implement the same objects, we simply had to change the imports to load the version from our new library.
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 *
And then... that's it, actually!
Since both versions of our library implement the same API, there is no need to change the rest of the code. By changing the imports, we can switch from one library to another very easily. Note that this is not always the case. In our example, the application connects to two remote YoctoHubs, but if it used a module plugged into a USB port, it would not work. Indeed, since the Typed Python library is written 100% in Python, access to USB ports is (for now) not supported.
A few improvements
Since we were already working on the code, we took the opportunity to slightly modify the application and type the code. If you decide to modify the code of this application, this should help you understand the code and avoid minor bugs during execution.
For example, the _closeRelay property is of type YRelay and is initialized with the YRelay.FindRelay method. We know that we always have a valid YRelay object (even if no relay is connected). We don't need to test if _closeRelay is 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
Conversely, the _co2sensor property is only initialized if the configuration file contains the name of the sensor. In this case, the property is of type YCarbonDioxide or None. In these cases, before using this property, you must test whether it is None AND whether the sensor is present.
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([])
Note that the same construction must be used if you are using a FirstXXXX function, as these functions only return a valid object if a corresponding module is present at runtime.
if co2sensor != None and co2sensor.isOnline():
value:float = self.co2sensor.get_currentValue()
print("CO2: %f ppm" % value)
Conclusion
We have also added an asynchronous version of this application. Here, the goal is purely educational. The aim is to show that it is possible to use our programming library in both synchronous and asynchronous versions.
We have updated the Git repository from the previous post with the three versions (legacy, typed_sync, and typed_async) of this application.
Everything is available at this address: https://github.com/yoctopuce-examples/velux_controler.
