S-Opener : connectez et sécurisez votre porte de garage avec la plateforme Constellation et un Raspberry Pi – La porte de garage intelligente !
Lorsque vous nous avons acheté notre maison il y a presque deux ans, la porte de garage était déjà motorisée. Il est tellement confortable de pouvoir rentrer sa voiture dans son garage sans avoir à sortir pour ouvrir une porte !
Un simple appui sur une télécommande et hop la porte se lève ! Seulement est-ce que le protocole sans fil utilisé est réellement sécurisé ? Que faire si l’alarme est armée ? Car avoir un compte à rebours qui se lance dès lors qu’on commence à ouvrir sa porte pour rentrer sa voiture c’est plutôt stressant !
Et puis dans une maison connectée avoir une porte de garage isolée avec pour seul moyen de contrôle une télécommande ce n’est pas acceptable
Alors idéalement on a une porte de garage qui communique avec son alarme, qu’on peut contrôler depuis n’importe où et n’importe quoi et en utilisant un protocole sécurisé ! Ça tombe bien car la plateforme Constellation me permet d’interconnecter services, applications, pages Web et objets connectés !
De là est né S-Opener, une solution pour rendre ma porte de garage connectée, sécurisée et intelligente !
Le constat
Ma porte de garage est pilotée par un gros boitier sans marque particulière de prime abord. En ouvrant le capot, on y trouve un transfo, un moteur, une lampe et surtout une carte de contrôle et son module d’extension sans fil (RF) pour les télécommandes.
La carte de contrôle est une BFT STIR LF1, une carte pour automatisme assez générique que l’on retrouve sur les portails et portes de garage.
Celle-ci active un relai pour mettre en route le moteur qui remonte ou abaisse la porte de garage. Pour activer la porte, je dispose d’un bouton poussoir physique dans mon garage ainsi que deux télécommandes sans fil qui communiquent avec la carte d’extension sans fil installée sur le connecteur JP2 : la BFT CLONIX :
Seulement comment fonctionne-t-elle ? Quel est son degré de sécurité ? En effet avec un simple signal radio on ouvre la porte et on se retrouve chez vous ! Alors comment est communiqué cet ordre ? Crypté ? Notion de “challenge” pour éviter la non-rejouabilité ?
Alors pendant nos travaux d’installation, comme nous n’utilisons pas le garage pour rentrer nos voitures on allait au compteur pour faire çà :
Couper le 220V qui alimente la carte et donc le moteur, c’est l’assurance d’avoir une porte qui reste fermée ! C’est radical mais au moins on est tranquille !
J’ai également déconnecté le module d’extension sans-fil, ainsi plus de contrôle par les télécommandes !
Il ne reste plus que l’interrupteur physique qui ne fait qu’actionner un contact sec permettant d’ouvrir la porte sans risque de piratage par les ondes !
Très bien, c’est sécurisé, mais lorsqu’on a commencé à utiliser le garage pour rentrer nos voitures on perd tout l’intérêt d’une porte de garage automatique : il faut sortir de sa voiture, rentrer chez soi par la porte d’entrée, désactiver l’alarme, remettre le 220V au compteur, aller au garage pour ouvrir la porte et enfin remonter dans la voiture pour la rentrer ! Pas très pratique !
Alors l’idéal c’est d’avoir une porte de garage connectée sur un protocole sécurisé, qui communique avec l’alarme et qui s’ouvrir automatiquement quand on rentre chez soi !
Sécurité des portes de garage : quel risque ?
Avant d’expliquer la solution que j’ai mise en place, petit détour sur l’état de la sécurité des portes de garage. Oublions les interrupteurs physiques qui, d’une simple pression, ouvre la porte car ils sont placés à l’intérieur de l’habitat donc on ne peut les considérer comme une faille !
Ce qui m’intéresse ce sont les moyens permettant d’ouvrir la porte du garage (ou portail) de l’extérieur : en gros les télécommandes. En France elles utilisent les ondes radios sur la bande de fréquences des 433 Mhz principalement.
Lorsque l’on appuie sur le bouton de la télécommande, celles-ci émettent sur cette fréquence un ordre à destination de la carte de réception pour demander l’ouverture ou la fermeture de la porte ou du portail.
Comme l’ordre est véhiculé par onde radio, la couche “transport” n’est absolument pas sécurisée : tout le monde peut ”écouter” et capter le signal (la propagation de l’onde est uniforme dans toutes les directions de l’espace). Mais alors que contient le signal comme informations permettant à ma porte de s’ouvrir et est-ce que ce signal est rejouable ?
Pour répondre à cette question il faut d’abord rappeler qu’il existe deux grands types de code : les codes fixes et les codes roulant (rolling code) !
Les codes fixes
L’idée est simple, pour ouvrir la porte il faut un code, un code fixe ! En appuyant sur le bouton de la télécommande j’envoie un signal par les ondes contenant ce code ! Lorsque le récepteur de la porte de garage reçoit un signal d’ouverture de porte, il démodule le signal RF pour savoir si le code est bien celui permettant d’ouvrir la porte.
Il faut donc partager un même code entre le récepteur et les émetteurs (= les télécommandes).
Ce code peut être défini manuellement avec de petit switch dans la télécommande (très répandu pour les portes/portails collectifs) ou bien négocié dynamiquement dans un processus d’appairage (le code est ensuite stocké dans une mémoire type EEPROM).
Là la sécurité est critique car elle est vulnérable soit aux attaques de “replay” (rejeu) soit aux attaques de “brute-force”.
Le rejeu consiste à enregistrer le signal RF lorsque l’utilisateur utilise sa télécommande. Une fois le signal capturé (celui qui contient le code pour ouvrir la porte), il suffit de le rejouer, c’est à dire de l’émettre à nouveau avec un émetteur RF. On peut ainsi ouvrir la porte à volonté !
Autre possibilité : cracker la combinaison par “brute force”, c’est à dire en essayant toute les combinaisons possibles.
Par exemple dans l’émetteur ci-dessus il y a 12 switchs permettant de définir le code, la combinaison est donc encodée sur 12 bits (2 puissance 12) soit 4096 codes possibles (très peu pour un ordinateur).
Bien sûr il faut prendre en compte le temps d’envoi de chaque bit ainsi que le temps de pause entre chaque bits. Il faut aussi deviner sur combien de bits la combinaison est définie (en fonction des constructeurs, généralement entre 8 et 12 bits).
C’est tout l’exploit démontré par Samy Kamkar avec OpenSesame qui permet de cracker les mécanismes à code fixe en utilisant un procédé permettant de réduire considérablement le temps d’attaque : environ 8 secondes pour un code de 8 à 12 bits !!!!
Pour le fun, le code d’OpenSesame (en open source) a été porté sur un jouet Mattel permettant de disposer d’un cracker universel de code fixe portable !
8 secondes pour ouvrir une porte à code fixe avec un jouet, ça fait peur !!!
Les codes roulants ou codes tournants
Bien que les dispositifs à code fixe sont très répandus malgré leurs manque de sécurité évident, il existe des solutions plus sures basées sur des codes roulants (rolling code ou hopping code).
L’idée de base est d’utiliser une table de combinaison contenant des milliards de codes. Cette table est partagée entre les émetteurs (télécommandes/clés) et le récepteur lors de l’appairage (via l’échange d’informations clés permettant de générer de part et d’autre la même table de combinaison).
A chaque fois que l’on appuie sur le bouton de la télécommande, on envoie par RF le prochain code de la table. Le récepteur s’assure ensuite que le code reçu figure bien dans sa table à partir du dernier index où il s’était arrêté ! Il est donc impossible de rejouer le même code car il est “passé” (= déjà utilisé) et est donc obsolète ! Bien sûr les combinaisons suivantes dans la table ne sont pas prédictibles.
On pourrait alors penser qu’il n’est pas possible de cracker ce genre de système car il s’agit d’un code à usage unique et que les combinaisons sont codées sur plus de 32 ou 64 bits ce qui est très long à cracker (en tenant donc du temps d’émission et de pause et sachant que la validation d’un code remet à zéro l’attaque).
Cependant il existe une astuce dévoilée encore une fois par le fameux Samy Kamkar lors de la Defcon 2015 avec son nouveau projet nommé “RollJam”.
L’idée est simplement efficace : brouiller une fréquence légèrement plus basse de façon à ce que le signal RF de la télécommande n’atteigne jamais le récepteur (voiture ou porte de garage) tout en écoutant très finement la fréquence de l’émetteur pour outrepasser le brouillage et intercepter le code de la télécommande.
La télécommande a donc envoyé le prochain code de sa table, que nous appellerons le code “N” mais qui ne sera jamais reçu par le récepteur alors que l’attaquant, lui, en a connaissance.
L’utilisateur pensera donc à un problème de transmission et de manière inconsciente appuiera une nouvelle fois sur le bouton pour ouvrir sa voiture ou sa porte. Un nouveau code “N+1” est émis !
Même scénario, on continue de brouiller la fréquence pour empêcher d’ouvrir la voiture/porte et on enregistre ce nouveau code. L’attaquant a donc à sa connaissance le code “N” et le “N+1” mais la voiture/porte n’est toujours pas ouverte !
Mais avec le 2ème code en notre possession nous rejouons le 1er code, le code “N” : la voiture ou la porte s’ouvre, non pas par la télécommande mais avec un dispositif qui a rejoué un code valide qui n’a jamais atteint sa cible, donc qui n’a pas jamais été révoqué !
L’utilisateur n’y voit que du feu, mais en attendant l’attaquant à toujours en sa possession le code “N+1” qui lui aussi reste valide.
En clair, en mettant à proximité d’une porte de garage (ou voiture) à code roulant un petit dispositif (ici réalisé par Samy avec un Teensy et deux émetteurs/récepteurs RF 433, les CC1101) il est possible de connaitre X combinaisons d’avance en officiant en tant que passerelle : la télécommande n’atteint jamais le récepteur à cause du brouillage ce qui permet au dispositif de connaitre les prochaines combinaisons valides et de rejouer les anciennes combinaisons connues pour leurrer l’utilisateur le laissant penser que “tout fonctionne normalement”.
Pour pallier à cela il faudrait soit un système bidirectionnel permettant d’établir un mécanisme de chalenge entre l’émetteur et le récepteur : le récepteur émet un nombre aléatoire que la télécommande doit crypter avec la clé secrète partagée entre les deux au moment de l’appairage (en utilisant bien sûr un algorithme reconnu sûr).
Sans disposer d’une communication bidirectionnelle on pourrait imaginer que la télécommande envoie un HMAC d’un horodatage avec une clé secrète : cela implique que la télécommande comme le récepteur ont tous deux une horloge RTC parfaitement synchrone. Ainsi toute demande de plus de N secondes de différence serait rejetée ce qui limite la faille car l’attaquant aurait alors un code d’avance valide seulement pour quelques secondes, le plus dur étant de synchroniser les deux horloges dans le temps avec tout ce que ça implique.
Bref ce qu’il faut retenir c’est qu’aujourd’hui les mécanismes RF mis en œuvre pour les portes de garage ne sont pas infaillibles. Les codes fixes sont très risqués et pour des codes roulants la mise en œuvre de l’attaque est plus complexe mais reste accessible.
Pour les curieux je vous recommande la présentation de Samy K. à la DEFCON 2015 : http://samy.pl/defcon2015/2015-defcon.pdf
En clair pour avoir l’esprit tranquille et chasser de mon esprit la “faille” possible de ma porte, j’ai préféré déconnecter le module RF “CLONIX” et ranger les télécommandes dans un tiroir
La solution : S-Opener
L’idée est donc de pouvoir contrôler sa porte de garage mais de façon sécurisée!
Le principe de couper le 220V me plait bien, il suffit donc de mettre un relai pour contrôler l’alimentation du moteur ! Pour pouvoir ouvrir la porte à distance (mode télécommande) on utilisera un 2ème relai pour faire le contact sec sur la carte de contrôle BFT !
Pour piloter ces deux relais on utilisera un Raspberry Pi ! Ça tombe bien j’ai un vieux Raspberry Pi B 1ère génération (ceux avec 256Mo de RAM) qui traine dans un tiroir et je ne savais plus quoi en faire à côté des RPi V2 !
C’est exactement ce dont j’ai besoin pour gérer une porte de garage ! Pour les interrupteurs physiques dans le garage, je vais les relier directement sur les entrées du Raspberry afin de laisser le RPi décider de la suite !
Ensuite il suffira de connecter notre Raspberry à la Constellation. Il sera ainsi très simple de faire une page Web de contrôle de la porte de garage en temps réel mais aussi de pouvoir rajouter de l’intelligence car de ce fait la porte de garage aura accès à tous les équipements de la maison, dont l’alarme !
Par exemple, si on demande d’ouvrir la porte de garage (sur les interrupteurs physiques ou à distance) alors que l’alarme est activée, on refusera l’ordre et la porte restera fermée !
En résumé la porte de garage est pilotée par des relais eux même pilotés par un Raspberry connecté à Constellation par un simple câble RJ45 :
Le hardware
Après avoir validé le prototype, on passe à la réalisation d’une carte “double-relai”. On a donc deux relais pilotés en 5V et supportant 10A à 220V, bref on est large ! J’ai ajouté un bornier pour simplifier les connexions.
Pour piloter les deux relais on trouve donc deux transistors et deux diodes sur les bobines. Pour la connexion avec le Raspberry j’ai mis une petite barrette : 2 pins pour l’alimentation des relais (5V + GND) et bien évidement 1 pin par relai pour le changement d’état.
Pour l’installation, j’ai opté pour deux boitiers étanches. L’un pour les équipements 220V et l’autre pour le Raspberry et les I/O afin de limiter les perturbations électromagnétiques.
Pour les interrupteurs, reprenez soin d’utiliser du câble blindé. Pour ma part du RJ45 Cat6a en SFTP (double blindage) car à côté des interrupteurs des lampes 220V ça génère beaucoup de perturbation (surtout si vous avez des néons).
A gauche on a donc le boitier “220V” avec le transfo 5V (simple chargeur USB désassemblé pour prendre moins de place) et notre carte de relais qui sur le relai #1 envoi le 220V au moteur et sur le relai #2 fait une bouche sur le contact sec de la carte de contrôle pour donner l’ordre d’ouvrir ou de fermer la porte.
A droite on a le Raspberry Pi avec l’arrivée du câble réseau, les I/O des relais et les inputs des interrupteurs physiques du garage.
Sur le couvercle j’ai ajouté deux LEDs (avec leurs résistances) pour afficher l’état de la porte. Rouge lorsque tout est fermé, vert lorsque que le relai 220v alimente la porte !
Si vous regardez attentivement la photo de gauche ci-dessous, vous remarquerez les condensateurs ! En effet, les interrupteurs physiques sont connectés sur les entrées du RPi en “pull-up” !
Sur de longues distances et à proximité de câbles 220V il y a beaucoup de perturbation ! Si j’allume une lampe néon au fond du garage, mon Raspberry détecte plus d’une 20e d’impulsion sur l’entrée !!!
Alors pour éviter la pollution électromagnétique et donc les fausses informations, trois choses :
- Utilisez du câble blindé (relié à la terre) et éviter les zones de “contamination” ! Séparez le plus possible les câbles basse tension de la haute tension.
- Ajouter un condensateur 1uF entre votre entrée et la masse au niveau du Raspberry pour filtrer le signal (un condensateur pour chaque entrée)
- Personnellement, avec les points 1 et 2 j’ai quasiment éliminé tous les faux positifs, sauf le cas du néon que j’ai dû filtrer d’un point de vue “software” comme nous le verrons dans la suite de cet article.
Et voilà S-Opener proprement installé ! Avant de refermer le couvercle, il faut juste charger sur la SD du Raspberry la dernière version en date de Raspbian. Pour la suite on utilisera SSH pour déployer une sentinelle Constellation et après tout se contrôlera depuis Constellation sans avoir besoin de se connecter sur le Raspberry !
Programmation avec la Constellation
Si vous avez déjà lu mes articles sur The Mirror, l’alarme connectée, S-Energy, S-Sound ou plus récemment S-Panel, je pense que vous connaissez déjà cette plateforme !
La seule chose à faire sur un Raspberry après avoir déployé l’image de l’OS sur la carte SD, c’est de déployer une sentinelle Constellation via SSH et d’y renseigner l’URL de votre Constellation et la clé d’accès.
Dès lors, votre Rapsberry Pi (ici “sopener”) est intégré à votre Constellation :
Nous pouvons donc passer à la programmation du package Constellation.
Création du package Constellation
Depuis Visual Studio et avec le SDK Constellation installé, nous allons créer un projet Constellation en Python :
Un package Constellation est une sorte de service qui peut être déployé sur n’importe quelle sentinelle de votre Constellation. Chaque package peut recevoir et envoyer des messages, des sortes de “Web methods”.
Ainsi le package S-Opener expose une méthode “PushButton” permettant à n’importe qui dans la Constellation d’envoyer un ordre d’ouverture de la porte de garage.
Pour exposer une méthode Python dans la Constellation il suffit d’ajouter le décorateur “MessageCallback” :
1 2 3 4 | @Constellation.MessageCallback() def PushButton(): "Push Button to open or close the door" # code ici .... |
Ici le “MessageCallback” est très simple puisqu’il ne prend aucun paramètre. Un autre package de ma Constellation, une page Web, une application mobile ou tout autre objet connecté tel qu’un Arduino ou un ESP8266 peut envoyer un message “PushButton” au package “S-Opener” et ainsi déclencher le code Python de ma méthode ci-dessus ! On ne peut plus simple
De plus chaque package déclare ses MessageCallbacks dans la Constellation ce qui permet aux autres systèmes connectés de découvrir les MC exposés. Par exemple, le “Constellation Message Callbacks Browser” est une page HTML utilisant l’API JS de Constellation pour générer des formulaires de test pour chaque MC des packages de votre Constellation :
Piloter la porte de garage avec des relais en Python
L’idée du package est donc de contrôler deux relais pour alimenter en 220V la porte de garage mais aussi faire le contact sec pour ordonner l’ouverture ou la fermeture de la porte.
Je déclare tout d’abord les numéros de PIN que j’ai utilisés pour connecter mes relais et les LEDS (une LED rouge pour indiquer que le 220V est coupé et une LED verte pour indiquer que le moteur est alimenté) :
1 2 3 4 | GREEN_LED_PIN = 12 RED_LED_PIN = 16 OPENER_RELAY_PIN = 18 PSU_RELAY_PIN = 22 |
Au démarrage j’initialise ces GPIO en “sortie” :
1 2 3 4 5 6 7 8 9 10 11 | GPIO.setmode(GPIO.BOARD) # Leds GPIO.setup(GREEN_LED_PIN, GPIO.OUT) GPIO.setup(RED_LED_PIN, GPIO.OUT) GPIO.output(GREEN_LED_PIN, False) GPIO.output(RED_LED_PIN, False) # Relays GPIO.setup(PSU_RELAY_PIN, GPIO.OUT) GPIO.setup(OPENER_RELAY_PIN, GPIO.OUT) GPIO.output(OPENER_RELAY_PIN, False) GPIO.output(PSU_RELAY_PIN, False) |
Je déclare ensuite deux méthodes pour alimenter ou non le moteur en 220V :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | def PowerOn(): "Power On motor" global motorState global lastActivity Constellation.WriteInfo("PowerOn motor"); GPIO.output(PSU_RELAY_PIN, True) GPIO.output(GREEN_LED_PIN, True) GPIO.output(RED_LED_PIN, False) lastActivity = int(round(time.time())) motorState = True Constellation.PushStateObject("Power", True) def PowerOff(): "Power Off motor" global motorState global lastActivity Constellation.WriteInfo("PowerOff motor"); GPIO.output(PSU_RELAY_PIN, False) GPIO.output(GREEN_LED_PIN, False) GPIO.output(RED_LED_PIN, True) motorState = False Constellation.PushStateObject("Power", False) |
Notez bien que ce ne sont pas des “MessageCallbacks” autrement dit personne ne peut invoquer directement ces deux méthodes !
J’ai une 3ème méthode qui elle provoque l’impulsion permettant d’ouvrir ou fermer la porte :
1 2 3 4 5 6 7 8 | def SendImpulsion(): "Sending Impulsion on motor" global lastActivity Constellation.WriteInfo("Sending impulsion"); lastActivity = int(round(time.time())) GPIO.output(OPENER_RELAY_PIN, True) time.sleep(0.1) GPIO.output(OPENER_RELAY_PIN, False) |
On active simplement le 2ème relai pendant 100ms pour faire un contact sec et ordonner l’ouverture ou la fermeture de la porte ! Encore une fois cette méthode n’est pas accessible de l’extérieure.
Contrôler la porte de garage depuis la Constellation
Pour ouvrir la porte, il faut donc d’abord mettre le courant via la méthode “PowerOn” puis faire un ”SendImpulsion”.
Ce qui se résume :
1 2 3 4 5 6 7 | @Constellation.MessageCallback() def PushButton(): "Push Button to open or close the door" # Version simple : if motorState == False: PowerOn() SendImpulsion() |
Si le moteur est “Off” on l’allume puis on donne une impulsion ! Ici la méthode est exposée dans la Constellation ce qui permet à qui conque connecté avec une clé d’accès valide à ma Constellation d’envoyer un message pour contrôler la porte.
Par exemple depuis une simple page HTML en utilisant l’API Constellation pour Javascript il me suffit de faire :
1 | constellation.server.sendMessage({ Scope: 'Package', Args: [ 'SOpener' ] }, 'PushButton', {}); |
On pourrait également ouvrir la porte depuis un autre package Python :
1 | Constellation.SendMessage("SOpener", "PushButton", { }, Constellation.MessageScope.package) |
Ou bien depuis un programme écrit en .NET/C# :
1 | PackageHost.CreateScope("SOpener").Proxy.PushButton(); |
Ou même ouvrir la porte de garage depuis un script Powershell (en utilisant l’API REST de Constellation) :
1 2 | $client = New-Object System.Net.WebClient $client.DownloadString($constellationBaseURI + "SendMessage?" + $constellationCredential + "&scope=Package&args=SOpener&key=PushButton") |
Ou encore depuis un petit objet connecté “home-made” réalisé avec Gadgeteer:
1 2 3 4 | private void button_ButtonPressed(Button sender, Button.ButtonState state) { this.constellation.SendMessage("SOpener", "PushButton", null); } |
Bref c’est super, on a une porte de garage connectée
Contrôler la porte depuis les interrupteurs physiques
Après avoir déclaré le n° de PIN du GPIO sur lequel sont connectés les interrupteurs, je configure le GPIO au démarrage du package :
1 2 3 4 | # Switch inputs GPIO.setup(SWITCH_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) # GPIO Event handlers GPIO.add_event_detect(SWITCH_PIN, GPIO.RISING, callback=OnRisingButton, bouncetime=500) |
Lorsque l’on appuie sur un des interrupteurs, la méthode “OnRisingButton” sera déclenchée avec le code suivant :
1 2 3 4 5 6 | def OnRisingButton(channel): # Need to filter out the false positive of some power fluctuation time.sleep(0.01) if channel == SWITCH_PIN and GPIO.input(channel) != GPIO.HIGH: Constellation.WriteInfo("Button %s pressed!" % channel) PushButton() |
On marque une pause de 10ms avant de vérifier une nouvelle fois que notre input n’est pas “High” pour éviter ainsi les micro-fluctuations provoquées par les perturbations électromagnétiques qui entrainent des faux positifs ! C’est comme cela que j’ai contré les faux positifs levés par le néon
Si l’on a bien appuyé volontairement sur un des interrupteurs, on invoque la méthode “PushButton”, la même qui est exposée dans Constellation ! Elle s’occupera de mettre le 220V au moteur si nécessaire et faire l’impulsion pour déclencher l’ouverture ou la fermeture de la porte.
Coopération avec l’alarme de la maison
Jusqu’à présent on a donc notre porte de garage connectée dans Constellation. La sécurité repose désormais sur la Constellation accessible uniquement en employant des clés d’accès très longues (passphrase de plusieurs dizaines de caractères) le tout sur un protocole crypté en SSL.
Seulement quand doit-on couper le 220V du moteur, autrement dit quand appeler le “PowerOff” ? Idéalement lorsque je ferme la porte ! Et cette information est connue… par le système d’alarme lui-même connecté à Constellation (voir l’article ici).
Les StateObjectLinks
Les StateObjectLinks permettent de lier du code .NET, JS ou Python à l’état d’un StateObject de ma Constellation.
Avec l’API Python, comme on ne peut ajouter de décorateur (ou attribut en .NET) sur une variable, le StateObjectLink s’applique sur une méthode de la façon suivante :
1 2 3 | @Constellation.StateObjectLink(package='Paradox', name="AreaInfo1") def ParadoxAreaStateUpdated(stateObject): Constellation.WriteInfo("L'alarme est = %s" % "activée" if (stateObject.Value.IsFullArmed or stateObject.Value.IsStayArmed) else "desactivée") |
Si vous vous rappelez de mon article sur l’ alarme Paradox, le package Constellation “Paradox” pousse un StateObject nommé “AreaInfo1” qui contient toutes les informations sur le secteur 1.
Le StateObject contient plusieurs propriétés dont les propriétés booléennes “IsFullArmed” lorsque le système est complètement armé ou “IsStayArmed” pour l’armement partiel.
Dans le code Python ci-dessus, on crée un lien entre la méthode Python “ParadoxAreaStateUpdated” et le StateObject “AreaInfo1” du package “Paradox”.
Autrement dit dès que l’état de mon alarme change, la méthode Python sera invoquée avec comme paramètre un objet Python qui contient toutes les propriétés de mon StateObject poussé par le package Paradox (lui-même écrit en C# ).
Ainsi dans les logs Constellation, le programme Python écrira “L’alarme est activée” ou “L’alarme est désactivée” en fonction de l’état de l’alarme !
Cela nous permet donc d’inclure très facilement des variables d’état de n’importe quel package connecté à Constellation dans notre code, comme ici l’état de l’alarme, d’une zone ou bien la T° de consigne du thermostat Nest, le niveau de CO² du NetAtmo, l’état d’une lampe ou d’un volet Z-Wave, etc…
Couper automatiquement l’alimentation 220V du moteur lorsque la porte est fermée
Premier usage possible avec les infos de l’alarme : couper automatiquement le relai qui alimente le 220V à la porte de garage lorsque celle-ci est fermée !
Pour cela créons un lien entre le StateObject de ma porte de garage (chez moi la Zone n° 5) et une variable de mon code Python :
1 2 3 4 5 6 7 8 9 10 | global garageDoorOpened global garageDoorLastActivity @Constellation.StateObjectLink(package='Paradox', name="ZoneInfo5") def ParadoxGarageDoorStateUpdated(stateObject): global garageDoorOpened global garageDoorLastActivity garageDoorOpened = stateObject.Value.IsOpen garageDoorLastActivity = int(round(time.time())) Constellation.WriteInfo("ParadoxGarageDoor = %s" % garageDoorOpened) |
Puis au démarrage de mon package, j’ai une boucle infinie qui tourne chaque seconde et qui, si le moteur est allumé avec la porte de garage fermée depuis plus de 5 secondes, coupe l’alimentation 220V par l’invocation de la méthode “PowerOff” :
1 2 3 4 5 6 | while Constellation.IsRunning: ts = int(round(time.time())) if motorState == True and garageDoorOpened == False and (ts - garageDoorLastActivity) >= 5: Constellation.WriteInfo("Power off the motor when the door is closed") PowerOff() time.sleep(1) |
Simplement efficace
Interdire l’ouverture de la porte si l’alarme est armée
Autre lien entre l’alarme et S-Opener : on ne peut pas, sous aucun prétexte, ouvrir la porte de garage si l’alarme est armée quel que soit son mode d’armement (complet ou partiel).
Pour cela créons un lien avec le StateObject de l’alarme pour maintenir la variable “areaArmed” :
1 2 3 4 5 | @Constellation.StateObjectLink(package='Paradox', name="AreaInfo1") def ParadoxAreaStateUpdated(stateObject): global areaArmed areaArmed = stateObject.Value.IsFullArmed or stateObject.Value.IsStayArmed Constellation.WriteInfo("ParadoxAreaState = %s" % areaArmed) |
Et modifions notre MessageCallback “PushButton” pour vérifier si l’alarme est armée ou non et le cas échant interrompre l’ordre d’ouverture de la porte :
1 2 3 4 5 6 7 8 9 10 | @Constellation.MessageCallback() def PushButton(): "Push Button to open or close the door" if areaArmed == True: Constellation.WriteWarn("Unable to send impulsion : the Area is armed !") return else: if motorState == False: PowerOn() SendImpulsion() |
Une application mobile pour vous ouvrir la porte à votre retour ou fermer la maison lorsque vous partez!
Comme pour mon projet S-Panel, le plus simple pour réaliser une application mobile est d’utiliser Cordova. Cette fois ci on ne va pas se contenter de faire une simple application qui ouvre une page Web dans une sorte de frame (le InAppBrowser) mais on va réellement développer une application connectée à notre Constellation.
L’idée est simple : quand je rentre chez moi je veux que mon alarme se déverrouille et que ma porte de garage s’ouvre au moment où j’arrive devant ma porte. Pour le départ, je veux pouvoir fermer ma porte manuellement et que l’alarme s’arme d’elle-même dès que j’ai pris la route.
Et si vous vous rappelez de l’article sur l’alarme, lorsque que celle-ci est armée, la Constellation orchestre l’hibernation de la maison : couper S-Sound, baisser le thermostat Nest, fermer les volets et les lumières, fermer les TVs et ampli, etc…
Création de l’application Cordova
Pour réaliser cela, nous allons créer une nouvelle application Cordova qui pour ma part sera destinée à la plateforme Android pour fonctionner aussi bien sur mon smartphone que celui de ma femme.
1 2 3 | cordova create SOpener fr.constellation.sopener SOpener cd SOpener cordova platform add android |
Le principe de l’application est donc de vous géo-localiser. Il existe deux techniques :
- Le geo-fencing : active un évènement lorsque que vous entrez ou sortez d’une “région” géographique
- La geo-location : récupération de la position GPS
Il faut noter que la “geo-location” est très gourmande en énergie mais aussi très précise et cela demande que votre application soit lancée. A l’inverse le geo-fencing permet d’enregistrer un évènement lorsque que vous entrez ou sortez d’une “région” géographique et cela même si votre application n’est pas démarrée.
Dans notre cas, nous “réveillerons” l’application dès que nous approchons de la maison en affichant une notification permettant à l’utilisateur de lancer l’application SOpener.
Une fois l’application S-Opener lancée sur le smartphone, on active la geo-location GPS pour connaitre très précisément la position de l’utilisateur.
Localisez-moi
Ajoutons donc le plugin pour le Geofencing et celui pour la Geolocation :
1 2 | cordova plugin add https://github.com/cowbell/cordova-plugin-geofence cordova plugin add org.apache.cordova.geolocation |
Pour afficher une notification indiquant à l’utilisateur qu’il est proche (moins de 800 mètres) de la maison, on vient simplement enregistrer un geofence :
1 | var homeCoordinates = { latitude: 50.123456789, longitude: 3.123456789 }; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | // Add geofence next to home if needed if(window.localStorage['geofence'] === undefined || window.localStorage['geofence'].latitude != homeCoordinates.latitude || window.localStorage['geofence'].longitude != homeCoordinates.longitude) { window.geofence.initialize() .then(function () { window.geofence.addOrUpdate({ id: "MAISON", //A unique identifier of geofence latitude: homeCoordinates.latitude, //Geo latitude of geofence longitude: homeCoordinates.longitude, //Geo longitude of geofence radius: 800, //Radius of geofence in meters transitionType: 1, //Type of transition 1 - Enter, 2 - Exit, 3 - Both notification: { id: 1, title: "Approche de la maison", text: "Cliquez ici pour ouvrir S-Opener", openAppOnClick: true } } ).then(function () { // Save geofence coordinates window.localStorage['geofence'] = homeCoordinates; console.log('Geofence successfully added'); }, function (reason) { console.log('Adding geofence failed', reason); }); console.log('Geofence successfully initialize'); }, function (reason) { console.log('initialize geofence failed', reason); }); } |
Le Geofence n’est pas très réactif car il se base sur les informations GSM pour estimer votre position, c’est pour cela qu’il est recommandé de mettre un rayon assez grand sur votre fence. Ici le fait de mettre 800 mètres me permet d’avoir une notification quand j’arrive à environ 400 mètres de ma maison en utilisation normale et sans avoir lancé une autre application utilisant le GPS (comme Google Maps ou Waze).
Une fois que l’on clique sur la notification, cela ouvre l’application. Toujours au démarrage de celle-ci j’appelle la fonction ”watchPosition” permettant de suivre la position GPS. Dans le callback vous donnant la position il est impératif de ne surtout pas ajouter de logique métier ni de manipuler le DOM de l’application, sinon ça marche beaucoup moins bien !
De ce fait à chaque fois que la position GPS bouge, j’enregistre cette position dans une variable globale de mon application nommée “currentPosition” :
1 2 3 4 5 6 7 8 9 10 | // Start the GPS Watching position var watchID = navigator.geolocation.watchPosition(function(position) { app.currentPosition = position; },function(error) { console.log(error); },{ enableHighAccuracy: true, maximumAge: 5000, timeout: 10000 }); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | toRad: function(value) { var RADIANT_CONSTANT = 0.0174532925199433; return (value * RADIANT_CONSTANT); }, calculateDistance: function(starting, ending) { var KM_RATIO = 6371; try { var dLat = app.toRad(ending.latitude - starting.latitude); var dLon = app.toRad(ending.longitude - starting.longitude); var lat1Rad = app.toRad(starting.latitude); var lat2Rad = app.toRad(ending.latitude); var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.sin(dLon / 2) * Math.sin(dLon / 2) * Math.cos(lat1Rad) * Math.cos(lat2Rad); var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); var d = KM_RATIO * c; return d; } catch(e) { return -1; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | setInterval( function(){ // Wait for position if(app.currentPosition == null) { return; } // Calculate distance var distanceInMeter = app.calculateDistance(app.currentPosition.coords, homeCoordinates) * 1000; // Exemple : if(distanceInMeter < 20) { // A moins de 20 metres } else if(distanceInMeter < 100) { // A moins de 100 metres } else if(distanceInMeter < 250) { // A moins de 250m } else if(distanceInMeter < 1000) { // A moins de 1 km } else { // Plus de 1 km } }, 1000); |
Connexion à Constellation
Pour connecter notre application Cordova à Constellation rien de plus simple, car une application Cordova est une application HTML/JS et il existe une API Javascript pour Constellation.
Après avoir référencé le Javascript de l’API Constellation dans ma page HTML, je me connecte à Constellation en spécifiant son URI et la clé d’accès. Ensuite je m’abonne aux StateObjects de l’alarme et à ceux de S-Opener :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | // Constellation connection var constellation = $.signalR.createConstellationClient("https://monserver.constellation.com", "mon access key", "SOpenerClient"); // Mise à jour des variables locales en fonction de l'état de StateObject constellation.client.onUpdateStateObject(function (message) { if (message.Name == "AreaInfo1") { armed = message.Value.IsFullArmed; // Alarme armée ou non ? } else if (message.Name == "ZoneInfo5") { isDoorOpen = message.Value.IsOpen; // Porte de garage fermée ou non ? } else if (message.Name == "Power") { power = message.Value; // Moteur alimenté ou non ? } }); // Abonnement aux StateObjects constellation.connection.stateChanged(function (change) { if (change.newState === $.signalR.connectionState.connected) { constellation.server.requestAndSubscribeStateObjects("*", "SOpener", "*", "*"); constellation.server.requestAndSubscribeStateObjects("*", "Paradox", "AreaInfo1", "*"); constellation.server.requestAndSubscribeStateObjects("*", "Paradox", "ZoneInfo5", "*"); } }); // Connexion constellation.connection.start(); |
Grace à cela, je dispose dans mon code Javascript de mon application mobile, des variables m’indiquant l’état de l’alarme, de la porte de garage et de S-Opener !
Départ ou arrivée ?
Pour savoir si l’utilisateur part de la maison ou est entrain de rentrer deux méthodes :
- Si la 1ère position reçue indique une distance de moins de 30 mètres
1 2 3 4 | // First, determine if it's Departure or Arrival if( isAtHome == undefined) { isAtHome = distanceInMeter < 30; } |
- Si le Smartphone est connecté au Wifi de la maison grâce au plugin WifiWizard
1 2 3 4 5 6 | // If connected to the local Wifi SSID, you are at home ! WifiWizard.getCurrentSSID(function(ssid){ if( ssid == localWifiSSID && isAtHome == undefined) { isAtHome = true; } }, function(e) { console.log('Getting CurrentSSID failed', e); }); |
La logique métier
On a maintenant toutes informations à notre disposition. Je ne suis absolument pas pour enregistrer en dur dans l’application mon code PIN d’alarme, cela doit rester un secret qui ne sort jamais de votre tête (= “Ce que je sais” et non “Ce que je possède”).
Pour cela l’interface graphique de l’application me proposera un keypad me permettant de saisir mon code PIN de l’alarme de façon à “armer” l’application.
- Une fois arrivée à 50 mètre, j’envoie un message dans la Constellation à destination du package Paradox pour désarmer l’alarme comme expliqué ici. Puis à 20 mètres, j’envoie un second message au package S-Opener pour ouvrir la porte !
- Dans le cas d’un départ, je laisse l’utilisateur fermer le garage avec le bouton sur l’interface et si l’utilisateur à saisie son code PIN, j’envoie un message au package Paradox pour armer le système dès que je dépasse les 100 mètres de distance de la maison.
L’application m’indiquera également des informations quant à ma distance de la maison, l’état de l’alarme, de la porte, etc…
Le code complet se résume à ces quelques lignes :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | // Logique principale if(distanceInMeter < 150) { if(isAtHome) { // On quitte le domicile ... if(distanceInMeter < 20) { app.showMessage("AT HOME"); } else if(distanceInMeter > 100) { if(!armed && theCode !== "") { // Armement de l'alarme app.showMessage("ARMING SYSTEM"); constellation.server.sendMessage({ Scope: 'Package', Args: [ 'Paradox' ] }, 'AreaArm', { Area: 1, Mode: 1, PinCode: theCode }); theCode = ""; } else { app.showMessage("IN THE STREET"); } } } else { // On rentre au domicile ... if(distanceInMeter < 20) { if(parkedCar) { app.showMessage("GARAGE BUSY"); } else { app.showMessage("ARRIVED"); if(!armed && !power && !isAtHome && theCode !== "") { // Ouverture du garage constellation.server.sendMessage({ Scope: 'Package', Args: [ 'SOpener' ] }, 'PushButton', {}); } } } else if(distanceInMeter < 50) { if(armed && theCode !== "") { // Désarmement de l'alarme app.showMessage("DISARMING SYSTEM"); constellation.server.sendMessage({ Scope: 'Package', Args: [ 'Paradox' ] }, 'AreaDisarm', { Area: 1, PinCode: theCode }); } } else { app.showMessage("IN THE STREET"); } } } else if(distanceInMeter < 250) { app.showMessage("< 250M"); } else if(distanceInMeter < 1000) { app.showMessage("< 1KM"); } else if(distanceInMeter < 5000) { app.showMessage("< 5KM"); } else if(distanceInMeter < 10000) { app.showMessage("< 10KM"); } else { app.showMessage("> 10KM"); } |
Pour finir je vous conseille également d’ajouter le plugin Insomnia de façon à empêcher la mise en veille de votre téléphone lorsque S-Opener est lancé, laissant ainsi l’application au 1er plan jusqu’à votre arrivée à la maison :
1 2 | // Keep screen awake window.plugins.insomnia.keepAwake(); |
Il ne reste plus qu’à compiler l’application pour Android et l’installer sur votre téléphone en copiant l’APK.
1 | cordova build android |
Le garage est-il libre ?
Vous avez peut-être remarqué dans le code ci-dessus la variable “carParked” ou sur la capture l’encadré rouge indiquant “BUSY”.
En effet à la maison, le 1er arrivé rentre sa voiture dans le garage et le 2ème se gare à l’extérieur devant le garage Il faut donc savoir, au moment d’arriver à la maison si le garage est libre ou non.
La solution la plus simple est de faire comme si de rien n’était, d’ouvrir de le garage et de constater visuellement si il est libre ou occupé ! Mais pour une expérience vraiment WAF on va confier cette tache à S-Opener.
Pour cela l’idée est très simple : mesurer la distance entre le moteur et le sol :
D’après la hauteur de nos deux voitures on peut conclure qu’une distance de plus d’un mètre sans obstacle indique un emplacement libre, sinon c’est qu’il y a une voiture !
De ce fait, pour mesurer la distance j’utilise un capteur à ultra-son, ici un HC SR04 :
Il dispose de 4 pins : deux pour l’alimentation (5V), une nommée TRIGGER pour envoyer des impulsions et une dernière nommée ECHO, pour d’”écouter” l’écho de retour.
En mesurant le temps entre l’émission de l’impulsion et son retour on a le temps que l’onde a mise pour faire l’aller-retour. Tenant compte de sa vitesse de propagation on peut donc estimer la distance entre le capteur et l’obstacle devant lui. Le SR04 est capable de mesurer des distances de 2 cm à 4 mètres environ.
Pour la conception, j’ai utilisé un boitier ABS chez Conrad :
Puis j’ai percé deux trous pour pouvoir installer le SR04 :
Ne reste plus qu’à l’installer dans le boitier et à le connecter à un câble (ici un ancien câble USB avec blindage) avec des connecteurs femelles pour pouvoir le brancher facilement sur les GPIO du Raspberry :
Puis, avec de l’adhésif double face, j’ai fixé le capteur sur le rail de la porte de garage, à proximité du moteur :
Pour la partie programmation, la pin “TRIGGER” est une sortie alors que “ECHO” une entrée :
1 2 3 | # HC SR04 sensor GPIO.setup(HC_TRIGGER_PIN,GPIO.OUT) GPIO.setup(HC_ECHO_PIN,GPIO.IN) |
Pour effectuer une mesure, on envoie une impulsion de 10uS au trigger puis on attend l’écho de retour. La différence de temps entre l’envoi et le retour nous donnera la distance :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | def MeasureFloorDistance(): # Pulse 10Us as sensor except GPIO.output(HC_TRIGGER_PIN, True) time.sleep(0.00001) GPIO.output(HC_TRIGGER_PIN, False) # Wait echo signaloff = time.time() while GPIO.input(HC_ECHO_PIN) == 0: signaloff = time.time() while GPIO.input(HC_ECHO_PIN) == 1: signalon = time.time() # Calculate distance timepassed = signalon - signaloff distance = timepassed * 17000 return distance |
Pour obtenir une valeur fiable et éviter les erreurs de mesure, j’ai une méthode (déclarée en MessageCallback) qui effectue 10 mesures à 1 seconde d’intervalle avec une moyenne glissante.
A la fin de la mesure la valeur est poussée dans un StateObject “DistanceToFloor” ainsi qu’un autre StateObject “ParkedCar” indiquant si une voiture est garée en comparant la distance à un seuil (100 cm pour ma part).
1 2 3 4 5 6 7 8 9 | @Constellation.MessageCallback() def CalculateFloorDistance(): distance = MeasureFloorDistance() for i in range(2,10): time.sleep(1) distance = (distance + MeasureFloorDistance()) / 2 Constellation.PushStateObject("DistanceToFloor", distance) Constellation.PushStateObject("ParkedCar", (distance < CAR_DISTANCE_THRESHOLD)) Constellation.WriteInfo("DistanceToFloor: %s cm | ParkedCar: %s" % (distance, (distance < CAR_DISTANCE_THRESHOLD))) |
Cette méthode est appelée automatiquement lors du “PowerOff”, c’est à dire une fois que la porte de garage est fermée et que l’on coupe l’alimentation 220V du moteur, S-Opener met à jour les informations du StateObject quant à la présence ou non d’une voiture.
De ce fait, l’application mobile S-Opener est capable de m’indiquer si le garage est vide ou non, au quel cas, la porte ne sera pas ouverte à mon arrivée !
S-Opener en action
A 6 km de la maison, le système est armé et le garage occupé :
Arrivée à la maison, le garage est occupé et l’alarme déjà désarmée, rien ne se passera :
A 163 mètres de la maison, j’ai saisie le code de l’alarme, l’application est prête à désarmer l’alarme et à ouvrir la porte (car le garage est libre) :
Arrivée devant le garage, le système est désarmé et la porte de garage ouverte :
Au départ de la maison, l’application me reconnait comme “à la maison”, le système est désarmé et le garage occupé (ma voiture n’est pas encore sortie) :
Après saisie du code PIN de l’alarme, à plus de 100 mètre de la maison un ordre d’armement de l’alarme est envoyé (le garage est reconnu comme libre) :
Après 1 minute et à plus de 600 mètres de la maison, le système d’alarme Paradox est armé :
Allo la porte, c’est moi, ouvres-toi s’il te plait, je rentre !
Puisque la porte de garage est connecté dans la Constellation, elle est pilotable à partir de n’importe où et n’importe quoi comme un Gadgeteer, un script Powershell, une page Web, une application mobile, une tablette, un programme .Net, etc….
Alors pour le fun, je me suis amusé à la piloter par téléphone ! Et ne pensez pas que j’ai autant de temps à perdre que cela, car grâce à Skype for Business (ex-Lync) et à Constellation ça ne m’a pris que quelques minutes.
Microsoft Lync Server (anciennement nommé Microsoft Office Communications Server abrégé OCS) maintenant renommé “Skype for Business” est une plateforme de communication destinée aux entreprises, fournissant l’infrastructure nécessaire à l’utilisation de la messagerie instantanée, la présence, la voix et la visioconférence.
Cette plateforme, basée sur les technologies Microsoft .NET, met un tas d’API pour développer avec ! Je vous conseille la lecture de mon très cher ami Alexis Conia, expert et MVP Lync pour en apprendre d’avantage.
Sa société Neitiv est spécialisée dans la mise en place d’infrastructure Lync et dans le développement d’application autour de la communication instantanée. Grace à son expertise, j’ai écrit au début de l’année un package Constellation permettant de connecter Lync à votre Constellation.
De plus sa société Neitiv m’a gracieusement créé un compte Lync sur leurs serveurs en m’affectant un numéro de téléphone public me permettant donc de passer ou de recevoir des appels depuis Constellation !
Je revendrais en détail sur ce sujet dans un prochain article !
Par exemple, dans ma règle principale, je déclare l’élément suivant : “Je veux que tu <ordre> (le garage | la porte de garage) “
1 2 3 4 5 6 7 8 9 10 | <item> <tag>$.RootKey="sopener";</tag> <item> je veux que tu </item> <ruleref uri="#ordre"/> <tag> $.state=$$;</tag> <one-of> <item> le garage </item> <item> la porte de garage </item> </one-of> </item> |
Comme les “ordres” sont également utilisés pour la gestion des lumières et volets (Vera / Z-Wave), une règle définie les ordres de la façon suivante :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <rule id="ordre" scope="private"> <one-of> <item> <tag> $="true"; </tag> <one-of> <item> ouvre </item> <item> allume </item> </one-of> </item> <item> <tag> $="false"; </tag> <one-of> <item> éteind </item> <item> ferme </item> </one-of> </item> </one-of> </rule> |
Ainsi je peux dire au téléphone “Je veux que tu ouvres le garage” ou “Je veux que tu fermes la porte de garage”. Dans la première phrase, je vais obtenir un objet sémantique ayant deux propriétés :
- RootKey = “sopener” (l’élément qui a matché)
- state = “true” (correspond à la valeur de l’ordre qui à matché : ouvre = true).
Une fois que le package “Lync” reconnait une phrase vocale par téléphone, il envoie un message nommé ‘”Recognize” avec le résultat sémantique dans la Constellation au groupe “Lync” !
C’est à dire que n’importe quel package .NET, Python ou même un Arduino connecté à votre Constellation et abonné au groupe “Lync” peut interagir avec les appels téléphonique
Par exemple, en C#, il me suffit de faire une méthode que je déclare comme “MessageCallback” et qui si, la phrase reconnu est de type “sopener” envoie un message au package “S-Opener” pour ouvrir la porte tout en répondant à l’interlocuteur au téléphone par l’envoi d’un message “SpeakMessage” au package Lync :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | [MessageCallback(IsHidden = true)] public void Recognize(dynamic recognizeResult) { string sender = MessageContext.Current.Scope.Args[0]; dynamic semantic = recognizeResult.SemanticValue; if (semantic.RootKey == "sopener") { if (!this.IsArmed) { this.SendMessage("SOpener").PushButton(); this.SendMessage("Lync").SpeakMessage(sender, "je " + (string)recognizeResult.Text); } else { this.SendMessage("Lync").SpeakMessage(sender, "impossible de " + (string)recognizeResult.Text + ". L'alarme est armée"); } } } |
Le package “Lync” écoute aussi les touches DTMF du téléphone et les envoient dans des messages dans votre Constellation. Il est donc également possible d’armer ou désarmer l’alarme par téléphone en utilisant le pavé numérique pour saisir son code :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | private string code = string.Empty; [MessageCallback(IsHidden = true)] public void ToneReceived(dynamic tone) { string sender = MessageContext.Current.Scope.Args[0]; switch ((int)tone.Tone) { case 11: // Touche # this.WriteInfo("{0} de l'alarme par téléphone", this.IsArmed ? "Désactivation" : "Activation"); if (this.IsArmed) { this.SendMessage("Paradox").AreaDisarm(new { Area = this.AreaStatus.DynamicValue.Id, PinCode = code }); } else { this.SendMessage("Paradox").AreaArm(new { Area = this.AreaStatus.DynamicValue.Id, Mode = 1, PinCode = code }); } this.SendMessage("Lync").SpeakMessage(sender, "Je " + (this.IsArmed ? "désactive" : "active") + " l'alarme"); this.code = string.Empty; break; case 10: // Touche * this.code = string.Empty; break; default: // Touches des chiffres (= code) this.code += ((int)tone.Tone).ToString(); break; } } |
Très pratique pour piloter l’alarme depuis le téléphone intégré dans la voiture
Conclusion
A la base le besoin était simple : connecter la porte de garage et par la même occasion supprimer le module RF qui ne peut pas être considéré comme sûr.
Grace à un Rasperry Pi et des relais, nous pouvons très facilement piloter n’importe quel automatisme pour porte de garage ou portail; La sécurité repose maintenant sur le réseau et système informatique.
L’utilisation de la plateforme Constellation facilite grandement l’administration, le déploiement et la supervision des applicatifs sur le Raspberry et permet également d’interconnecter S-Opener dans l’écosystème des services et objets connectés de la maison.
Ainsi S-Opener peut se servir de l’alarme pour ajouter de l’intelligence et de la sécurité dans son fonctionnement et lui-même d’être piloté depuis un téléphone, un Arduino, une page Web ou une application mobile sans se lancer dans des développements lourds et spécifiques.
Cette solution a été développé et installé en Février 2015, soit plus de 6 mois d’utilisation quotidienne : notification lorsque le garage est occupé, ouverture automatique à notre arrivée, fermeture et armement automatique de l’alarme lorsque nous quittons le domicile ! Le confort de l’intelligence ambiante
Avec un cout global d’environ de 50€ (pour les boitiers, l’alimentation 5V, le Raspberry, la carte SD et les relais), la certification WAF est obtenue avec succès !
marc
Bonjour,
j’ai déjà posé la question via plusieurs canaux mais sans réponse : comment peut-on tester la plateforme constellation ? Beta testeur ? merci de vos réponses,
Eric
Pareil, pour moi. J’aimerais être béta testeur de cette solution. Merci de votre réponse, même non (au moins je serais fixé) 🙂
Pat
Vous pouvez aller voir sur : http://www.myconstellation.io/ pour demander un accès à la beta (privée)
Sebastien
Je vous envoie un mail d’invitation dans la semaine 😉
Hydro
Bonsoir. Je suis également intéressé par la Beta pour connecter ma centrale domotique au reste de mes équipements.
Jonathan
+1 j’aimerai avoir accès à la beta.
Hervé
Bonjour,
Je suis également intéressé par un accès à la béta. Par avance merci de votre réponse même négative (pour être fixé) j’ai déjà fait la demande via divers canaux restée sans réponse. Cela n’empêchera pas la lecture de votre excellent blog.
Bouv
Bonjour,
Idem, je suis intéressé pour un accès à la bêta. Merci de me tenir informer de la réponse (positive ou négative).
Eric
Hello, merci ! Tu nous aurais pas oublié ? J’ai hâté d’avoir cet accès.
Hector
Bonjour Sébastien,
Je suis toujours intéressé pour un accès à la bêta, voir même à une alpha, voir même si tu peux envoyer des sources par email… 🙂
A très prochainement.
Alex
Hey ! Moi aussi je serai super intéressé de tester la beta et commencer à bidouiller avec les API 🙂
marc
Bonjour,
Je n’ai rien reçu pour tester la plateforme.
merci,
Eric
Peut-on avoir des nouvelles sur la mise à disposition de la platforme ? Peut-être un article pour nous tenir informé. Le manque de retour ou l’attente doit être frustrante pour pas mal de gens qui attendent avec impatience de mettre les mains dans le cambouis, moi le premier. Ma domotique attends de tes nouvelles Seb !
marc
je confirme, rien reçu.
On se pose des questions !
merci,
Trackback: Créez votre “Home Analytics” : l’analyse et le reporting de votre domotique, informatique et objets connectés avec ElasticSearch, Graylog, Kibana et la plateforme Constellation - Sebastien.warin.fr
Trackback: Contrôlez votre maison avec la télécommande de la TV
Jojo
Bonjour. Super tuto.
J’ai une question concernant l’alimentation. Sur du long terme l’alimentation par usb de la raspberry pi ne va pas poser pb ? Ca se fait automatiquement ? Pourquoi ne pas avoir alimenté la pi par son alim ?
Vous indiquez transformateur 5V USB desassemblé: lequel avec vous utilisé ?
Merci
René Pallut
Fabuleux, la série d’article basée sur « constellation »
Vraiment passionnant et motivant; il manque juste … constellation, dommage.
Achetez ma Ferrari… je livre le moteur dans 5 ans
Trackback: S-Watch : pilotez votre domotique et objets connectés depuis une montre Samsung Gear S2 ou comment développer des applications Tizen connectées à Constellation - Sebastien.warin.fr
Bastien
Bonjour Sébastien,
Votre article est très intéressant. J’avais cette idée également sauf que je ne savais pas par où commencer.
Je débute dans le monde Linux, Domotique et connais peu les langages de programmations.
J’ai réussi à installer le serveur constellation mais je bloque sur le package S-opener qui n’est plus dans les package en ligne.
Y a t il une raison où constellation a évolué et par conséquent S-opener n’est pas compatible.
Bref il va falloir que je trouve une alternative mais je vais garder le concept avec le portable.
Merci pour les informations et pour ton article.