[[PageOutline(2-5, Table of Contents, floated)]] = uid device = == Introduction == One of my main motivations to write the '''pKNyX''' framework was to develop my own client/server, smartphone-oriented, application. This device is the server part. == Features == All the configuration is done in this device; when the client connects, it first retreives the config, and build the GUI automatically. == Example == First create a '''pKNyX''' device as described in the [[Tutorial]]. Then, edit '''{{{device.py}}}''' file as following: {{{ #!python # -*- coding: utf-8 -*- import time import bottle from pknyx.api import Device from pknyx.api import logger import settings from widgets.button import Button, Button2 from widgets.toggle import Toggle from widgets.sunblind import Sunblind from widgets.display import DisplayNumber, DisplayMessage from widgets.heating import Heating from widgets.slider import Slider class Server(Device): FB_001 = dict(cls=Button, name="lumiere_maison", params={"options": {"label": "O", "icon": "Oxygen/22/actions/edit-delete.png", "value": 0}, "label": "Tout"},) FB_002 = dict(cls=Button, name="lumiere_rdc", params={"options": {"label": "O", "value": 0}, "label": "Rdc"}) FB_003 = dict(cls=Button, name="lumiere_etage", params={"options": {"label": "O", "value": 0}, "label": "Étage"}) FB_004 = dict(cls=Toggle, name="lumiere_entree", params={"room": "Entrée", "label": "Appliques & spots"}) FB_005 = dict(cls=Toggle, name="lumiere_sdb", params={"room": "Sdb", "label": "Appliques & spots"}) FB_006 = dict(cls=Toggle, name="lumiere_cuisine", params={"room": "Cuisine", "label": "Appliques & spots"}) FB_007 = dict(cls=Toggle, name="lumiere_sejour", params={"room": "Séjour", "label": "Appliques & suspension"}) FB_008 = dict(cls=Toggle, name="lumiere_salon", params={"room": "Salon", "label": "Appliques"}) FB_009 = dict(cls=Toggle, name="lumiere_ch_1", params={"room": "Chambre 1", "label": "Appliques & spots"}) FB_010 = dict(cls=Toggle, name="lumiere_ch_2", params={"room": "Chambre 2", "label": "Appliques & spots"}) FB_100 = dict(cls=Sunblind, name="bso_maison", params={"label": "Tout"}) FB_101 = dict(cls=Sunblind, name="bso_rdc", params={"label": "Rdc"}) FB_102 = dict(cls=Sunblind, name="bso_etage", params={"label": "Étage"}) FB_103 = dict(cls=Sunblind, name="bso_nord", params={"label": "Nord"}) FB_104 = dict(cls=Sunblind, name="bso_sud", params={"label": "Sud"}) FB_105 = dict(cls=Sunblind, name="bso_est", params={"label": "Est"}) FB_106 = dict(cls=Sunblind, name="bso_ouest", params={"label": "Ouest"}) FB_107 = dict(cls=Sunblind, name="bso_cuisine", params={"room": "Cuisine", "label": "Sud"}) FB_108 = dict(cls=Sunblind, name="bso_sejour", params={"room": "Séjour", "label": "Tout"}) FB_109 = dict(cls=Sunblind, name="bso_sejour_baie", params={"room": "Séjour", "label": "Baie"}) FB_110 = dict(cls=Sunblind, name="bso_sejour_ouest", params={"room": "Séjour", "label": "Ouest"}) FB_111 = dict(cls=Sunblind, name="bso_salon", params={"room": "Salon", "label": "Tout"}) FB_112 = dict(cls=Sunblind, name="bso_salon_sud", params={"room": "Salon", "label": "Sud"}) FB_113 = dict(cls=Sunblind, name="bso_salon_ouest", params={"room": "Salon", "label": "Ouest"}) FB_114 = dict(cls=Sunblind, name="bso_ch_1", params={"room": "Chambre 1", "label": "Ouest"}) FB_115 = dict(cls=Sunblind, name="bso_ch_2", params={"room": "Chambre 2", "label": "Sud"}) FB_200 = dict(cls=DisplayNumber, name="temperature_sdb", params={"room": "Sdb", "label": "Inter", "options": {"postfix": " °C"}}) FB_201 = dict(cls=DisplayNumber, name="temperature_cuisine", params={"room": "Cuisine", "label": "Inter", "options": {"postfix": " °C"}}) FB_202 = dict(cls=DisplayNumber, name="temperature_sejour", params={"room": "Séjour", "label": "Inter", "options": {"postfix": " °C"}}) FB_203 = dict(cls=DisplayNumber, name="temperature_salon", params={"room": "Salon", "label": "Inter", "options": {"postfix": " °C"}}) FB_204 = dict(cls=DisplayNumber, name="temperature_ch_1", params={"room": "Chambre 1", "label": "Inter", "options": {"postfix": " °C"}}) FB_205 = dict(cls=DisplayNumber, name="temperature_ch_2", params={"room": "Chambre 2", "label": "Inter", "options": {"postfix": " °C"}}) FB_206 = dict(cls=DisplayNumber, name="temperature_vmc_air_ext", params={"room": "Local technique", "label": "air extérieur", "options": {"postfix": " °C"}}) FB_207 = dict(cls=DisplayNumber, name="temperature_vmc_air_insuffle", params={"room": "Local technique", "label": "air insufflé", "options": {"postfix": " °C"}}) FB_208 = dict(cls=DisplayNumber, name="temperature_vmc_air_extrait", params={"room": "Local technique", "label": "air extrait", "options": {"postfix": " °C"}}) FB_209 = dict(cls=DisplayNumber, name="temperature_vmc_air_rejete", params={"room": "Local technique", "label": "air rejeté", "options": {"postfix": " °C"}}) FB_210 = dict(cls=DisplayNumber, name="temperature_meteo_air_ext", params={"room": "Extérieur", "label": "air extérieur", "options": {"postfix": " °C"}}) FB_211 = dict(cls=DisplayNumber, name="temperature_meteo_air_int", params={"room": "Global", "label": "air intérieur", "options": {"postfix": " °C"}}) FB_300 = dict(cls=Heating, name="chauffage_sdb_rdc", params={"room": "Sdb", "label": "Consigne"}) FB_301 = dict(cls=Heating, name="chauffage_sejour", params={"room": "Séjour", "label": "Consigne"}) FB_302 = dict(cls=Heating, name="chauffage_etage", params={"room": "Étage", "label": "Consigne"}) LNK_6_0_0 = dict(fb="lumiere_entree", dp="command", gad="6/0/0") LNK_6_1_0 = dict(fb="lumiere_entree", dp="state", gad="6/1/0") LNK_6_0_2 = dict(fb="lumiere_sdb_rdc", dp="command", gad="6/0/2") LNK_6_1_2 = dict(fb="lumiere_sdb_rdc", dp="state", gad="6/1/2") LNK_6_0_5 = dict(fb="lumiere_cuisine", dp="command", gad="6/0/5") LNK_6_1_5 = dict(fb="lumiere_cuisine", dp="state", gad="6/1/5") LNK_6_0_7 = dict(fb="lumiere_sejour", dp="command", gad="6/0/7") LNK_6_1_7 = dict(fb="lumiere_sejour", dp="state", gad="6/1/7") LNK_6_0_9 = dict(fb="lumiere_salon", dp="command", gad="6/0/9") LNK_6_1_9 = dict(fb="lumiere_salon", dp="state", gad="6/1/9") LNK_6_0_51 = dict(fb="lumiere_ch_1", dp="command", gad="6/0/51") LNK_6_1_51 = dict(fb="lumiere_ch_1", dp="state", gad="6/1/51") LNK_6_0_52 = dict(fb="lumiere_ch_2", dp="command", gad="6/0/52") LNK_6_1_52 = dict(fb="lumiere_ch_2", dp="state", gad="6/1/52") LNK_6_0_4 = dict(fb="lumiere_rdc", dp="command", gad="6/0/4") LNK_6_0_54 = dict(fb="lumiere_etage", dp="command", gad="6/0/54") LNK_9_1_30 = dict(fb="bso_cuisine", dp="louvre_step", gad="9/1/30") LNK_9_3_30 = dict(fb="bso_cuisine", dp="louvre_moveto", gad="9/3/30") LNK_9_0_30 = dict(fb="bso_cuisine", dp="blind_updown", gad="9/0/30") LNK_9_1_50 = dict(fb="bso_sejour_baie", dp="louvre_step", gad="9/1/50") LNK_9_3_50 = dict(fb="bso_sejour_baie", dp="louvre_moveto", gad="9/3/50") LNK_9_0_50 = dict(fb="bso_sejour_baie", dp="blind_updown", gad="9/0/50") LNK_9_1_52 = dict(fb="bso_sejour_ouest", dp="louvre_step", gad="9/1/52") LNK_9_3_52 = dict(fb="bso_sejour_ouest", dp="louvre_moveto", gad="9/3/52") LNK_9_0_52 = dict(fb="bso_sejour_ouest", dp="blind_updown", gad="9/0/52") LNK_9_1_59 = dict(fb="bso_sejour", dp="louvre_step", gad="9/1/59") LNK_9_3_59 = dict(fb="bso_sejour", dp="louvre_moveto", gad="9/3/59") LNK_9_0_59 = dict(fb="bso_sejour", dp="blind_updown", gad="9/0/59") LNK_9_1_60 = dict(fb="bso_salon_sud", dp="louvre_step", gad="9/1/60") LNK_9_3_60 = dict(fb="bso_salon_sud", dp="louvre_moveto", gad="9/3/60") LNK_9_0_60 = dict(fb="bso_salon_sud", dp="blind_updown", gad="9/0/60") LNK_9_1_61 = dict(fb="bso_salon_ouest", dp="louvre_step", gad="9/1/61") LNK_9_3_61 = dict(fb="bso_salon_ouest", dp="louvre_moveto", gad="9/3/61") LNK_9_0_61 = dict(fb="bso_salon_ouest", dp="blind_updown", gad="9/0/61") LNK_9_1_79 = dict(fb="bso_salon", dp="louvre_step", gad="9/1/79") LNK_9_3_79 = dict(fb="bso_salon", dp="louvre_moveto", gad="9/3/79") LNK_9_0_79 = dict(fb="bso_salon", dp="blind_updown", gad="9/0/79") LNK_9_1_150 = dict(fb="bso_ch_1", dp="louvre_step", gad="9/1/150") LNK_9_3_150 = dict(fb="bso_ch_1", dp="louvre_moveto", gad="9/3/150") LNK_9_0_150 = dict(fb="bso_ch_1", dp="blind_updown", gad="9/0/150") LNK_9_1_160 = dict(fb="bso_ch_2", dp="louvre_step", gad="9/1/160") LNK_9_3_160 = dict(fb="bso_ch_2", dp="louvre_moveto", gad="9/3/160") LNK_9_0_160 = dict(fb="bso_ch_2", dp="blind_updown", gad="9/0/160") LNK_9_1_240 = dict(fb="bso_maison", dp="louvre_step", gad="9/1/240") LNK_9_3_240 = dict(fb="bso_maison", dp="louvre_moveto", gad="9/3/240") LNK_9_0_240 = dict(fb="bso_maison", dp="blind_updown", gad="9/0/240") LNK_9_1_241 = dict(fb="bso_rdc", dp="louvre_step", gad="9/1/241") LNK_9_3_241 = dict(fb="bso_rdc", dp="louvre_moveto", gad="9/3/241") LNK_9_0_241 = dict(fb="bso_rdc", dp="blind_updown", gad="9/0/241") LNK_9_1_242 = dict(fb="bso_etage", dp="louvre_step", gad="9/1/242") LNK_9_3_242 = dict(fb="bso_etage", dp="louvre_moveto", gad="9/3/242") LNK_9_0_242 = dict(fb="bso_etage", dp="blind_updown", gad="9/0/242") LNK_9_1_243 = dict(fb="bso_nord", dp="louvre_step", gad="9/1/243") LNK_9_3_243 = dict(fb="bso_nord", dp="louvre_moveto", gad="9/3/243") LNK_9_0_243 = dict(fb="bso_nord", dp="blind_updown", gad="9/0/243") LNK_9_1_244 = dict(fb="bso_sud", dp="louvre_step", gad="9/1/244") LNK_9_3_244 = dict(fb="bso_sud", dp="louvre_moveto", gad="9/3/244") LNK_9_0_244 = dict(fb="bso_sud", dp="blind_updown", gad="9/0/244") LNK_9_1_245 = dict(fb="bso_est", dp="louvre_step", gad="9/1/245") LNK_9_3_245 = dict(fb="bso_est", dp="louvre_moveto", gad="9/3/245") LNK_9_0_245 = dict(fb="bso_est", dp="blind_updown", gad="9/0/245") LNK_9_1_246 = dict(fb="bso_ouest", dp="louvre_step", gad="9/1/246") LNK_9_3_246 = dict(fb="bso_ouest", dp="louvre_moveto", gad="9/3/246") LNK_9_0_246 = dict(fb="bso_ouest", dp="blind_updown", gad="9/0/246") LNK_2_0_1 = dict(fb="temperature_sdb", dp="value", gad="2/0/1") LNK_2_0_5 = dict(fb="temperature_cuisine", dp="value", gad="2/0/5") LNK_2_0_7 = dict(fb="temperature_sejour", dp="value", gad="2/0/7") LNK_2_0_4 = dict(fb="temperature_salon", dp="value", gad="2/0/4") LNK_2_0_52 = dict(fb="temperature_ch_1", dp="value", gad="2/0/52") LNK_2_0_53 = dict(fb="temperature_ch_2", dp="value", gad="2/0/53") LNK_3_0_2 = dict(fb="chauffage_sdb", dp="setpoint", gad="3/0/2") LNK_3_2_2 = dict(fb="chauffage_sdb", dp="state", gad="3/2/2") LNK_3_0_7 = dict(fb="chauffage_sejour", dp="setpoint", gad="3/0/7") LNK_3_2_7 = dict(fb="chauffage_sejour", dp="state", gad="3/2/7") LNK_3_0_50 = dict(fb="chauffage_etage", dp="setpoint", gad="3/0/50") LNK_3_2_50 = dict(fb="chauffage_etage", dp="state", gad="3/2/50") LNK_5_0_0 = dict(fb="temperature_vmc_air_ext", dp="value", gad="5/0/0") LNK_5_0_1 = dict(fb="temperature_vmc_air_insuffle", dp="value", gad="5/0/1") LNK_5_0_2 = dict(fb="temperature_vmc_air_extrait", dp="value", gad="5/0/3") LNK_5_0_3 = dict(fb="temperature_vmc_air_rejete", dp="value", gad="5/0/2") DESC = "Server" def init(self): logger.trace("Server.init()") self.createConfig() self.initWebServer() def createConfig(self): """ """ def conf(fb, *args, **kwargs): """ Helper """ return self.fb[fb].getConfig(*args, **kwargs) self._config = { "pages": [ { "title": "Global", "subtitle": "Fonctions globales", "path": "global", "widgets": [ conf("lumiere_maison", group="Éclairages"), conf("lumiere_rdc", group="Éclairages"), conf("lumiere_etage", group="Éclairages"), conf("bso_maison", group="Brise-soleil"), conf("bso_rdc", group="Brise-soleil"), conf("bso_etage", group="Brise-soleil"), conf("bso_nord", group="Brise-soleil"), conf("bso_sud", group="Brise-soleil"), conf("bso_est", group="Brise-soleil"), conf("bso_ouest", group="Brise-soleil"), ] }, { "title": "Éclairages", "subtitle": "Gestion des lumières", "path": "lights", "widgets": [ conf("lumiere_entree"), conf("lumiere_sdb"), conf("lumiere_cuisine"), conf("lumiere_sejour"), conf("lumiere_salon"), conf("lumiere_ch_1"), conf("lumiere_ch_2"), conf("lumiere_maison", group="Global"), conf("lumiere_rdc", group="Global"), conf("lumiere_etage", group="Global"), ] }, { "title": "Brise soleil", "subtitle": "Gestion des BSO", "path": "sunblinds", "widgets": [ conf("bso_cuisine"), conf("bso_sejour"), conf("bso_sejour_baie"), conf("bso_sejour_ouest"), conf("bso_salon"), conf("bso_salon_sud"), conf("bso_salon_ouest"), conf("bso_ch_1"), conf("bso_ch_2"), conf("bso_maison", group="Global"), conf("bso_rdc", group="Global"), conf("bso_etage", group="Global"), conf("bso_nord", group="Orientation"), conf("bso_sud", group="Orientation"), conf("bso_est", group="Orientation"), conf("bso_ouest", group="Orientation"), ] }, { "title": "Chauffage", "subtitle": "Gestion du chauffage", "path": "heating", "widgets": [ conf("temperature_sdb", label="Température"), conf("chauffage_sdb"), conf("temperature_sejour", group="Séjour", label="Température"), conf("chauffage_sejour", group="Séjour"), conf("temperature_ch_1", group="Dégagement", label="Température"), conf("chauffage_etage", group="Dégagement") ] }, { "title": "Températures", "subtitle": "Sondes de température", "path": "temperatures", "widgets": [ conf("temperature_sdb"), conf("temperature_cuisine"), conf("temperature_sejour"), conf("temperature_salon"), conf("temperature_ch_1"), conf("temperature_ch_2"), conf("temperature_vmc_air_ext", group="VMC"), conf("temperature_vmc_air_insuffle", group="VMC"), conf("temperature_vmc_air_extrait", group="VMC"), conf("temperature_vmc_air_rejete", group="VMC"), ] }, { "title": "Ventilation", "subtitle": "Gestion VMC", "path": "vmc", "widgets": [ conf("temperature_vmc_air_ext", group="Sondes de température"), conf("temperature_vmc_air_insuffle", group="Sondes de température"), conf("temperature_vmc_air_extrait", group="Sondes de température"), conf("temperature_vmc_air_rejete", group="Sondes de température"), ] }, { "title": "Pièces", "subtitle": "Fonctions par pièce", "path": "rooms", "pages": [ { "title": "Entrée", "subtitle": "", "path": "entree", "widgets": [ conf("lumiere_entree", group="Éclairages"), conf("temperature_entree", group="Sondes de température"), ] }, { "title": "Sdb", "subtitle": "", "path": "sdb", "widgets": [ conf("lumiere_sdb", group="Éclairages"), conf("temperature_sdb", group="Chauffage", label="Température"), conf("chauffage_sdb", group="Chauffage"), ] }, { "title": "Cuisine", "subtitle": "", "path": "cuisine", "widgets": [ conf("lumiere_cuisine", group="Éclairages"), conf("bso_cuisine", group="Brise-soleil"), conf("temperature_cuisine", group="Sondes de température"), ] }, { "title": "Séjour", "subtitle": "", "path": "sejour", "widgets": [ conf("lumiere_sejour", group="Éclairages"), conf("bso_sejour", group="Brise-soleil", label="Tout"), conf("bso_sejour_baie", group="Brise-soleil"), conf("bso_sejour_ouest", group="Brise-soleil"), conf("chauffage_sejour", group="Chauffage"), ] }, { "title": "Salon", "subtitle": "", "path": "salon", "widgets": [ conf("lumiere_salon", group="Éclairages"), conf("bso_salon", group="Brise-soleil", label="Tout"), conf("bso_salon_sud", group="Brise-soleil"), conf("bso_salon_ouest", group="Brise-soleil"), conf("temperature_salon", group="Sondes de température"), ] }, { "title": "Chambre 1", "subtitle": "", "path": "ch_1", "widgets": [ conf("lumiere_ch_1", group="Éclairages"), conf("bso_ch_1", group="Brise-soleil"), conf("temperature_ch_1", group="Sondes de température"), ] }, { "title": "Chambre 2", "subtitle": "", "path": "ch_2", "widgets": [ conf("lumiere_ch_2", group="Éclairages"), conf("bso_ch_2", group="Brise-soleil"), conf("temperature_ch_2", group="Sondes de température"), ] }, { "title": "Local technique", "subtitle": "", "path": "technique", "widgets": [ conf("temperature_vmc_air_ext", group="VMC"), conf("temperature_vmc_air_insuffle", group="VMC"), conf("temperature_vmc_air_extrait", group="VMC"), conf("temperature_vmc_air_rejete", group="VMC") ] }, { "title": "Maison", "subtitle": "", "path": "maison", "widgets": [ conf("lumiere_maison", group="Éclairage", label="Tout"), conf("bso_maison", group="Brise-soleil", label="Tout"), ] }, { "title": "Rdc", "subtitle": "", "path": "rdc", "widgets": [ conf("lumiere_rdc", group="Éclairages", label="Tout"), conf("bso_rdc", group="Brise-soleil", label="Tout"), ] }, { "title": "Étage", "subtitle": "", "path": "etage", "widgets": [ conf("lumiere_etage", group="Éclairages", label="Tout"), conf("bso_etage", group="Brise-soleil", label="Tout"), ] }, ] } ] } def initWebServer(self): """ """ self.app = bottle.Bottle() loadConfigRoute = bottle.Route(app=self.app, rule='/config', method='GET', callback=self.loadConfig) self.app.add_route(loadConfigRoute) readRoute = bottle.Route(app=self.app, rule="/eib/", method='GET', callback=self.read) self.app.add_route(readRoute) writeRoute = bottle.Route(app=self.app, rule="/eib//", method='PUT', callback=self.write) self.app.add_route(writeRoute) pknyxuiStaticRoute = bottle.Route(app=self.app, rule="/pknyxui/", method='GET', callback=self.pknyxuiStatic) self.app.add_route(pknyxuiStaticRoute) qooxdooStaticRoute = bottle.Route(app=self.app, rule="/qooxdoo-sdk/", method='GET', callback=self.qooxdooStatic) self.app.add_route(qooxdooStaticRoute) self.app.error_handler[404] = self.error404 def mainLoop(self): logger.trace("Server.mainLoop()") self.app.run(server="paste", host=settings.WEB_SERVER, port=settings.WEB_PORT, reloader=False, debug=True) def shutdown(self): logger.trace("Server.shutdown()") self.app.close() def loadConfig(self): logger.trace("Server.loadConfig()") return self._config def read(self, ts): logger.debug("Server.read(): last read ts=%d" % int(ts)) eib = [] while True: for fb in self.fb.values(): result = fb.read(ts) if result: eib.append(result) if ts == 0 or eib: break time.sleep(0.01) ts = int(time.time()) logger.debug("Server.read(): eib=%s" % eib) return dict(eib=eib, ts=ts) def write(self, fb, dp): value = bottle.request.forms.get("value") logger.info("Server.write(): fb=%s, dp=%s, value=%s" % (fb, dp, value)) self.fb[fb].write(dp, value) def pknyxuiStatic(self, filename): return bottle.static_file(filename, root=settings.PKNYXUI_ROOT) def qooxdooStatic(self, filename): return bottle.static_file(filename, root=settings.QOOXDOO_ROOT) def error404(self, error): return "

Tiens, je croyais que Chuck Norris avait retrouvé cette foutue page 404 !!!

" DEVICE = Server }}} Next, '''{{{settings.py}}}''': {{{ #!python # -*- coding: utf-8 -*- from pknyx.common import config DEVICE_NAME = "uid" DEVICE_IND_ADDR = "1.1.2" DEVICE_VERSION = "0.1" # Override default logger level config.LOGGER_LEVEL = "info" WEB_SERVER = "my_machine" WEB_PORT = 8081 PKNYXUI_ROOT = "/home/toto/pKNyXui" QOOXDOO_ROOT = "/home/toto/qooxdoo-sdk" }}} '''{{{PKNYXUI_ROOT}}}''' and '''{{{QOOXDOO_ROOT}}}''' must match your pKNyXui and qooxdoo installation paths. ''TODO: add link to pKNyXui''