lundi 23 décembre 2013

Domotique: Profiter des services Google ...

Utiliser Google Script pour s'interfacer avec le web
J'ai depuis un (trop) long moment de sérieux problèmes avec Google Calendar: il n'est pas rare (ça arrive au moins une fois par jour) qu'un RDV déclaré dans Google Calendar ne soit pas récupéré par l'API Python. je suis obligé de modifier ce RDV pour qu'il soit de nouveau détecté. Même problème si je supprime un RDV: celui-ci est toujours rapatrié par l'API Python, ce qui est gênant car il n'y a alors aucune solution pour rectifier le tir ...

J'ai décidé de prendre le problème à bras le corps: supprimer cette partie un peu faiblarde de mon installation pour la remplacer par une solution que je pense plus pérène à plusieurs égards, en profitant des services Google pour:
- récupérer les infos depuis Calendar,
- les mettre en forme, près à être traités par mon script Python,
- effectuer la remontée vers le site web pour suivre les évolutions de mon chauffage (j'ai aussi traité mon alarme, mais je préfère ne pas trop échanger à ce propos ...)

La solution est d'utiliser Google Script, pour récupérer les infos de Google Calendar, les stocker dans un tableur de Google Drive, que je viens lire avec un script Python. Résultat: une bonne partie de la charge de travail (lecture de Google Calendar pour les consignes de chauffage et de l'alarme) sont désormais dédiés à Google, plus à mon installation.
De même, je gérais autrefois l'historisation des courbes de températures et la génération de la page web. C'est désormais Google qui gère tout ça, au travers de l'exploitation directe des graphiques du tableur dans une page web de Google Sites.

L'idée derrière tout ceci est que je compte très prochainement modifier toute mon installation afin de la rendre 100% autonome, en rajoutant une carte Ethernet à mon Arduino. L'exploitation des données de Google se fera directement depuis le sketch de l'Arduino. Exit le PC qui tourne sous Python !

Logique des échanges
Le but de ce passage sous Google Script est de:

  • Lire cycliquement les données de Google Calendar de la journée courante, puis les copier dans un tableur Google. Google Script permettant de le faire toutes les minutes, je ne me suis pas privé: ça rend les prises en comptes plus rapides lors de changements, et la charge est chez Google ...
  • Depuis le script Python, lire cycliquement le contenu de ce tableur Google. Je n'ai pas changé le taux qui j'avais dans mon ancienne version (de l'ordre de 5mn)
  • Depuis le script Python, envoyer cycliquement les températures des sondes, l'état de commande de chauffage (en route ou pas), la valeur de la température de consigne. Dans l'ancienne version, ces lectures étaient stockées toutes les 5mn, et envoyées vers le site web toutes les demi-heures, ce qui faisait que ce n'était guère exploitable sur le coup. Désormais, j'envoie ces données dans un autre tableur Google toutes les 3mn
  • Générer les indicateurs (températures courantes) et les historiques. Les indicateurs et les courbes que l'on peut générer dans le tableur Google sont directement exploitables dans des pages web d'un Google site. Les historiques sont établis par Google script à partir des données sauvegardées toutes les 3 minutes: le script me fait des moyennes des mesures toutes les 15mn pour m'afficher un historique sur 4 heures, et des moyennes toutes les heures pour un historique à 48h
Le résultat...
Rendez-vous sur le site pour voir ce que ça donne !

Les scripts
Concernant Google Script, vous trouverez une approche dans ce post.
Côté Python, c'est ici qu'il faut aller voir ... un post arrive prochainement

jeudi 7 février 2013

Domotique: Echanger des SMS avec son Arduino

Mon Arduino m'envoie des SMS (et je le lui rends bien ...)
J'ai récemment complété mon installation domotique existante (une commande de chauffage décrite dans ce blog, ainsi qu'un système d'alarme ... pas décrit ...) avec un système de commandes (et de retours de la part de mon installation) par SMS interposés.
Plutôt que d'investir dans un shield officiel (qui tourne autour de 50 euros, ce que je trouve hors de prix), j'ai préféré la réutilisation d'un ancien téléphone portable qui trainait dans un de mes tiroirs: un Sagem MyX4.
Avantage: ce téléphone a encore un port série, et se branchait sur un port DB9 de PC.
En interfaçant mon Arduino sur le câble série d'origine, moyennant le passage par un adaptateur MAX232 acheté sur e-bay, il est très simple de communiquer avec lui.

Certains téléphones GSM sont en effet équipés d'un modem, et acceptent les commandes AT.
Le protocole AT permet, au moyen d'une simple ligne de commande, d'envoyer ou lire un SMS.

Compatibilité AT
Tous les téléphones GSM n'étant pas équipés d'un modem, un petit test préliminaire (depuis un PC) s'impose.
Tout d'abord, il faut relier le téléphone à un port série du PC, puis configurer (si possible, ce qui est normalement le cas des anciens téléphones se branchant sur port série) la vitesse du port série. Le MyX4 permet par exemple de faire des échanges à une vitesse maxi de 38400bd.
Lancer ensuite un hyper terminal sous Windows, en choisissant la configuration suivante:

Dans le terminal, saisir ensuite (attention, il va falloir suivre, c'est complexe !):
AT puis appuyer sur Entrée

Si le téléphone est compatible, il va répondre:
OK

Vous pouvez prendre une pause bien méritée ...

Format des SMS
Saisir ensuite AT+CMGF?
Si le téléphone répond 1, c'est gagné, vous pourrez envoyer des SMS au format texte. Attention cependant, le format texte implique des envois avec du texte simple sans caractères accentués.
Si le téléphone répond 0:
Saisir AT+CMGF=1 pour tenter de forcer le mode texte.
Si le téléphone répond ERROR, votre tel n'acceptera d'envoyer / recevoir des SMS qu'au format PDU. Allez donc faire un petit tour sur cette page ...

Envoyer un SMS
Tapez AT+CMGS="num_tel_cible" puis Entrée
Un prompt > s'affiche
Tapez le contenu du SMS, puis CTRL-Z pour envoyer
Le téléphone va vous répondre quelque chose du genre: +CMGS:01

Après quelques secondes, votre destinataire va recevoir votre SMS

Recevoir un SMS
Il va falloir déterminer où sont stockés vos SMS reçus. C'est normalement configurable dans le téléphone, mais une commande AT va vous permettre de vous aider. Les SMS peuvent être soit stockés dans la mémoire du téléphone (ME), soit sur la sim (SM).
Si vous lisez à un endroit où il n'y a pas de SMS, le téléphone ne vous répondra rien lors de vos demandes de lecture.
Choisissez tout d'abord la zone de stockage. Par exemple, pour lire la mémoire du téléphone, tapez:
AT+CPMS="ME"

Pour lire les SMS, tapez ensuite AT+CGML="ALL"
Si un/des SMS se trouvent dans la zone choisie, vous aurez droit à une réponse du genre:
+CGML:900,"REC UNREAD",+336xxxxxxxx",,12/02/13,19/99/99+02"
CONTENU DU SMS

Effacer les SMS
Pour supprimer un SMS, il suffit (normalement) de saisir:
AT+CGMD=Indice_du_SMS
Il se trouve que cette commande ne fonctionne pas sur mon tel. Par contre la commande suivante fonctionne sans problème:
AT+CGMD=1,4
J'ai trouvé cette astuce je ne sais plus trop où. En gros elle doit supprimer tous les SMS reçus, ou au moins les 4 premiers. Toujours est-il que cette commande fonctionne.

Via Arduino
L'envoi ou la réception depuis l'Arduino est somme toute très simple: il suffit de fixer la vitesse du port série à la même vitesse que celui du téléphone, puis de communiquer via Serial.print ou Serial.read.
Pour ma part les commandes implémentées sont assez simples: Th20 pour fixer le chauffage à 20°, ou ThOff pour l'éteindre. Même chose pour l'alarme: AlaOn ou AlaOff.

Mon installation domotique me répond à toute mes commandes pour m'indique si elle a bien pris en compte. De même, je suis prévenu si l'alarme se déclenche ...

Sans trop investir, il est donc possible de s'ouvrir pas mal de possibilités. Et merci à Free, qui permet de faire tout ça pour 2€ par mois !



Une solution alternative, 2 euros moins cher !

Moyennant quelques restrictions, vous n'aurez besoin ni d'abonnement Free, ni même de modem !

Il y a quand même 4 (plus ou moins grosses) limites:
- seul votre Arduino pourra vous sms-er
- il faudra éviter les surconsommation sous peines de blocages (temporaires), mais cette limitation est contournable si vous avez les data actives sur votre téléphone (pour recevoir les notifications de l'agenda Google)
- seul le titulaire de l'agenda Google sera prévenu
- il faut que votre Arduino puisse communiquer avec un PC (via la liaison série par exemple)


Il suffit pour cela de détourner un peu l'utilisation de base de Google Calendar.

A chaque fois que vous créez une tâche dans l'agenda Google, il est possible de paramétrer cette tâche afin d'être prévenu par SMS, alerte ou e-mail.
Il suffit donc de faire un petit script pour qu'il crée une tâche avec demande de SMS et/ou de notification, et vous recevrez ce SMS et cette notif après quelques secondes. Après essais d'une 20aine de tâches (sur une courte période, il s'avère que les SMS ne passent plus (Google doit faire un blocage) mais ça revient très vite, et ça ne coupe même jamais si on n'abuse pas.
Dans tous les cas, les notifications, elles passent toujours (mais il faut garder les data actives sur le téléphone récepteur)

Côté Arduino
En partant du principe que votre Installation Arduino détecte l'évènement à notifier par SMS, et que le PC fait le traitement de création de tâche, autant dire que la notification Arduino->PC est plutôt simple: un simple Serial.Println d'une chaine de caractères quelconque fera l'affaire.

Côté PC
Google fournit une API permettant de créer des évènements (ou de les lire, comme dans mon installation de commande de chauffage) avec Calendar.
Pour ma part j'ai choisi de m'interfacer avec Calendar via des scripts Python.

Voici donc le script permettant de créer ces évènements:


try:
    from xml.etree import ElementTree
except ImportError:
    from elementtree import ElementTree
import gdata.calendar.service
import gdata.service
import atom.service
import gdata.calendar
import atom
import base64
import time

def send_sms(message_text):
    cal_client = gdata.calendar.service.CalendarService()
    cal_client.email = "identifiant GMAIL"
    cal_client.password = "mot de passe GMAIL"
    cal_client.source = 'calendar-sms-misuse-1.0'
    cal_client.ProgrammaticLogin()

    event = gdata.calendar.CalendarEventEntry()
    event.title = atom.Title(text=message_text)
    event.content = atom.Content(text="")

    
    # Les notifs par SMS ne fonctionnent pas si elles ne sont pas programmées pour être déclenchées
    #  au moins 5mn avant le début de la tâche
    # On crée donc une tache commencant dans 6mn, d'une durée de 1mn
    start_time = time.strftime('%Y-%m-%dT%H:%M:%S.000Z', time.gmtime(time.time()+(6*60)))
    end_time = time.strftime('%Y-%m-%dT%H:%M:%S.000Z', time.gmtime(time.time() + 7*60))
    when = gdata.calendar.When(start_time=start_time, end_time=end_time)
    # On crée une alerte à 5mn:

    # Dans le cas où on veut être alerté par tous les moyens possibles
        reminder = gdata.calendar.Reminder(minutes=5, extension_attributes={"method":"all"})
    # Dans le cas où une notif suffit
        reminder = gdata.calendar.Reminder(minutes=5, extension_attributes={"method":"alert"})
    # et dans le cas d'un SMS
        reminder = gdata.calendar.Reminder(minutes=5, extension_attributes={"method":"alert"})
        
    when.reminder.append(reminder)
    event.when.append(when)

    cal_client.InsertEvent(event, '/calendar/feeds/default/private/full')



send_sms("contenu du SMS")
En espérant que ces 2 méthodes vous soient utiles !