156 | | My first idea was to provide a special API to create rules, in a simple way, like this: |
157 | | |
158 | | {{{ |
159 | | #!python |
160 | | from pknyx.api import trigger, group |
161 | | |
162 | | @trigger.schedule.every(minute=5) |
163 | | def heatingBathroomManagement(event): |
164 | | |
165 | | tempObj = group.findById("bathroom_temp") |
166 | | tempSetupObj = group.findById("bathroom_temp_setup") |
167 | | heaterObj = group.findById("bathroom_heater") |
168 | | |
169 | | if tempObj.value < tempSetupObj.value - 0.25: |
170 | | heaterObj.value = 1 |
171 | | elif tempObj.value > tempSetupObj.value + 0.25: |
172 | | heaterObj.value = 0 |
173 | | }}} |
174 | | |
175 | | 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: |
176 | | |
177 | | {{{ |
178 | | #!python |
179 | | from pknyx.api import Rule, Stack, ETS |
180 | | |
181 | | stack = Stack() |
| 156 | My first idea was to provide a special API to create rules, but in fact, they can be implemented as Functional Block. This way, we use the same paradigm, wich is always better ;o) |
| 157 | |
| 158 | {{{ |
| 159 | #!python |
| 160 | from pknyx.api import FunctionalBlock, Stack, ETS, Scheduler |
| 161 | |
| 162 | |
| 163 | stack = Stack(individualAddress="1.2.3") |
183 | | |
184 | | class BathroomHeater(Rule): |
185 | | |
186 | | DP_01 = dict(name="bathroom_temp", dptId="9.001", flags="CWTU", priority="low") |
187 | | DP_02 = dict(name="bathroom_temp_setup", dptId="9.001", flags="CWTU", priority="low") |
188 | | DP_03 = dict(name="bathroom_heater", dptId="1.001", flags="CWTU", priority="low") |
189 | | |
190 | | @Rule.schedule.every(minute=5) |
191 | | def checkTemperature(self): |
192 | | |
193 | | if self.dp["bathroom_temp"].value < self.dp["bathroom_temp_setup"].value - 0.25: |
194 | | self.dp["bathroom_heater"].value = "On" |
195 | | elif self.dp["bathroom_temp"].value > self.dp["bathroom_temp_setup"].value + 0.25: |
196 | | self.dp["bathroom_heater"].value = "Off" |
197 | | |
198 | | heater = BathroomHeater(name="bathroom_heater", desc="Bathroom heating management", address="1.2.3") |
199 | | ets.register(heaterRule) |
200 | | ets.link(rule=heater, dp="bathroom_temp", gad="1/1/1") |
201 | | ets.link(rule=heater, dp="bathroom_temp_setup", gad="1/1/2") |
202 | | ets.link(rule=heater, dp="bathroom_heater", gad="1/1/3") |
203 | | }}} |
204 | | |
205 | | This way, we use the same paradigm, which is always better ;) |
206 | | |
| 165 | schedule = Scheduler() |
| 166 | |
| 167 | |
| 168 | class HeatingManagerBlock(FunctionalBlock): |
| 169 | |
| 170 | DP_01 = dict(name="temperature", access="input", dptId="9.001", default=19.) |
| 171 | DP_02 = dict(name="setup", access="input", dptId="9.001", default=19.) |
| 172 | DP_03 = dict(name="heater", access="output", dptId="1.001", default="Off") |
| 173 | |
| 174 | GO_01 = dict(dp="temperature", flags="CWU", priority="low") |
| 175 | GO_02 = dict(dp="setup", flags="CWU", priority="low") |
| 176 | GO_03 = dict(dp="heater", flags="CRT", priority="low") |
| 177 | |
| 178 | @schedule.every(minute=5) |
| 179 | def manageHeater(self): |
| 180 | |
| 181 | # Read inputs |
| 182 | temperature = self.dp["bathroom"].value |
| 183 | setup = self.dp["setup"].value |
| 184 | |
| 185 | # Manage heater |
| 186 | if temperature < setup - 0.25: |
| 187 | heater = "On" |
| 188 | elif temperature > setup + 0.25: |
| 189 | heater = "Off" |
| 190 | |
| 191 | # Set outputs |
| 192 | self.dp["bathroom_heater"].value = heater |
| 193 | |
| 194 | |
| 195 | heatingManagerBlock = HeatingManagerBlock(name="heating_manager", desc="A simple heating manager block example") |
| 196 | |
| 197 | ets.register(heatingManagerBlock) |
| 198 | |
| 199 | ets.weave(fb=heatingManagerBlock, dp="temperature", gad="1/1/1") |
| 200 | ets.weave(fb=heatingManagerBlock, dp="setup", gad="1/1/2") |
| 201 | ets.weave(fb=heatingManagerBlock, dp="heater", gad="1/1/3") |
| 202 | |
| 203 | stack.serve() |
| 204 | }}} |
| 205 | |
| 206 | All you have to do is to use the Group Addresses you use in your real installation, through ETS application. The first one will update the temperature the Functional Block needs; the second one is used to give the setpoint, and the last one is used to switch on/off a real heater, through a KNX actuator. |
| 207 | |
| 208 | Note that it is possible to instanciate several heating managers, and weave them to different heaters. |
| 209 | |
| 210 | A more complex heating manager could compute a PID and output the power to use to heat. |