Connectivités

Atelier Open the Box

Gestion des équipements

Table des matières

1 ZigBee
2 EnOcean
3 Philips Hue
4 SimpleBee
5 Développer un nouveau Base Driver

Ce document a pour but de présenter les différentes technologies de connectivité d’équipements qui sont utilisables dans Open the Box, l’’infrastructure fournissant les base drivers compatibles. Pour chaque technologie, on présente au minimum :

  • les dépendances éventuellement nécessaires pour la configuration Maven ;
  • les références aux équipements pour la configuration Declarative Services ;
  • un exemple de code Java manipulant les services OSGi correspondants.

À noter que la partie ZigBee propose un tutoriel complet, présentant un exemple concret d’’application Open the Box.

1 ZigBee

Voir le tutoriel ZigBee.

2 EnOcean

EnOcean est un protocole radio utilisé dans les maisons et bâtiments tertiaires intelligents. Cette technologie a l’avantage d’utiliser peu d’énergie pour fonctionner, ce qui permet d’avoir des équipements sans batterie.

Le base driver EnOcean est disponible en open source via le site github d’Eclipse Smarthome. Ce site indique notamment comment mettre en œuvre le base driver open source EnOcean pour OSGi.

2.1 Equipements Enocean décris dans ce tutoriel

Détecteur de fumée Eltako Capteur de température Prise on/off
smoke detector Temperature sensor plug on/off

Le détecteur de fumée et le capteur de température seront automatiquement détectés par l’envoi périodique de données tandis que la prise a besoin d’être appairée, puisqu’aucune donnée n’est envoyée et que la prise utilise juste la fonction on/off

2.2 Appairage

La prise on/off doit donc être appairée comme tous les equipements Enocean pouvant recevoir des commandes.
Pour rentrer en mode d’appairage, appuyez sur le bouton pendant 5 secondes. La prise devrait alterner entre l’état on et off toutes les secondes pendant 30 secondes. A noter qu’il existe différentes facon de rentrer en mode appairage, ce processus devrait être décrit dans le manuel utilisateur.
il ensuite faut envoyer une trame d’appairage via le base driver. Cependant cette action n’est pas automatique. Cela veut dire qu’il faudra coder cette action dans l’application (ie coder un envoi de commande) et ensuite envoyer la trame d’appairage manuellement via l’application.

2.3 Maven

Dépendance dans le POM Maven sur le Base Driver OSGi EnOcean :

<dependency>
   <groupId>org.osgi</groupId>
   <artifactId>org.osgi.service.enocean</artifactId>
   <scope>system</scope>
   <version>6.0.0</version>
   <systemPath>${basedir}/lib/org.osgi.service.enocean-1.0.0.jar</systemPath>
</dependency>

2.4 Declarative Services

La référence EnOcean se déclare par Declarative Services.
Declarative Services are used to declare the EnOcean reference. DAns le code, le fichier se trouve ici : “src/main/resources/OSGI-INF/component-devices.xml”

<reference name="enoceanDevice" cardinality="0..n" policy="dynamic"
   interface="org.osgi.service.enocean.EnOceanDevice"
   bind="setEnoceanDevice" unbind="unsetEnoceanDevice" />
// target="”(DEVICE_FRIENDLY_NAME=an EnOcean device)"

2.5 Code Java

   private List<EnOceanDevice> enOceanDevices;
   public void setEnoceanDevice(EnOceanDevice device) {
      if (! enOceanDevices.contains(device)) {
         enOceanDevices.add(device);
      }
   }

Pour afficher la liste des propriétés lors d’un binding EnOcean, il suffit de rajouter le code Java suivant:

      public void setEnoceanDevice(EnOceanDevice device,Map properties) {
      if (! enOceanDevices.contains(device)) {
         enOceanDevices.add(device);
      }
	    Set set = properties.entrySet();
        Iterator it =  set.iterator();
        while (it.hasNext()) {
            Entry entry = (Entry) it.next();
            Object key = entry.getKey();
			Object value = entry.getValue();
             System.out.println(TAG, key + ", property (" + key 
               + "): " + value);
            System.out.println(TAG, "(" + value + ").getClass().getName()"
               + value.getClass().getName());           
        }
   }
   
   }

2.6 Événements

La lecture des payloads/frames EnOcean envoyées par les équipements EnOcean se fait par récupération d’événements (OSGi EventAdmin), il suffit donc d’implémenter l’interface org.osgi.service.event.EventHandler et sa méthode EventHandler.handleEvent(Event event).

2.6.1 Profils d’équipement EnOcean

Un profil d’équipement Enocean (EEP) classifie un type d’appareil, et par conséquent décrit comment communiquer avec ce type.
Le document de spécification EEP peut se trouver ici.

2.6.2 Capteur de température

Voici un exemple de code pour un capteur de température :

public void handleEvent(Event event) {   
   System.out.println(TAG, "> handleEvent(event: " + event);
   String[] pns = event.getPropertyNames();
   for (int i = 0; i < pns.length; i++) {
      System.out.println(TAG, "pns[" + i + "]: " + pns[i] + ", event.getProperty(" + pns[i] + "): "          
         + event.getProperty(pns[i]));
// pns[0]: enocean.device.profile.func, event.getProperty(enocean.device.profile.func): 2 
// pns[1]: enocean.device.profile.rorg, event.getProperty(enocean.device.profile.rorg): 165 
// pns[2]: enocean.device.chip_id, event.getProperty(enocean.device.chip_id): 8969457 
// pns[3]: enocean.device.profile.type, event.getProperty(enocean.device.profile.type): 5 
// pns[4]: enocean.message, event.getProperty(enocean.message): a5000035080088dcf100 
// pns[5]: event.topics, event.getProperty(event.topics): org/osgi/service/enocean/EnOceanEvent/MESSAGE_RECEIVED    
   }    
   String topic = event.getTopic();    
   if (topic.equals(EnOceanEvent.TOPIC_MSG_RECEIVED)) {
      String chipId = (String) event.getProperty(EnOceanDevice.CHIP_ID);       
      String rorg = (String) event.getProperty(EnOceanDevice.RORG);       
      String func = (String) event.getProperty(EnOceanDevice.FUNC);       
      String type = (String) event.getProperty(EnOceanDevice.TYPE);       
      EnOceanMessage data = (EnOceanMessage) event.getProperty(EnOceanEvent.PROPERTY_MESSAGE);       
      String displayId = Utils.printUid(Integer.parseInt(chipId));       
      String profile = rorg + "/" + func + "/" + type;       
      System.out.println(TAG, "> MSG_RECEIVED event : sender=" + displayId + ", profile = '" + profile + "'");
      System.out.println(TAG, "Try to identify the device that has sent the just received event (e.g. is it an A5-02-05 device - a temperature sensor range 0°C to +40°C ?).");
      if ("165".equals(rorg)) {
         // hex 0xa5 == int 165.
         if ("2".equals(func)) {
            if ("5".equals(type)) {
               System.out.println(TAG, "This event has been sent by an A5-02-05 device.");
               System.out.println( TAG, "The end of page 12, and the beginning of page 13 of EnOcean_Equipment_Profiles_EEP_V2.61_public.pdf specifies how to get the temp°C value starting from an EnOcean telegram.");
               // propertyNames[4]: enocean.message, event.getProperty(propertyNames[4]): a5000035080088dcf100
               byte[] payload = data.getPayloadBytes();
               System.out.println(TAG, "payload: " + payload + ", payload.length: " + payload.length);
               for (int j = 0; j < payload.length; j++) {
                  System.out.println(TAG, "payload[" + j + "]: " + payload[j]);
               }
               byte rawTemperatureDB1InHexAsAByte = payload[2];
               float rawTemperatureDB1InNumberAsADouble = rawTemperatureDB1InHexAsAByte;
               System.out.println(TAG, "rawTemperatureDB1InNumberAsADouble: " + rawTemperatureDB1InNumberAsADouble);
               if (rawTemperatureDB1InNumberAsADouble < 0) {
                  System.out.println(TAG, "rawTemperatureDB1InNumberAsADouble is negative, so let's convert rawTemperatureDB1InNumberAsADouble to unsigned 0..255 value instead of -127..128 one.");
                  rawTemperatureDB1InNumberAsADouble = rawTemperatureDB1InNumberAsADouble * -1
                     + 2 * (128 + rawTemperatureDB1InNumberAsADouble);
                  System.out.println(TAG, "rawTemperatureDB1InNumberAsADouble: "
                     + rawTemperatureDB1InNumberAsADouble);
               } else {
                  System.out.println(TAG, "rawTemperatureDB1InNumberAsADouble is positive, everything is ok.");
               }
               // Now let's apply the formula: (rawTemperatureDB1InNumberAsADouble-255)*-40/255+0 = temp in celsius.
               double tempInCelsius = (rawTemperatureDB1InNumberAsADouble - 255) * -40 / 255 + 0;
               System.out.println(TAG, "tempInCelsius: " + tempInCelsius);
            } else {
               System.out.println(TAG, "This event has NOT been sent by an A5-02-05 device. TYPE is NOT equal to 5.");
            }
         } else {
            System.out.println(TAG, "This event has NOT been sent by an A5-02-05 device. FUNC is NOT equal to 2.");
         }
      }
   }
}

2.6.3 Détecteur de fumée

Exemple d’un détecteur de fumée Eltako :

   if ("246".equals(rorg)) {
	  // the rorg in the Enocean terminology defines the type of the device.
	  // the Enocean profile specification lists all the existing rorgs in hexadecimal
      // suppose Eltako Detector
	
      byte[] payload = data.getBytes();
      if (payload[1] == 0x10) {
         System.out.println("fire");
         handleFire(chipId);
      } else if (payload[1] == 0x00) {
         System.out.println("normal situation");
         handleNormalSituation(chipId);
      }
   }

2.7 Invocation de commande

Il est possible, depuis OSGi, de faire émettre/envoyer des payloads/frames EnOcean via le dongle USB EnOcean.

Invoquer une commande revient simplement, dans l’implémentation actuelle, à envoyer une trame codée “en dur” via le base driver.
Pour cela, on a besoin d’avoir l’objet EnoceanDevice, de créer un objet RPC et d’utiliser la méthode invoke. L’objet RPC contient l’information du type de trame souhaitée. On peut créer une classe RPC en dur pour chaque trame ou bien créer une classe RPC paramétrable comme suit :

public class CustomEnoceanRPC implements EnOceanRPC {
		
   //let's define here all the frames supported by the base driver. from now they are hardcoded as is:
   
   public static String APPAIR_FRAME="HARDCODED_UTE_APPAIR";
   // 55 00 0D 07 01 FD D4 D2 01 08 00 3E FF 91 00 00 00 00 00 03 01 85 64 14 FF 00 E9
	
   public static String ON_FRAME="HARDCODED_VLD_TURN_ON";
   // 55 00 09 07 01 56 D2 01 00 01 00 00 00 00 00 03 01 85 64 14 FF 00 64
	
   public static String OFF_FRAME="HARDCODED_VLD_TURN_OFF";
   // 55 00 09 07 01 56 D2 01 00 00 00 00 00 00 00 03 01 85 64 14 FF 00 F0
	
   public static String PTM210_APPAIR_FRAME="HARDCODED_APPAIR_TURN_ON";
   // 55 00 07 07 01 7A F6 50 00 29 21 9F 30 01 FF FF FF FF 31 00 37
   //PTM210 represents a classical Enocean switch, the frame on is also considered as a pairing one.
	
   public static String PTM210_ON_FRAME="HARDCODED_APPAIR_TURN_ON";
   // same frame as above.
	
   public static String PTM210_OFF_FRAME="HARDCODED_TURN_OFF";
   // 55 00 07 07 01 7A F6 70 00 29 21 9F 30 01 FF FF FF FF 2D 00 62
   //the switch frame off
   private String frame;
   private int senderId = 0x00000000;
	
   public CustomEnoceanRPC (String frame) {
      this.frame = frame;
   }

   public void setSenderId(int chipId) {
      this.senderId = chipId;
   }

   public int getSenderId() {
      return senderId;
   }

   public byte[] getPayload() {
      return null;
   }

   public int getManufacturerId() {
      return -1;
   }

   public int getFunctionId() {
      return -1;
   }

   public String getName() {
      // TODO Auto-generated method stub
      return frame;
   }
}

création une méthode pour gérer l’invocation :

   public void invokeFrame(EnOceanDevice eod, String FrameName) {

      // Let's create an enoceanrpc in order to pair with the plug.
      EnOceanRPC appairRPC = new CustomEnoceanRPC(FrameName);
      EnOceanHandler handlerTurnOnRpc = new EnOceanHandler() {
         public void notifyResponse(EnOceanRPC enOceanRPC, byte[] payload) {
            System.out.println( "enOceanRPC: " + enOceanRPC + ", payload: " + payload);
         }
      };

      if (eod == null) {
         System.out.println("There is no EnOceanDevice --> So no invocation is possible.");
      } else {
         System.out.println("There is an EnOceanDevice --> So invocation is possible.");
		 //invoke the command described in the RPC
         eod.invoke(appairRPC, handlerTurnOnRpc);

      }

   }

Utilisation de cette méthode pour une commande OFF :

invokeFrame(device, CustomEnoceanRPC.PTM210_OFF_FRAME);

3 Philips Hue

Les lampes Hue sont des lampes connectées permettant des actions à distance (régler l’intensité, la couleur, clignoter, etc.)
Elles sont livrées avec un « Hue Bridge » qui joue le rôle d’une passerelle et centralise les commandes au réseau d’ampoules (ZigBee Light Link).
Le base driver OSGi est fourni sous forme d’un bundle. Après son démarrage, le base driver représente le bridge Hue par un service OSGi quand il est disponible sur le réseau et chaque lampe disponible par un service OSGi d’une autre interface.

3.1 Maven

Dépendance dans le POM Maven sur le Base Driver Open the Box pour Hue :

<dependency>
   <groupId>com.orange.openthebox.hab</groupId>
   <artifactId>hue.basedriver</artifactId>
   <version>1.0.3-SNAPSHOT</version>
   <scope>compile</scope>
</dependency>

Ce base driver fait lui-même appel à un bundle hue.controller qui s’’occupe de la connexion effective avec le bridge Hue via une interface REST.

3.2 Declarative Services

<reference name="hueBridge" cardinality="1..1" policy="dynamic"
   interface="com.orange.openthebox.hab.HueBridgeDevice"
   bind="setHueBridge" unbind="unsetHueBridge" />
<reference name="hueLight" cardinality="0..n" policy="dynamic"
   interface="com.orange.openthebox.hab.HueLightDevice"
   // target=”(DEVICE_FRIENDLY_NAME=Hue Lamp *)”
   bind="setHueLight" unbind="unsetHueLight" />

Remarque : le bridge Hue n’’est pas forcément indispensable si l’’on veut gérer des lampes Hue individuellement ; il sert si l’’on utilise la notion de groupe de lampes, pour donner des effets communs à un ensemble de lampes gérées par le même bridge.

3.3       Code Java

   private List<HueLightDevice> lights;
   public void setHueLight(HueLightDevice light) {
      if (!lights.contains(light)) {
         lights.add(light);
      }
   }

   HueLightDevice light = lights.get(0);
   light.setState(true, 255, 0, 46920, 0, 1);

La méthode HueLightDevice.setState(boolean on, int brightness, int saturation, int hue, int effect, int alert)permet de manipuler :

  • l’’allumage de la lampe ;
  • la luminosité / intensité (valeur de 0 à 255, ou -1 sinon) ;
  • la saturation (valeur de 0 (blanc) à 255, ou -1 sinon) ;
  • la couleur, en code hexa (valeur de 0 à 65535, ou -1 sinon) ; exemples : jaune (12750), vert clair (25500), vert foncé (36210), bleu (46920), violet (56100), rouge (65280)…
  • l’’effet Hue (0 : pas d’’effet, 1 : « color loop », la lampe passe par toutes les couleurs) ;
  • le mode alerte (0 : pas d’’effet, 1 : un cycle de respiration, 2 : cycles toutes les 30s).

La classe HueBridgeDevice permet de gérer un groupe de lampes, et propose quelques « effets » d’éclairage prédéfinis sur ces lampes :

  • setWakeUp : turns on the lights, white color, medium brightness
  • setMeal : turns on the lights, light orange color, medium brightness
  • setNight : turns off the lights
  • setHomeCinema : turns on the lights, red color, low brightness
  • setParty : turns on the lights, color loop effect, low brightness

4 SimpleBee

Voir le tutoriel SimpleBee.

5 Développer un nouveau Base Driver

Voir la page Développer un nouveau base driver.