Version 41 (modified by 11 years ago) ( diff ) | ,
---|
Table of Contents
Proposal
This page is more or less a sandbox where I put my ideas. Feel free to comment.
Main line
pKNyX is not a ready-to-use application: it is a framework. This means that it is up-to-you to write the application which fits your needs.
For that, you will need to know Python, a powerfull , high level, object oriented, scripting language. Don't be afraid: Python is really easy to learn. It is now very popular, and used in many free and commercial applications to extend their capabilities. It is now even teached at school, to young people (~15 years old)! There are hundreds documentations, tutorials and examples on the web, and a huge community. Learning Python is really not a waste of time, and will help you to do many other tasks than simply building your KNX application!
pKNyX will provide you powerfull tools so you will only focus on your problem, and not on language subtilities, like it can be the case with other languages, like C, C++...
Ok, let me show you what I have in mind.
Vocabulary
First, some definitions of terms and concepts I will use in pKNyX. They are the summary of my undersanding of the KNX specifications.
Unlike other standards, KNX is build arround the concept of Distributed Applications. An application is split up into several Functional Blocks, which can themselves be implemented in different Devices in the KNX network.
A Functional Block communicates over the bus through its Datapoints. A Datapoint can be an input, an output or a parameter. For the real communication, a Datapoint can be implemented as Group Object, Interface Object Property, Polling Value or Memory Mapping. The most usefull is of course the Group Object.
To be able to communicate, Datapoints must have some well defined types, named Datapoint Types. These standardized Datapoint Types ensure the compatibility of the different devices made from many manufacturers. This another key point of the KNX bus.
pKNyX tries to follow as much as possible the KNX specifications in its implementation. This may not leads to the best architecture in term of efficiency, but I think it is the best way to have something easy to understand, maintain and improve. This can also serve as a summary of all informations I can grab arround these specs.
Functional Blocks
This will be the central feature of pKNyX, allowing user to create virtual devices which mimics real KNX devices. The Device itself will be implemented as the process level.
Here is a very simple example of what I have in mind: creating a virtual minimalistic weather station, which uses informations from a non-KNX real weather-station, or from a web site. This station only implements temperature/humidity.
from pknyx.api import FunctionalBlock, Stack, ETS class WeatherTemperatureBlock(FunctionalBlock): DP_01 = dict(name="temperature", access="output", dptId="9.001", default=19.) DP_02 = dict(name="humidity", access="output", dptId="9.007", default=50.) GO_01 = dict(dp="temperature", flags="CRT", priority="low") GO_02 = dict(dp="humidity", flags="CRT", priority="low") weatherTempBlock = WeatherTemperatureBlock(name="weather_tempertature", desc="A simple weather block example") stack = Stack(individualAddress="1.2.3") ets = ETS(stack) ets.weave(fb=weatherTempBlock, dp="temperature", gad="1/1/1") ets.weave(fb=weatherTempBlock, dp="humidity", gad="1/1/2") stack.run()
That's it! As you can see, concepts used here are simple... This Functional Block can be then used from any other real device of your installation, through Groups Addresses 1/1/1
and 1/1/2
. All you have to do, is to weave (link, bind...) the Group Objects of your real devices to these Groups Addresses, using the real ETS application.
Lets have a closer look to this example. First, we import some python objects:
from pknyx.api import FunctionalBlock, Stack, ETS
These objects are classes.
Then, we create a custom Functional Block; this is done by subclassing the FunctionBlock base class, and adding a few attributes:
class WeatherTemperatureBlock(FunctionalBlock): DP_01 = dict(name="temperature", access="output", dptId="9.001", default=19.) DP_02 = dict(name="humidity", access="output", dptId="9.007", default=50.) GO_01 = dict(dp="temperature", flags="CRT", priority="low") GO_02 = dict(dp="humidity", flags="CRT", priority="low")
The DP_xx
class attributes are the Datapoints of our Functional Block. The GO_xx
class attributes are the Group Objects mapping the Datapoints to the bus. They are both defined as python dictionnary; they will be automatically instanciated for you by the framework. The named used here does not matter, as long as they start with DP_
for Datapoints, and GO_
for Group Objects.
Then, we just instanciate our new Funtional Block:
weatherTempBlock = WeatherTemperatureBlock(name="weather_tempertature", desc="A simple weather block example")
We also need to instanciante some global high level objects:
stack = Stack(individualAddress="1.2.3") ets = ETS(stack)
The Stack
object is used to communicate over the bus (real bus, of course, but also on virtual bus). We can give it an Individual Address, to mimic real devices behaviour.
The ETS
object is a tool which works like the reall ETS application, and used to weave (bind, link...) our Group Objects to Group Addresses:
ets.weave(fb=weatherTempBlock, dp="temperature", gad="1/1/1") ets.weave(fb=weatherTempBlock, dp="humidity", gad="1/1/2")
And finally:
stack.serve()
This call is blocking, and launch the framework main loop.
We now have a process (Device) running, listening to the bus. The Datapoints, through their respective Group Objects, will react to requests on the Group Addresses their are weaved to ("1/1/1" and "1/1/2"). According to the flags, they will transmit their internal value on a Read request, or if this value changes.
Ok, but our Functional Block is not really usefull yet, as it does not refresh its Datapoints! Let's see how it can be done:
from pknyx.api import FunctionalBlock, Stack, ETS from pknyx.api import Scheduler schedule = Scheduler() class WeatherTemperatureBlock(FunctionalBlock): DP_01 = dict(name="temperature", access="output", dptId="9.001", default=19.) DP_02 = dict(name="humidity", access="output", dptId="9.007", default=50.) GO_01 = dict(dp="temperature", flags="CRT", priority="low") GO_02 = dict(dp="humidity", flags="CRT", priority="low") @schedule.every(minute=5) def updateTemperaturHumidity(self, event): # How we retreive the temperature/humidity is out of the scope of this proposal # temperature = xxx # humidity = xxx self.dp["temperature"] = temperature self.dp["humidity"] = humidity weatherTempBlock = WeatherTemperatureBlock(name="weather_tempertature", desc="A simple weather block example") stack = Stack(individualAddress="1.2.3") ets = ETS(stack) ets.weave(fb=weatherTempBlock, dp="temperature", gad="1/1/1") ets.weave(fb=weatherTempBlock, dp="humidity", gad="1/1/2") stack.run()
We have just imported and instanciated an additionnal object:
from pknyx.api import Scheduler schedule = Scheduler()
We have also defined a method which role is to update the temperature and humidity Datapoints values:
@schedule.every(minute=5) def updateTemperaturHumidity(self, event): # How we retreive the temperature/humidity is out of the scope of this proposal # temperature = xxx # humidity = xxx self.dp["temperature"] = temperature self.dp["humidity"] = humidity
Note how this method is periodically called, using the schedule.every()
method as python decorator. This decorator will automatically register our method and call it every 5 minutes.
In our method, we retreive the temperature and humidity values (not explained here), and give the value to the respective Datapoints. If the value has changed from the previous call, the Datapoint will automagically transmit it to the bus (according to the flags, of course).
That's it for now. This is only a draft version; final implementation may change, according to feedback/suggestions I will get. But the core is all there. Again, the goal of the framework is to provide very high level tools to build complete and powerfull applications and KNX extensions.
Simple rule
My first idea was to provide a special API to create rules, in a simple way, like this:
from pknyx.api import trigger, group @trigger.schedule.every(minute=5) def heatingBathroomManagement(event): tempObj = group.findById("bathroom_temp") tempSetupObj = group.findById("bathroom_temp_setup") heaterObj = group.findById("bathroom_heater") if tempObj.value < tempSetupObj.value - 0.25: heaterObj.value = 1 elif tempObj.value > tempSetupObj.value + 0.25: heaterObj.value = 0
but it implies to define the group objects mapping, which is exactly what we did in the Virtual Device example! So, let's see rules as virtual devices... All we have to do is to define the needed Datapoints:
from pknyx.api import Rule, Stack, ETS stack = Stack() ets = ETS(stack) class BathroomHeater(Rule): DP_01 = dict(name="bathroom_temp", dptId="9.001", flags="CWTU", priority="low") DP_02 = dict(name="bathroom_temp_setup", dptId="9.001", flags="CWTU", priority="low") DP_03 = dict(name="bathroom_heater", dptId="1.001", flags="CWTU", priority="low") @Rule.schedule.every(minute=5) def checkTemperature(self): if self.dp["bathroom_temp"].value < self.dp["bathroom_temp_setup"].value - 0.25: self.dp["bathroom_heater"].value = "On" elif self.dp["bathroom_temp"].value > self.dp["bathroom_temp_setup"].value + 0.25: self.dp["bathroom_heater"].value = "Off" heater = BathroomHeater(name="bathroom_heater", desc="Bathroom heating management", address="1.2.3") ets.register(heaterRule) ets.link(rule=heater, dp="bathroom_temp", gad="1/1/1") ets.link(rule=heater, dp="bathroom_temp_setup", gad="1/1/2") ets.link(rule=heater, dp="bathroom_heater", gad="1/1/3")
This way, we use the same paradigm, which is always better ;)
linknx compatibility mode
As I still want to use knxweb, I also plan to develop a special device-like object to mimic linknx behaviour. All it has to do is to provide the basic xml services used by clients, to display bus status and send orders. Other features, like advanced configuration, rules and so, will be omitted.
Again, we can use the same paradigm as for virtual devices and rules. The framework will provide another custom class for that purpose, but it will be used the same way. It will just add specific features, and the internal structure to act as a linknx-like server.
Specific server mode
The purpose of this will be to provide the model part of an automatic web page generation mechanism. I don't know yet how it will exactly work, but the idea is to provide a simple list of usefull informations/settings which can be used from smartphones (simples buttons and displays).
Unsorted ideas
- each rule/device can be a process, rather than a thread. User choice.
- create managers to get status of all run applications