Commuter un relais par SMS

Commuter un relais par SMS

Récemment, on nous a demandé si nous avions une solution pour communiquer avec nos modules en utilisant des SMS. Nous avons bien le projet de sortir une version GSM de notre YoctoHub, mais pour l'instant nous nous concentrons sur le YoctoHub-Wifi et plusieurs autres nouveaux modules. Cependant, en utilisant un téléphone Android et avec un peu de programmation, il est tout à fait possible de communiquer avec un module Yoctopuce par SMS. Une utilisation classique est d'allumer et éteindre le chauffage de son chalet à distance.


Pour illustrer ce cas nous allons écrire une application Android pour contrôler n'importe quel relais Yoctopuce en envoyant un SMS. Il y a encore une année on n'aurait probablement pas utilisé ce genre d'approche car elle aurait tout de suite fait exploser le budget, mais de nos jours, les téléphones Android avec le support USB On The Go se sont démocratisés et il est possible de trouver certains modèles à moins de CHF 250.--.

Le kit "relais SMS"

Un Galaxy Note II, des modules Yoctopuce, et Smart Dock pour relier le tout
Un Galaxy Note II, des modules Yoctopuce, et Smart Dock pour relier le tout



Nous allons avoir besoin d'un téléphone Android (au minimum Android 4.0), d'un ou plusieurs modules Yoctopuce qui utilisent des relais (Yocto-Relay, Yocto-PowerRelay, Yocto-MaxiRelay, Yocto-MaxiCoupler, Yocto-LatchedRelay) et d'une Samsung Smart Dock ou d'un câble USB OTG en Y si vous cherchez une solutions moins coûteuse.

L'application Android

Il faut installer une application qui va écouter tous les SMS entrant et qui va commuter un relais si le message contient les bons mots clefs. Le code source est disponible sur GitHub.

Au lieu d'utiliser directement le nom du relais dans le SMS, nous allons définir un interrupteur logique qui contient les informations suivantes:

  • le nom de l’interrupteur logique ( par exemple "mon chauffage")
  • la logique de l’interrupteur (contact de travail ou contact de repos)
  • le hardwareId du relais à utiliser (par exemple "HI8PWER1-1234.relay2")

la page de config pour notre interrupteur logique music qui doit commuter le relais YLTCHRL1-12B98.relay1


Une fois que vous avez créé votre configuration avec l'application, vous pouvez tester directement que tous vos interrupteurs fonctionnent comme prévu, sans envoyer de SMS.

Pour contrôler vos interrupteurs logiques par SMS, rien de plus simple: il suffit d'envoyer un SMS à votre téléphone avec la commande à exécuter et du nom du relais logique. Les commandes sont:

  • "switch" change l'état de l’interrupteur dans tous les cas (s'il est éteint on l'allume, si il est allumé on l’éteint).
  • "switch on" allume l’interrupteur (si il est éteint on l'allume, s'il est allumé on ne fait rien).
  • "switch off" allume l’interrupteur (si il est éteint on ne fait rien, s'il est allumé on l'éteint).

Donc pour allumer notre chauffage il suffit d'envoyer un SMS avec le texte "switch on mon chauffage". L'application n'a pas besoin d'être en premier plan, ni même lancée: dès que vous avez installé l'application, elle est automatiquement lancée en tache de fond par Android.

  



Comment ça marche

Nous avons déjà détaillé dans plusieurs articles comment interagir avec nos modules sous Android. Pour récupérer les SMS c'est en fait assez simple. Il faut ajouter dans son manifeste le fait que l'on va écouter les SMS du téléphone avec <uses-permission android:name="android.permission.RECEIVE_SMS" />. Cette ligne va avertir l'utilisateur lors de l'installation que cette application pourra accéder à ses SMS.

Pour récupérer le contenu des SMS, il faut implémenter un BroadcastReceiver qui sera appelé à chaque SMS. Le code suivant va surcharger la méthode onReceive et reconstruire le texte du SMS ainsi que le numéro de l'expéditeur. Il faut ensuite décoder le texte du SMS pour trouver la commande à exécuter et le nom de l'interrupteur à activer.

public class SMSReceiver extends BroadcastReceiver{

    @Override
    public void onReceive(Context context, Intent intent) {
        Bundle bundle = intent.getExtras();
        if (bundle != null) {
            Object[] pdus = (Object[]) bundle.get("pdus");
            SmsMessage[] messages = new SmsMessage[pdus.length];
            for (int i = 0; i < pdus.length; i++)
                messages[i] = SmsMessage.createFromPdu((byte[]) pdus[i]);
            for (SmsMessage message : messages) {
                String msg = message.getMessageBody();
                String from = message.getOriginatingAddress();
                if(!parseIncomingSMS(context, from, msg)) {
                    this.abortBroadcast();
                }
            }
        }
    }


    protected  boolean parseIncomingSMS(Context ctx, String from,String msg) {
        ...
        Cmd cmd = parseCommand(msg)
        YSwitch yswitch = findSwitchFromMessage(msg);
        switch (cmd) {
        case TOGGLE:
            Toast.makeText(ctx, appname+" : toggle " +target,
                           Toast.LENGTH_LONG).show();
            yswitch.setOn(ctx,!s.isOn());
            break;
        case ON:
            Toast.makeText(ctx, appname+" : switch on " +target,
                          Toast.LENGTH_LONG).show();
            yswitch.setOn(ctx,true);
            break;
        case OFF:
            Toast.makeText(ctx, appname+" : switch off " +target,
Toast.LENGTH_LONG).show();
            yswitch.setOn(ctx,false);
            break;
        default:
            return false;
        }
        return true;
    }

}


Il faut ensuite enregistrer ce BroadcastReceiver dans le manifeste pour que l'OS lance automatiquement l'application et appelle la méthode que nous venons d'implémenter.

<receiver android:name=".SMSReceiver">
    <intent-filter android:priority="100">
        <action android:name="android.provider.Telephony.SMS_RECEIVED" />
    </intent-filter>
</receiver>



La classe YSwitch représente un interrupteur logique qui va stocker son nom, son état (on ou off), sa logique de travail (inversé ou non), et le hardwareId de son relais. La méthode setOn va
vérifier si il a besoin de modifier l’état du relais et si nécessaire envoyer un message à notre service qui est exécuté en tache de fond.

public class YSwitch {
    private String  mName;
    private boolean mOn;
    private boolean mInverted;
    private String  mHardwareId;


    public YSwitch() {
        mName ="";
        mOn = false;
        mInverted = false;
        mHardwareId = "";
    }

    ....

    public boolean isOn() {
        return mOn;
    }

    public void setOn(Context ctx, boolean newOn) {
        if(mOn == newOn)
            return;
        mOn = newOn;
        Intent msgIntent = new Intent(ctx, YoctoService.class);
        msgIntent.putExtra(YoctoService.PARAM_IN_CMD,
                           YoctoService.COMMANDS.SWITCH);
        msgIntent.putExtra(YoctoService.PARAM_IN_YRELAY, mHardwareId);
        if (mInverted)
            newOn = !newOn;
        msgIntent.putExtra(YoctoService.PARAM_IN_SWITCH_ON,newOn);
        ctx.startService(msgIntent);
    }

    ...
}



Notre service reçoit l'Intent que nous avons préparé dans la méthode setOn() et appel la méthode set_output() de notre librairie. Dans cette exemple nous utilisons uniquement des modules connecté par USB, dans touts les cas l'appel ne devrait pas prendre plus d'une millisecondes. Il serait possible de se passer du service et d'appeler directement les méthodes de la librairie Yoctopuce dans la methode setOn, mais Google recommande d’exécuter dans un thread séparé le code qui peut ralentir l'interface. Si nous avions utilisé des modules connectés à un YoctoHub-Ethernet il n'est pas impossible qu'Android mette plusieurs secondes à établir la première connexion et dans ce cas le service prend tout son sens.

...
@Override
protected void onHandleIntent(Intent intent) {
    String hwid;
    YRelay relay;
    COMMANDS cmd = (COMMANDS) intent.getSerializableExtra(PARAM_IN_CMD);
    if(cmd==null)
        return;
    switch (cmd) {
        case QUIT:
            Log.d(TAG, "Quit YoctoService");
            stopSelf();
            break;
        case SWITCH:
            hwid = intent.getStringExtra(PARAM_IN_YRELAY);
            boolean on  = intent.getBooleanExtra(PARAM_IN_SWITCH_ON,false);
            Log.d(TAG, "Swich " + hwid + (on ? " on" : " off"));
            relay = YRelay.FindRelay(hwid);
            try {
                relay.set_output(on ? YRelay.OUTPUT_ON : YRelay.OUTPUT_OFF);
            } catch (YAPI_Exception e) {
                e.printStackTrace();
            }
            break;
            ....
    }
}
...



Voilà, vous savez maintenant comment contrôler vos modules Yoctopuce par SMS en utilisant un téléphone Android.

Commenter aucun commentaire Retour au blog












Yoctopuce, get your stuff connected.