Action domotique déclenchée par la réception d’un SMS

Mon système domotique est entièrement contrôlable depuis l’intérieur comme l’extérieur de la maison, donc depuis le réseau local ou depuis Internet. Il est donc possible d’allumer ou éteindre une lampe ou encore activer/désactiver l’alarme à distance sans être présent dans la maison.

Ces opérations sont possibles car le serveur qui héberge l’interface web est branché sur le réseau local de la maison.

Problème : si la connexion internet tombe chez moi, je ne suis plus informé d’une alarme, je ne peux plus agir dessus.
La solution selon moi est de pouvoir déclencher des scénarios à l’aide d’un téléphone mobile, qui, lui, sera toujours joignable.

Comment donc déclencher des opérations à l’aide d’un mobile ?

  • Par le fait d’être appelé : trop dangereux, n’importe qui connaissant le numéro pourrait déclencher l’action, sans même s’en apercevoir.
  • Par commande vocale : plutôt complexe, il faut de la reconnaissance vocale derrière … peut être plus tard
  • par réception d’un SMS : parfait ! Un ordinateur sait très bien gérer les éléments textuels

Le matériel utilisé

  • une carte SIM
  • un nokia 3220 (ressorti d’un tiroir où il dort depuis 3 ans !)
  • un câble USB/série DKU acheté pour trois fois rien en Chine à l’époque

Communication avec le PC

Tout d’abord il faut que le PC reconnaisse le téléphone. L’objectif initial était de le faire fonctionner sous linux.
Apres plusieurs jours d’acharnement entre le drivers, le firmware du câble etc j’ai reporté cette étape à plus tard et suis donc passé sous Windows.
J’ai pu retrouver dans les méandres d’internet un drivers fournit par TI qui fonctionne.

Drivers TIUSB3410 : TEXAS-INSTRUMENTS_TUSB3410-U_A03_R286478.exe

Tout d’abord, vérifions que le dialogue avec le téléphone fonctionne.

  1. Ouvrez un Hyper Terminal (si vous êtes sous Win >Vista il n’est plus présent par défaut, vous pourrez facilement le retrouver via google)
  2. Ouvrez une connexion standard sur le port COM monté par Windows sur le cable USB
  3. Lancez la commande « AT » et vérifiez que vous avez bien un « OK » en retour

La démarche complète peut être récupérée sur le site wiki pour développeurs de nokia.

 Scriptage

Une fois que l’on arrive à dialoguer à l’aide des commandes AT standard, il est possible d’écrire un script pour automatiser la récupération de message.
J’ai choisi de le faire en python car une librairie de dialogue Série existe déjà : pySerial. De plus, je souhaite à terme brancher le telephone sur une machine Linux, Python est portable d’un OS à l’autre.

 Ouverture du flux

Voici un premier script qui ouvre simplement le flux sur le port COM3 sous Windows et essaye 5 fois d’envoyer une commande AT.

#initialization and open the port
#possible timeout values:
#    1. None: wait forever, block call
#    2. 0: non-blocking mode, return immediately
#    3. x, x is bigger than 0, float allowed, timeout block call
ser = serial.Serial()
ser.port = "\\.\COM3"
#ser.port = "/dev/ttyS2"
ser.baudrate = 9600
ser.bytesize = serial.EIGHTBITS #number of bits per bytes
ser.parity = serial.PARITY_NONE #set parity check: no parity
ser.stopbits = serial.STOPBITS_ONE #number of stop bits
ser.timeout = 2             #non-block read
ser.xonxoff = False     #disable software flow control
ser.rtscts = True     #disable hardware (RTS/CTS) flow control
ser.dsrdtr = True       #disable hardware (DSR/DTR) flow control
ser.writeTimeout = 2     #timeout for write

try: 
    ser.open()
except Exception, e:
    print "error open serial port: " + str(e)
    exit()
if ser.isOpen():
	try:
		ser.flushInput() #flush input buffer, discarding all its contents
		ser.flushOutput()#flush output buffer, aborting current output 
		#and discard all that is in buffer

		#init
		print("connecting")
		print("---------------------------------------------")

		for i in range(1,5):
			print(" + try " + str(i) + "/5 :")
			ser.write("AT\x0D")
			response = ser.readline()
			print("   read data: " + response)
			if "OK" in response:
				print("   connexion established")
				break

		ser.close()
	except Exception, e1:
		print "error communicating...: " + str(e1)
else:
	print "cannot open serial port "

Commandes AT

Les commandes enchaînées sont les suivantes :

AT : tester de dialogue avec le telephone

AT+CMGF=1 : permet de se mettre en mode texte pour SMS

AT+CMGL= »ALL » : permet de lister tous les messages reçus

AT+CMGD=1 : permet de supprimer le message dont l’identifiant est 1.

Script final

Principe :

  1. test de la connexion au téléphone
  2. configuration en mode texte SMS
  3. récupération des messages SMS
  4. stockage des messages dans un tableau python
  5. traitement des messages stockés (en fonction d’un émetteur ou d’un contenu ou encore du temps, il est possible d’enrichir le script)
  6. suppression des messages traités
  7. bouclage
Les actions sont déclenchées à l’aide d’urllib qui va appeler un webservice en HTTP.

Script:

#!/usr/bin/python
import serial, time, urllib

def supprimer_msg (ser, ID_msg):
	etat_final = False

	ser.flushInput() 
	ser.flushOutput()

	print("  [supprimer_msg] write data: AT+CMGD="+str(ID_msg)+"\x0D")

	for i in range (1,5):

		ser.write("AT+CMGD="+str(ID_msg)+"\x0D")
		response = ser.readline()
		print("  [supprimer_msg] read data: " + response)
		if "OK" in response:
			print("   ok recieved")
			etat_final = True
			break

	return etat_final

def traitement_message_recu( ser ):

	messages_tab = []
	current_message = 0

	for i in range (1,15):

		response = ser.readline()
		print(" [traitement_message_recu] read data: " + response)

		if "OK" in response:
			print("   OK recieved")
			break

		elif "ERROR" in response:
			print("   ERROR")
			break

		elif "+CMGL" in response:
			response = response.translate(None, '"')
			data_msg = response.split(",")
			current_message = data_msg[0].translate(None, '+CMGL: ')
		elif len(response) < 3:
			print("   skip empty line")
			continue
		else:
			print("   Message Processing ID [" + str(current_message) + "]")
			messages_tab.append( [current_message, str(data_msg[2]), data_msg[4], response] )

	# data_msg
	# data_msg[0] : ID interne telephone
	# data_msg[1] : from
	# data_msg[2] : date recieved
	# data_msg[3] : message content

	print("Processing stored messages")
	for message in messages_tab:
		print("message [" + str(message[0]) + "] from ["+ message[1] +"] at ["+ message[2] +"]: " + message[3])

		# if data_msg[2] != "+33123456789":
			# print ("message "+ current_message + " : expediteur inconnu :" + data_msg[2])

		print("appel de webservice_phone");
		urllib.urlopen("http://serveur_domotique/dhomeCore/ws_phone.php?id=xxxfrom="+urllib.quote(message[1],"")+"&date="+urllib.quote(message[2],"")+"&content="+urllib.quote(message[3],"")+"&type=sms");
		print("call http://serveur_domotique/dhomeCore/ws_phone.php?id=xxx&from="+urllib.quote(message[1],"")+"&date="+urllib.quote(message[2],"")+"&content="+urllib.quote(message[3],"")+"&type=sms");

		if "coucou" in message[3]:
			print("appel de webservice_action");
			urllib.urlopen("http://serveur_domotique/dhomeCore/ws_action.php?id=xxx&valeur1="+message[3]);

		print("suppression du message [" + message[0] + "]")
		supprimer_msg(ser,message[0])

		#+CMGL: 1,"REC READ","+33123456789",,"12/12/15,17:54:41+04"
	urllib.urlcleanup()

	return

#initialization and open the port
#possible timeout values:
#    1. None: wait forever, block call
#    2. 0: non-blocking mode, return immediately
#    3. x, x is bigger than 0, float allowed, timeout block call
ser = serial.Serial()
ser.port = "\\.\COM3"
#ser.port = "/dev/ttyS2"
ser.baudrate = 9600
ser.bytesize = serial.EIGHTBITS #number of bits per bytes
ser.parity = serial.PARITY_NONE #set parity check: no parity
ser.stopbits = serial.STOPBITS_ONE #number of stop bits
ser.timeout = 2             #non-block read
ser.xonxoff = False     #disable software flow control
ser.rtscts = True     #disable hardware (RTS/CTS) flow control
ser.dsrdtr = True       #disable hardware (DSR/DTR) flow control
ser.writeTimeout = 2     #timeout for write
try: 
    ser.open()
except Exception, e:
    print "error open serial port: " + str(e)
    exit()
if ser.isOpen():
	try:
		ser.flushInput() #flush input buffer, discarding all its contents
		ser.flushOutput()#flush output buffer, aborting current output 
		#and discard all that is in buffer

		#init
		print("connecting")
		print("---------------------------------------------")

		for i in range(1,5):
			print(" + try " + str(i) + "/5 :")
			ser.write("AT\x0D")
			response = ser.readline()
			print("   read data: " + response)
			if "OK" in response:
				print("   connexion established")
				break

		#config
		print("configuring +CMGF=1")
		print("---------------------------------------------")

		for i in range(1,5):
			print(" + try " + str(i) + "/5 :")

			ser.write("AT+CMGF=1\x0D")
			print("   write data: AT+CMGF=1\x0D")
			response = ser.readline()
			print("   read data: " + response)
			if "OK" in response:
				print("   configuration completed")
				break

		print("looping")
		print("---------------------------------------------")

		while True:
			ser.flushInput() 
			ser.flushOutput()
			print("  get messages")
			ser.write("AT+CMGL=\"ALL\"\x0D")
			response = ser.readline()
			print("  read data: " + response)

			traitement_message_recu(ser)
			time.sleep(20)

		ser.close()
	except Exception, e1:
		print "error communicating...: " + str(e1)
else:
	print "cannot open serial port "

 

 

2 thoughts to “Action domotique déclenchée par la réception d’un SMS”

  1. Salut
    intéressant tout ça, je pense que bien entendu il faut un abonnement mobile pour la réception des sms? 😀

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée.