[[PageOutline(2-5, Table of Contents, floated)]] = 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 Application''. 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'''. == Virtual Device == This will be the central feature of '''pKNyX''', allowing user to create virtual devices which mimics real KNX devices. They will follow the same architecture, like having '''Datapoints''' (aka as Communication Objects), which have to be linked to '''Group Address''' to communicate. Here is a very simple example of what I have in mind: creating a virtual weather station, which uses informations from a non-KNX real weather-station, or from a web site: {{{ #!python from pknyx.api import Device, Stack, ETS stack = Stack() ets = ETS(stack) # Weather station class definition class WeatherStation(Device): # Datapoints (= Group Objects) definition DP_01 = dict(name="temperature", dptId="9.001", flags="CRT", priority="low", initValue=0.) DP_02 = dict(name="humidity", dptId="9.007", flags="CRT", priority="low", initValue=0.) DP_03 = dict(name="wind_speed", dptId="9.005", flags="CRT", priority="low", initValue=0.) DP_04 = dict(name="wind_alarm", dptId="1.005", flags="CRT", priority="urgent", initValue="No alarm") DP_05 = dict(name="wind_speed_limit", dptId="9.005", flags="CWTU", priority="low", initValue=15.) DP_06 = dict(name="wind_alarm_enable", dptId="1.003", flags="CWTU", priority="low", initValue="Disable") # Instanciation of the weather station device object station = WeatherStation(name="weather_station", desc="My simple weather station example", address="1.2.3") # Linking weather station Datapoints to Group Address ets.link(dev=station, dp="temperature", gad="1/1/1") ets.link(dev=station, dp="humidity", gad="1/1/2") ets.link(dev=station, dp="wind_speed", gad="1/1/3") ets.link(dev=station, dp="wind_alarm", gad="1/1/4") ets.link(dev=station, dp="wind_speed_limit", gad="1/1/5") ets.link(dev=station, dp="wind_alarm_enable", gad="1/1/6") }}} That's it! As you can see, concepts used here are not new... This device can be then used from any other real device of your installation, through GADs {{{1/1/1}}} to {{{1/1/6}}}. All you have to do, is to link the Communication Objects of your real devices to these GADs, using the '''ETS''' application. For example, the DP Nr 4 will send the {{{"Alarm"}}} value over the bus when the wind speed will reach the value stored in DP Nr 5, but only if the value of the DP Nr 6 as been set to {{{"Enable"}}}. By linking this alram object to the alarm entry of you sun blinds, you can save them from destruction! Ok, but how the device knows the current wind speed? Well, it does not... yet! Let's see how it can be done: {{{ #!python from pknyx.api import Device, Stack, ETS stack = Stack() ets = ETS(stack) # Weather station class definition class WeatherStation(Device): # Datapoints (= Group Objects) definition DP_01 = dict(name="temperature", dptId="9.001", flags="CRT", priority="low", initValue=0.) DP_02 = dict(name="humidity", dptId="9.007", flags="CRT", priority="low", initValue=0.) DP_03 = dict(name="wind_speed", dptId="9.005", flags="CRT", priority="low", initValue=0.) DP_04 = dict(name="wind_alarm", dptId="1.005", flags="CRT", priority="urgent", initValue="No alarm") DP_05 = dict(name="wind_speed_limit", dptId="9.005", flags="CWTU", priority="low", initValue=15.) DP_06 = dict(name="wind_alarm_enable", dptId="1.003", flags="CWTU", priority="low", initValue="Disable") @Device.schedule.every(minute=5) def checkWindSpeed(self): # How we retreive the speed is out of the scope of this proposal # speed = xxx # Now, write the new speed value to the Datapoint self.dp["wind_speed"].value = speed # Check alarm speed if self.dp["wind_alarm_enable"].value == "Enable": if speed >= self.dp["wind_speed_limit"].value: self.dp["wind_alarm"].value = "Alarm" elif speed < self.dp["wind_speed_limit"].value - 5.: self.dp["wind_alarm"].value = "No alarm" # Instanciation of the weather station device object station = WeatherStation(name="weather_station", desc="My simple weather station example", address="1.2.3") # Linking weather station Datapoints to Group Address ets.link(dev=station, dp="temperature", gad="1/1/1") ets.link(dev=station, dp="humidity", gad="1/1/2") ets.link(dev=station, dp="wind_speed", gad="1/1/3") ets.link(dev=station, dp="wind_alarm", gad="1/1/4") ets.link(dev=station, dp="wind_speed_limit", gad="1/1/5") ets.link(dev=station, dp="wind_alarm_enable", gad="1/1/6") }}} All we do, here, is adding a method periodically called by the framework (every 5 minutes in the above example). In this method, we retreive the wind speed (not explained here), and give the value to the Datapoint Nr 3. If the value has changed from the previous call, the Datapoint will automagically transmit it to the bus (according to the flags, of course). We also check if the speed has reached the speed limit; if so, we change the corresponding Datapoint value, which wil, in turn, send the value on the bus if needed (a special value of the flag, like in '''linknx''', can force the value to be sent, even if it does not change). A last thing: is will be possible to compute and print the '''map table''': {{{ #!python print ets.computeMapTable() {'byDP': {'humidity (station)': ['1/1/2'], 'temperature (station)': ['1/1/1'], 'wind_alarm (station)': ['1/1/4'], 'wind_alarm_enable (station)': ['1/1/6'], 'wind_speed (station)': ['1/1/3'], 'wind_speed_limit (station)': ['1/1/5']}, 'byGAD': {'1/1/1': ['temperature (station)'], '1/1/2': ['humidity (station)'], '1/1/3': ['wind_speed (station)'], '1/1/4': ['wind_alarm (station)'], '1/1/5': ['wind_speed_limit (station)'], '1/1/6': ['wind_alarm_enable (station)']}} }}} 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: {{{ #!python 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 [[#VirtualDevice|Virtual Device]] example! So, let's see rules as virtual devices... All we have to do is to define the needed Datapoints: {{{ #!python 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