Système SMS intelligent pour les horaires de marée et la météo

Last modified date

Comments: 0

Préface

L’idée est de créer un système qui vous répond automatiquement par SMS. Par exemple vous envoyez marée brest et vous recevez les horaires de marée. Même principe pour la météo : vous envoyez météo paris, et vous recevez la météo de la ville.

Prérequis

  • Un PC / Raspberry Pi / Banana Pi / quelque chose sous Linux (le projet est réalisable avec Windows, mais je n’expliquerai que pour Linux)
  • Une clé 3G compatible avec Linux
  • Une carte SIM (forfait Free à 2€ par exemple)
  • [facultatif] Un hub USB (avec alimentation externe) pour alimenter la clé 3G
  • Une base de données des marées (je reparlerai après)
  • De la persévérance

Bon, comme je l’ai déjà dit, le but c’est d’envoyer un SMS comme par exemple « marée <ville> <date facultative> » et l’utilisateur reçoit la horaires et hauteurs de marée pour la date indiquée. Si il n’y a pas de date, on suppose que l’utilisateur souhaite obtenir les informations du jour actuel.

La base de données de marées

Nous avons cependant un problème majeur (que nous retrouverons aussi pour la météo) : le SEO. Cet acronyme signifie Search Engine Optimization. C’est ce qui va faire en sorte que même si vous faites des fautes d’orthographe dans une recherche sur un site / Google, vous obtenez tout de même des résultats correspondant à ce que vous recherchiez. Dans notre cas, le problème porte sur le nom de la ville : par exemple le port d’Aber Wrac’h. Si l’utilisateur orthographie mal le nom, il n’y aura pas de correspondance dans la base de données et donc pas de résultat.

J’utilise donc un autre système : un numéro associé à un port. Avant d’envoyer quoi que ce soit, l’utilisateur devra envoyer le SMS suivant : « marée liste-ports » et recevra une liste dans laquelle chaque port est associé à un numéro. Il ne lui reste donc plus qu’à envoyer « marée <numéro du port> <date facultative> ».

Or, pour cela vous devez posséder un base de données avec la liste des ports, ainsi que les données de marée pour chacun d’eux. Vous avez donc deux solutions : trouver une base de données sur internet, ou alors la créer vous même. C’est cette dernière option que j’ai choisi en écrivant un programme Python qui crée ma base de données avec Sqlite. Je ne vous fournirai pas cette dernière, donc vous devrez chercher un peu par vous-mêmes. Tout ce que je peux vous fournir, c’est la structure de ma base de données. Comme vous le voyez ci-dessous, j’ai une table pour la liste des ports, puis une table pour chaque port.

CREATE TABLE IF NOT EXISTS liste_ports(
	primaire INTEGER PRIMARY KEY AUTOINCREMENT,
	numero_port TEXT,
	nom_port TEXT,
	nom_port_conca TEXT,
	date_enregistrement TEXT
	)

CREATE TABLE IF NOT EXISTS """ + str(var_nom_port_conca) + """ (
	primaire INTEGER PRIMARY KEY AUTOINCREMENT,
	date_ev TEXT,
	date_ev_conca TEXT,
	data TEXT,
	date_enregistrement TEXT
	)

On suppose donc pour la suite que vous avez une base de données des marées.

Envoi des SMS avec Gammu

Pour plus de simplicité, vous pouvez auparavant désactiver le code PIN de la carte SIM en la mettant dans un téléphone. Rendez-vous ensuite dans les paramètres > Sécurité > Verrouillage de la carte SIM.


Branchez votre clé 3G (avec la carte SIM à l’intérieur) sur la machine. On regarde les logs pour vérifier qu’elle est bien détectée.

dmesg

[  508.072219] usb 1-4: New USB device found, idVendor=xxxx, idProduct=xxxx
[  508.072227] usb 1-4: New USB device strings: Mfr=1, Product=2, SerialNumber=1
[  508.072232] usb 1-4: Product: HUAWEI Mobile
[  508.072238] usb 1-4: Manufacturer: ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
[  508.072242] usb 1-4: SerialNumber: ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
[  508.073794] usb-storage 1-4:1.0: USB Mass Storage device detected
[  508.078676] usb-storage 1-4:1.1: USB Mass Storage device detected
[  508.079543] usb-storage 1-4:1.2: USB Mass Storage device detected
[  508.080139] usb-storage 1-4:1.3: USB Mass Storage device detected
[  508.080606] scsi host2: usb-storage 1-4:1.3
[  508.113380] usbcore: registered new interface driver usbserial
[  508.113405] usbcore: registered new interface driver usbserial_generic
[  508.113425] usbserial: USB Serial support registered for generic
[  508.116998] usbcore: registered new interface driver option
[  508.117016] usbserial: USB Serial support registered for GSM modem (1-port)
[  508.117138] option 1-4:1.0: GSM modem (1-port) converter detected
[  508.117798] usb 1-4: GSM modem (1-port) converter now attached to ttyUSB0
[  508.117822] option 1-4:1.1: GSM modem (1-port) converter detected
[  508.117906] usb 1-4: GSM modem (1-port) converter now attached to ttyUSB1
[  508.117923] option 1-4:1.2: GSM modem (1-port) converter detected
[  508.119648] usb 1-4: GSM modem (1-port) converter now attached to ttyUSB2

Retenez bien le numéro de port utilisé par votre clé 3G. Vous pouvez observer les caractéristiques de vote clé avec gammu –identify.

# gammu --identify
Warning: No configuration file found!
Avertissement: Pas de lecture de configuration, utilisation des défauts innés !
Périphérique       : /dev/ttyUSB0
Fabricant            : Huawei
Modèle              : E169 (E169)
Firmware             : xxxxxxxxxxxxx
IMEI                 : xxxxxxxxxxxxx
SIM IMSI             : xxxxxxxxxxxxx

On voit que j’ai de la chance : la clé est déjà détectée sans rien configurer. Ce n’est peut-être pas le cas pour vous. On va donc faire un gammu-config.

Voici ce que vous devriez observer.

               ┌────────────────────────────────────────┐
               │ Current Gammu configuration            │ 
               │                                        │ 
               │  P Port                 (/dev/mobile)  │ 
               │  C Connection           (at19200)      │ 
               │  M Model                ()             │ 
               │  D Synchronize time     (yes)          │ 
               │  F Log file             ()             │ 
               │  O Log format           (nothing)      │ 
               │  L Use locking          ()             │ 
               │  G Gammu localisation   ()             │ 
               │  H Help                                │ 
               │  S Save                                │ 
               │                                        │ 
               │                                        │ 
               │        <Ok>            <Annuler>       │ 
               │                                        │ 
               └────────────────────────────────────────┘ 

Dans le port, mettez le port auquel a été attachée votre clé (dans le dmesg qu’on a fait plus haut). Personnellement, j’en avais 3, je peux donc mettre ttyUSB0, ttyUSB1 ou ttyUSB2. Pour le reste, mettez comme moi ci-dessous :

                 ┌────────────────────────────────────────────┐
                 │ Current Gammu configuration                │ 
                 │                                            │ 
                 │  P Port                 (/dev/ttyUSB0)     │ 
                 │  C Connection           (at19200)          │ 
                 │  M Model                ()                 │ 
                 │  D Synchronize time     (no)               │ 
                 │  F Log file             (/var/log/syslog)  │ 
                 │  O Log format           (textdate)         │ 
                 │  L Use locking          ()                 │ 
                 │  G Gammu localisation   ()                 │ 
                 │  H Help                                    │ 
                 │  S Save                                    │ 
                 │                                            │ 
                 │                                            │ 
                 │         <Ok>             <Annuler>         │ 
                 │                                            │ 
                 └────────────────────────────────────────────┘ 

Sauvegardez. Maintenant, si vous faites un gammu –identify, vous devriez voir votre clé 3G et ses caractéristiques.

# gammu --identify
Périphérique       : /dev/ttyUSB0
Fabricant            : Huawei
Modèle              : E169 (E169)
Firmware             : xxxxxxxxxxxxx
IMEI                 : xxxxxxxxxxxxx
SIM IMSI             : xxxxxxxxxxxxx

Si c’est le cas, vous pouvez tester l’envoi de SMS. Il y a plein de syntaxes disponibles pour la commande, et je vous laisse aller voir la documentation gammu si vous êtes intéressé. Personnellement j’utilise cette commande.

gammu sendsms TEXT 123456 -text "openprojects.fr"

# gammu sendsms TEXT 123456 -text "openprojects.fr"
Si vous désirez interrompre, appuyez sur Ctrl + C...
Envois du SMS 1 1 / 1 2.... Attente d'une réponse réseau..OK, message de référence=202 1

Recevoir des SMS avec gammu-smsd

Le but est de recevoir les SMS, et exécuter une action en fonction de celui-ci. Nous aurons besoin de python3 plus tard. Installez-le.

apt install python3

Gammu-smsd a besoin d’une base de données pour fonctionner. On installe donc MySQL. C’est dans celle-ci que seront stockés les messages en cours d’envoi, envoyés, reçus, etc.

apt update
apt upgrade
apt install mysql-server
mysql_secure_installation

Je vous laisse le soin de configurer tout ça. Si vous avez besoin d’aide vous pouvez vous rendre ici, c’est très bien expliqué.

Je vous recommande fortement de lire la documentation de Gammu qui est très bien faite, en particulier cette page. Il faut configurer votre base de données comme indiqué dans la documentation.

Connectez-vous à MySQL.

mysql -u root -p

Entrez les commandes suivantes pour créer l’utilisateur smsd (nécessaire au fonctionnement du daemon), lui donner les droits dont il a besoin, et créer la base de données.

GRANT USAGE ON *.* TO 'smsd'@'localhost' IDENTIFIED BY 'password';
GRANT SELECT, INSERT, UPDATE, DELETE ON `smsd`.* TO 'smsd'@'localhost';
CREATE DATABASE smsd;
exit

Evidemment, vous pouvez remplacer password par le mot de passe que vous souhaitez. Utilisez exit pour quitter. On va maintenant créer la structure de la base de données smsd. Créez un fichier mysql.sql dans /tmp.

cd /tmp
nano mysql.sql

Placez-y le code ci-dessous (provenant de la documentation).

-- 
-- Database for Gammu SMSD
-- 
-- In case you get errors about not supported charset, please
-- replace utf8mb4 with utf8.

-- --------------------------------------------------------

-- 
-- Table structure for table `gammu`
-- 

CREATE TABLE `gammu` (
  `Version` integer NOT NULL default '0' PRIMARY KEY
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;

-- 
-- Dumping data for table `gammu`
-- 

INSERT INTO `gammu` (`Version`) VALUES (17);

-- --------------------------------------------------------

-- 
-- Table structure for table `inbox`
-- 

CREATE TABLE `inbox` (
  `UpdatedInDB` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
  `ReceivingDateTime` timestamp NOT NULL default '0000-00-00 00:00:00',
  `Text` text NOT NULL,
  `SenderNumber` varchar(20) NOT NULL default '',
  `Coding` enum('Default_No_Compression','Unicode_No_Compression','8bit','Default_Compression','Unicode_Compression') NOT NULL default 'Default_No_Compression',
  `UDH` text NOT NULL,
  `SMSCNumber` varchar(20) NOT NULL default '',
  `Class` integer NOT NULL default '-1',
  `TextDecoded` text NOT NULL,
  `ID` integer unsigned NOT NULL auto_increment,
  `RecipientID` text NOT NULL,
  `Processed` enum('false','true') NOT NULL default 'false',
  `Status` integer NOT NULL default '-1',
  PRIMARY KEY `ID` (`ID`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 AUTO_INCREMENT=1 ;

-- 
-- Dumping data for table `inbox`
-- 


-- --------------------------------------------------------

-- 
-- Table structure for table `outbox`
-- 

CREATE TABLE `outbox` (
  `UpdatedInDB` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
  `InsertIntoDB` timestamp NOT NULL default '0000-00-00 00:00:00',
  `SendingDateTime` timestamp NOT NULL default '0000-00-00 00:00:00',
  `SendBefore` time NOT NULL DEFAULT '23:59:59',
  `SendAfter` time NOT NULL DEFAULT '00:00:00',
  `Text` text,
  `DestinationNumber` varchar(20) NOT NULL default '',
  `Coding` enum('Default_No_Compression','Unicode_No_Compression','8bit','Default_Compression','Unicode_Compression') NOT NULL default 'Default_No_Compression',
  `UDH` text,
  `Class` integer default '-1',
  `TextDecoded` text NOT NULL,
  `ID` integer unsigned NOT NULL auto_increment,
  `MultiPart` enum('false','true') default 'false',
  `RelativeValidity` integer default '-1',
  `SenderID` varchar(255),
  `SendingTimeOut` timestamp NULL default '0000-00-00 00:00:00',
  `DeliveryReport` enum('default','yes','no') default 'default',
  `CreatorID` text NOT NULL,
  `Retries` int(3) default 0,
  `Priority` integer default 0,
  `Status` enum('SendingOK','SendingOKNoReport','SendingError','DeliveryOK','DeliveryFailed','DeliveryPending','DeliveryUnknown','Error','Reserved') NOT NULL default 'Reserved',
  `StatusCode` integer NOT NULL default '-1',
  PRIMARY KEY `ID` (`ID`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;

CREATE INDEX outbox_date ON outbox(SendingDateTime, SendingTimeOut);
CREATE INDEX outbox_sender ON outbox(SenderID(250));

-- 
-- Dumping data for table `outbox`
-- 


-- --------------------------------------------------------

-- 
-- Table structure for table `outbox_multipart`
-- 

CREATE TABLE `outbox_multipart` (
  `Text` text,
  `Coding` enum('Default_No_Compression','Unicode_No_Compression','8bit','Default_Compression','Unicode_Compression') NOT NULL default 'Default_No_Compression',
  `UDH` text,
  `Class` integer default '-1',
  `TextDecoded` text,
  `ID` integer unsigned NOT NULL default '0',
  `SequencePosition` integer NOT NULL default '1',
  `Status` enum('SendingOK','SendingOKNoReport','SendingError','DeliveryOK','DeliveryFailed','DeliveryPending','DeliveryUnknown','Error','Reserved') NOT NULL default 'Reserved',
  `StatusCode` integer NOT NULL default '-1',
  PRIMARY KEY (`ID`, `SequencePosition`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;

-- 
-- Dumping data for table `outbox_multipart`
-- 

-- --------------------------------------------------------

-- 
-- Table structure for table `phones`
-- 

CREATE TABLE `phones` (
  `ID` text NOT NULL,
  `UpdatedInDB` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
  `InsertIntoDB` timestamp NOT NULL default '0000-00-00 00:00:00',
  `TimeOut` timestamp NOT NULL default '0000-00-00 00:00:00',
  `Send` enum('yes','no') NOT NULL default 'no',
  `Receive` enum('yes','no') NOT NULL default 'no',
  `IMEI` varchar(35) NOT NULL,
  `IMSI` varchar(35) NOT NULL,
  `NetCode` varchar(10) default 'ERROR',
  `NetName` varchar(35) default 'ERROR',
  `Client` text NOT NULL,
  `Battery` integer NOT NULL DEFAULT -1,
  `Signal` integer NOT NULL DEFAULT -1,
  `Sent` int NOT NULL DEFAULT 0,
  `Received` int NOT NULL DEFAULT 0,
  PRIMARY KEY (`IMEI`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;

-- 
-- Dumping data for table `phones`
-- 

-- --------------------------------------------------------

-- 
-- Table structure for table `sentitems`
-- 

CREATE TABLE `sentitems` (
  `UpdatedInDB` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
  `InsertIntoDB` timestamp NOT NULL default '0000-00-00 00:00:00',
  `SendingDateTime` timestamp NOT NULL default '0000-00-00 00:00:00',
  `DeliveryDateTime` timestamp NULL,
  `Text` text NOT NULL,
  `DestinationNumber` varchar(20) NOT NULL default '',
  `Coding` enum('Default_No_Compression','Unicode_No_Compression','8bit','Default_Compression','Unicode_Compression') NOT NULL default 'Default_No_Compression',
  `UDH` text NOT NULL,
  `SMSCNumber` varchar(20) NOT NULL default '',
  `Class` integer NOT NULL default '-1',
  `TextDecoded` text NOT NULL,
  `ID` integer unsigned NOT NULL default '0',
  `SenderID` varchar(255) NOT NULL,
  `SequencePosition` integer NOT NULL default '1',
  `Status` enum('SendingOK','SendingOKNoReport','SendingError','DeliveryOK','DeliveryFailed','DeliveryPending','DeliveryUnknown','Error') NOT NULL default 'SendingOK',
  `StatusError` integer NOT NULL default '-1',
  `TPMR` integer NOT NULL default '-1',
  `RelativeValidity` integer NOT NULL default '-1',
  `CreatorID` text NOT NULL,
  `StatusCode` integer NOT NULL default '-1',
  PRIMARY KEY (`ID`, `SequencePosition`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;

CREATE INDEX sentitems_date ON sentitems(DeliveryDateTime);
CREATE INDEX sentitems_tpmr ON sentitems(TPMR);
CREATE INDEX sentitems_dest ON sentitems(DestinationNumber);
CREATE INDEX sentitems_sender ON sentitems(SenderID(250));

-- 
-- Dumping data for table `sentitems`
-- 


-- 
-- Triggers for setting default timestamps
-- 

DELIMITER //

CREATE TRIGGER inbox_timestamp BEFORE INSERT ON inbox
FOR EACH ROW
BEGIN
    IF NEW.ReceivingDateTime = '0000-00-00 00:00:00' THEN
        SET NEW.ReceivingDateTime = CURRENT_TIMESTAMP();
    END IF;
END;//

CREATE TRIGGER outbox_timestamp BEFORE INSERT ON outbox
FOR EACH ROW
BEGIN
    IF NEW.InsertIntoDB = '0000-00-00 00:00:00' THEN
        SET NEW.InsertIntoDB = CURRENT_TIMESTAMP();
    END IF;
    IF NEW.SendingDateTime = '0000-00-00 00:00:00' THEN
        SET NEW.SendingDateTime = CURRENT_TIMESTAMP();
    END IF;
    IF NEW.SendingTimeOut = '0000-00-00 00:00:00' THEN
        SET NEW.SendingTimeOut = CURRENT_TIMESTAMP();
    END IF;
END;//

CREATE TRIGGER phones_timestamp BEFORE INSERT ON phones
FOR EACH ROW
BEGIN
    IF NEW.InsertIntoDB = '0000-00-00 00:00:00' THEN
        SET NEW.InsertIntoDB = CURRENT_TIMESTAMP();
    END IF;
    IF NEW.TimeOut = '0000-00-00 00:00:00' THEN
        SET NEW.TimeOut = CURRENT_TIMESTAMP();
    END IF;
END;//

CREATE TRIGGER sentitems_timestamp BEFORE INSERT ON sentitems
FOR EACH ROW
BEGIN
    IF NEW.InsertIntoDB = '0000-00-00 00:00:00' THEN
        SET NEW.InsertIntoDB = CURRENT_TIMESTAMP();
    END IF;
    IF NEW.SendingDateTime = '0000-00-00 00:00:00' THEN
        SET NEW.SendingDateTime = CURRENT_TIMESTAMP();
    END IF;
END;//

DELIMITER ;

Connectez-vous à MySQL et importez la configuration.

mysql -u root -p
use smsd;
source /tmp/mysql.sql

Comme précédemment, utilisez exit pour quitter MySQL.

On installe gammu-smsd. D’après ce que j’ai vu, le paquet gammu seul permet les appels, envoi et réception de SMS, etc. L’installation du paquet gammu-smsd permet 2 choses supplémentaires : l’utilisation du RunOnReceive, à savoir l’exécution d’un script lors de la réception d’un SMS, ainsi que d’éviter les pertes de SMS.

apt install gammu-smsd

Il se peut que vous ayez des erreurs à l’installation de gammu-smsd, comme quoi le service ne peut pas démarrer. Ce n’est pas grave. Redémarrez simplement votre machine.

reboot

Vérifiez l’état du service.

systemctl status gammu-smsd

# systemctl status gammu-smsd
● gammu-smsd.service - SMS daemon for Gammu
   Loaded: loaded (/lib/systemd/system/gammu-smsd.service; enabled; vendor prese
   Active: failed (Result: exit-code) since Wed 2018-08-22 19:53:51 CEST; 6min a
     Docs: man:gammu-smsd(1)
  Process: 820 ExecStopPost=/bin/rm -f /var/run/gammu-smsd.pid (code=exited, sta
  Process: 816 ExecStart=/usr/bin/gammu-smsd --pid=/var/run/gammu-smsd.pid --dae
 Main PID: 819 (code=exited, status=2)

août 22 19:53:51 promethee gammu-smsd[816]: Warning: No PIN code in /etc/gammu-s
août 22 19:53:51 promethee systemd[1]: gammu-smsd.service: PID file /var/run/gam
août 22 19:53:51 promethee systemd[1]: Started SMS daemon for Gammu.
août 22 19:53:51 promethee gammu-smsd[819]: Created POSIX RW shared memory at 0x
août 22 19:53:51 promethee gammu-smsd[819]: Starting phone communication...
août 22 19:53:51 promethee gammu-smsd[819]: Can't open device: Erreur à l'ouvert
août 22 19:53:51 promethee gammu-smsd[819]: Stopping Gammu smsd: Aucune erreur. 
août 22 19:53:51 promethee systemd[1]: gammu-smsd.service: Main process exited, 
août 22 19:53:51 promethee systemd[1]: gammu-smsd.service: Unit entered failed s
août 22 19:53:51 promethee systemd[1]: gammu-smsd.service: Failed with result 'e

On voit qu’il n’arrive pas à se connecter à la clé 3G. C’est normal car nous ne l’avons pas encore configurée. Pour cela éditez le fichier de config de gammu-smsd. N’oubliez pas de remplacer le mot de passe par celui que vous avez utilisé lors de la création de l’utilisateur smsd dans MySQL.

nano /etc/gammu-smsdrc

# Configuration file for Gammu SMS Daemon

# Gammu library configuration, see gammurc(5)
[gammu]
# Please configure this!
port = /dev/ttyUSB0
connection = at19200
# Debugging
#logformat = textall

# SMSD configuration, see gammu-smsdrc(5)
[smsd]
service = files
logfile = syslog
# Increase for debugging information
debuglevel = 0

# Paths where messages are stored
inboxpath = /var/spool/gammu/inbox/
outboxpath = /var/spool/gammu/outbox/
sentsmspath = /var/spool/gammu/sent/
errorsmspath = /var/spool/gammu/error/

RunOnReceive = python3 /path/vers/votre/fichier.py

service = SQL
driver = native_mysql
host = localhost
database = smsd
user = smsd
password = xxxxxxxxxxxxx (votre vot de passe )

Redémarrez le service et vérifiez son état.

systemctl restart gammu-smsd
systemctl status gammu-smsd

# systemctl status gammu-smsd
● gammu-smsd.service - SMS daemon for Gammu
   Loaded: loaded (/lib/systemd/system/gammu-smsd.service; enabled; vendor preset: enabled)
   Active: failed (Result: exit-code) since Wed 2018-08-22 20:08:58 CEST; 3s ago
     Docs: man:gammu-smsd(1)
  Process: 2021 ExecStopPost=/bin/rm -f /var/run/gammu-smsd.pid (code=exited, status=0/SUCCESS)
  Process: 2017 ExecStart=/usr/bin/gammu-smsd --pid=/var/run/gammu-smsd.pid --daemon (code=exited, status=0/SUCCESS)
 Main PID: 2018 (code=exited, status=2)

août 22 20:08:57 promethee systemd[1]: Starting SMS daemon for Gammu...
août 22 20:08:57 promethee systemd[1]: Started SMS daemon for Gammu.
août 22 20:08:57 promethee gammu-smsd[2018]: Connected to Database: smsd on localhost
août 22 20:08:57 promethee gammu-smsd[2018]: Database structure is from newer Gammu version
août 22 20:08:57 promethee systemd[1]: gammu-smsd.service: Main process exited, code=exited, status=2/INVALIDARGUMENT
août 22 20:08:58 promethee systemd[1]: gammu-smsd.service: Unit entered failed state.
août 22 20:08:58 promethee systemd[1]: gammu-smsd.service: Failed with result 'exit-code'.

Ob voit qu’on a une erreur de version : Database structure is from newer Gammu version. La solution est ici. On exécute donc les commandes suivantes pour supprimer la base de données.

mysql -u root -p
drop database smsd;

On recrée la base de données pour gammu-smsd.

nano /tmp/mysql.sql

On colle la config dans mysql.sql comme on l’avait fait précédemment, mais en changeant le numéro de version : on remplace 17 par 16. Ce qui nous donne au final pour le fichier mysql.sql :

-- 
-- Database for Gammu SMSD
-- 
-- In case you get errors about not supported charset, please
-- replace utf8mb4 with utf8.

-- --------------------------------------------------------

-- 
-- Table structure for table `gammu`
-- 

CREATE TABLE `gammu` (
  `Version` integer NOT NULL default '0' PRIMARY KEY
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;

-- 
-- Dumping data for table `gammu`
-- 

INSERT INTO `gammu` (`Version`) VALUES (16);

-- --------------------------------------------------------

-- 
-- Table structure for table `inbox`
-- 

CREATE TABLE `inbox` (
  `UpdatedInDB` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
  `ReceivingDateTime` timestamp NOT NULL default '0000-00-00 00:00:00',
  `Text` text NOT NULL,
  `SenderNumber` varchar(20) NOT NULL default '',
  `Coding` enum('Default_No_Compression','Unicode_No_Compression','8bit','Default_Compression','Unicode_Compression') NOT NULL default 'Default_No_Compression',
  `UDH` text NOT NULL,
  `SMSCNumber` varchar(20) NOT NULL default '',
  `Class` integer NOT NULL default '-1',
  `TextDecoded` text NOT NULL,
  `ID` integer unsigned NOT NULL auto_increment,
  `RecipientID` text NOT NULL,
  `Processed` enum('false','true') NOT NULL default 'false',
  `Status` integer NOT NULL default '-1',
  PRIMARY KEY `ID` (`ID`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 AUTO_INCREMENT=1 ;

-- 
-- Dumping data for table `inbox`
-- 


-- --------------------------------------------------------

-- 
-- Table structure for table `outbox`
-- 

CREATE TABLE `outbox` (
  `UpdatedInDB` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
  `InsertIntoDB` timestamp NOT NULL default '0000-00-00 00:00:00',
  `SendingDateTime` timestamp NOT NULL default '0000-00-00 00:00:00',
  `SendBefore` time NOT NULL DEFAULT '23:59:59',
  `SendAfter` time NOT NULL DEFAULT '00:00:00',
  `Text` text,
  `DestinationNumber` varchar(20) NOT NULL default '',
  `Coding` enum('Default_No_Compression','Unicode_No_Compression','8bit','Default_Compression','Unicode_Compression') NOT NULL default 'Default_No_Compression',
  `UDH` text,
  `Class` integer default '-1',
  `TextDecoded` text NOT NULL,
  `ID` integer unsigned NOT NULL auto_increment,
  `MultiPart` enum('false','true') default 'false',
  `RelativeValidity` integer default '-1',
  `SenderID` varchar(255),
  `SendingTimeOut` timestamp NULL default '0000-00-00 00:00:00',
  `DeliveryReport` enum('default','yes','no') default 'default',
  `CreatorID` text NOT NULL,
  `Retries` int(3) default 0,
  `Priority` integer default 0,
  `Status` enum('SendingOK','SendingOKNoReport','SendingError','DeliveryOK','DeliveryFailed','DeliveryPending','DeliveryUnknown','Error','Reserved') NOT NULL default 'Reserved',
  `StatusCode` integer NOT NULL default '-1',
  PRIMARY KEY `ID` (`ID`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;

CREATE INDEX outbox_date ON outbox(SendingDateTime, SendingTimeOut);
CREATE INDEX outbox_sender ON outbox(SenderID(250));

-- 
-- Dumping data for table `outbox`
-- 


-- --------------------------------------------------------

-- 
-- Table structure for table `outbox_multipart`
-- 

CREATE TABLE `outbox_multipart` (
  `Text` text,
  `Coding` enum('Default_No_Compression','Unicode_No_Compression','8bit','Default_Compression','Unicode_Compression') NOT NULL default 'Default_No_Compression',
  `UDH` text,
  `Class` integer default '-1',
  `TextDecoded` text,
  `ID` integer unsigned NOT NULL default '0',
  `SequencePosition` integer NOT NULL default '1',
  `Status` enum('SendingOK','SendingOKNoReport','SendingError','DeliveryOK','DeliveryFailed','DeliveryPending','DeliveryUnknown','Error','Reserved') NOT NULL default 'Reserved',
  `StatusCode` integer NOT NULL default '-1',
  PRIMARY KEY (`ID`, `SequencePosition`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;

-- 
-- Dumping data for table `outbox_multipart`
-- 

-- --------------------------------------------------------

-- 
-- Table structure for table `phones`
-- 

CREATE TABLE `phones` (
  `ID` text NOT NULL,
  `UpdatedInDB` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
  `InsertIntoDB` timestamp NOT NULL default '0000-00-00 00:00:00',
  `TimeOut` timestamp NOT NULL default '0000-00-00 00:00:00',
  `Send` enum('yes','no') NOT NULL default 'no',
  `Receive` enum('yes','no') NOT NULL default 'no',
  `IMEI` varchar(35) NOT NULL,
  `IMSI` varchar(35) NOT NULL,
  `NetCode` varchar(10) default 'ERROR',
  `NetName` varchar(35) default 'ERROR',
  `Client` text NOT NULL,
  `Battery` integer NOT NULL DEFAULT -1,
  `Signal` integer NOT NULL DEFAULT -1,
  `Sent` int NOT NULL DEFAULT 0,
  `Received` int NOT NULL DEFAULT 0,
  PRIMARY KEY (`IMEI`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;

-- 
-- Dumping data for table `phones`
-- 

-- --------------------------------------------------------

-- 
-- Table structure for table `sentitems`
-- 

CREATE TABLE `sentitems` (
  `UpdatedInDB` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
  `InsertIntoDB` timestamp NOT NULL default '0000-00-00 00:00:00',
  `SendingDateTime` timestamp NOT NULL default '0000-00-00 00:00:00',
  `DeliveryDateTime` timestamp NULL,
  `Text` text NOT NULL,
  `DestinationNumber` varchar(20) NOT NULL default '',
  `Coding` enum('Default_No_Compression','Unicode_No_Compression','8bit','Default_Compression','Unicode_Compression') NOT NULL default 'Default_No_Compression',
  `UDH` text NOT NULL,
  `SMSCNumber` varchar(20) NOT NULL default '',
  `Class` integer NOT NULL default '-1',
  `TextDecoded` text NOT NULL,
  `ID` integer unsigned NOT NULL default '0',
  `SenderID` varchar(255) NOT NULL,
  `SequencePosition` integer NOT NULL default '1',
  `Status` enum('SendingOK','SendingOKNoReport','SendingError','DeliveryOK','DeliveryFailed','DeliveryPending','DeliveryUnknown','Error') NOT NULL default 'SendingOK',
  `StatusError` integer NOT NULL default '-1',
  `TPMR` integer NOT NULL default '-1',
  `RelativeValidity` integer NOT NULL default '-1',
  `CreatorID` text NOT NULL,
  `StatusCode` integer NOT NULL default '-1',
  PRIMARY KEY (`ID`, `SequencePosition`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;

CREATE INDEX sentitems_date ON sentitems(DeliveryDateTime);
CREATE INDEX sentitems_tpmr ON sentitems(TPMR);
CREATE INDEX sentitems_dest ON sentitems(DestinationNumber);
CREATE INDEX sentitems_sender ON sentitems(SenderID(250));

-- 
-- Dumping data for table `sentitems`
-- 


-- 
-- Triggers for setting default timestamps
-- 

DELIMITER //

CREATE TRIGGER inbox_timestamp BEFORE INSERT ON inbox
FOR EACH ROW
BEGIN
    IF NEW.ReceivingDateTime = '0000-00-00 00:00:00' THEN
        SET NEW.ReceivingDateTime = CURRENT_TIMESTAMP();
    END IF;
END;//

CREATE TRIGGER outbox_timestamp BEFORE INSERT ON outbox
FOR EACH ROW
BEGIN
    IF NEW.InsertIntoDB = '0000-00-00 00:00:00' THEN
        SET NEW.InsertIntoDB = CURRENT_TIMESTAMP();
    END IF;
    IF NEW.SendingDateTime = '0000-00-00 00:00:00' THEN
        SET NEW.SendingDateTime = CURRENT_TIMESTAMP();
    END IF;
    IF NEW.SendingTimeOut = '0000-00-00 00:00:00' THEN
        SET NEW.SendingTimeOut = CURRENT_TIMESTAMP();
    END IF;
END;//

CREATE TRIGGER phones_timestamp BEFORE INSERT ON phones
FOR EACH ROW
BEGIN
    IF NEW.InsertIntoDB = '0000-00-00 00:00:00' THEN
        SET NEW.InsertIntoDB = CURRENT_TIMESTAMP();
    END IF;
    IF NEW.TimeOut = '0000-00-00 00:00:00' THEN
        SET NEW.TimeOut = CURRENT_TIMESTAMP();
    END IF;
END;//

CREATE TRIGGER sentitems_timestamp BEFORE INSERT ON sentitems
FOR EACH ROW
BEGIN
    IF NEW.InsertIntoDB = '0000-00-00 00:00:00' THEN
        SET NEW.InsertIntoDB = CURRENT_TIMESTAMP();
    END IF;
    IF NEW.SendingDateTime = '0000-00-00 00:00:00' THEN
        SET NEW.SendingDateTime = CURRENT_TIMESTAMP();
    END IF;
END;//

DELIMITER ;

On recrée la base de données et la structure comme précédemment.

mysql -u root -p
CREATE DATABASE smsd;
use smsd
source /tmp/mysql.sql

On redémarre le service et on affiche son état.

systemctl restart gammu-smsd
systemctl status gammu-smsd

# systemctl status gammu-smsd
● gammu-smsd.service - SMS daemon for Gammu
   Loaded: loaded (/lib/systemd/system/gammu-smsd.service; enabled; vendor preset: enabled)
   Active: active (running) since Wed 2018-08-22 20:21:26 CEST; 15s ago
     Docs: man:gammu-smsd(1)
  Process: 2180 ExecStopPost=/bin/rm -f /var/run/gammu-smsd.pid (code=exited, status=0/SUCCESS)
  Process: 2183 ExecStart=/usr/bin/gammu-smsd --pid=/var/run/gammu-smsd.pid --daemon (code=exited, status=0/SUCCESS)
 Main PID: 2184 (gammu-smsd)
    Tasks: 1 (limit: 4915)
   CGroup: /system.slice/gammu-smsd.service
           └─2184 /usr/bin/gammu-smsd --pid=/var/run/gammu-smsd.pid --daemon

août 22 20:21:26 promethee systemd[1]: Starting SMS daemon for Gammu...
août 22 20:21:26 promethee systemd[1]: Started SMS daemon for Gammu.
août 22 20:21:26 promethee gammu-smsd[2184]: Connected to Database: smsd on localhost
août 22 20:21:26 promethee gammu-smsd[2184]: Connected to Database native_mysql: smsd on localhost
août 22 20:21:26 promethee gammu-smsd[2184]: Created POSIX RW shared memory at 0x7fa4be055000
août 22 20:21:26 promethee gammu-smsd[2184]: Starting phone communication...
août 22 20:21:32 promethee gammu-smsd[2184]: Inserting phone info

Le service a démarré, donc on peut maintenant tester l’envoi et la réception de SMS. La commande pour envoyer les SMS n’est plus la même. Vous devez utiliser gammu-smsd-inject. Pour plus d’information consultez la documentation.

Comme précédemment il existe plusieurs syntaxes. J’en utilise personnellement une qui est assez particulière.

echo "openprojects.fr" | gammu-smsd-inject TEXT 123456 -len 10000 > /dev/null 2>&1

Maintenant on va passer à la partie importante de gammu-smsd : le RunOnReceive. Ce dernier permet d’exécuter un script à chaque fois qu’un SMS est reçu. Sur la documentation, seuls des scripts Shell sont utilisés. On va faire un peu plus compliqué en utilisant Python. Créez un fichier Python dans le répertoire de votre choix. J’utilise personnellement /home/user/Documents/scripts.

mkdir /home/user/Documents/scripts 
cd /home/user/Documents/scripts 
nano ror_sms.py

ror = run on RunOnReceive. Collez-y ce script que j’ai fait (je mâche tout le travail… ).

# openprojects.fr
# -*- coding: utf-8 -*-

import os

def send_message(dest_message, dest_number):
        os.system("echo \"%s\" | gammu-smsd-inject TEXT %s -len 10000 > /dev/null 2>&1" % (dest_message, dest_number))

try:
        sender_number = os.environ['SMS_1_NUMBER']
        sender_message = os.environ['SMS_1_TEXT']
        send_message (sender_message, sender_number)
except Exception as e:
        print("Impossible de récupérer les variables d'environnement gammu "  + str(e))

Modifiez le fichier de configuration de gammu-smsd.

nano /etc/gammu-smsdrc

Faites pointer le chemin du RunOnReceive vers votre script

RunOnReceive = python3 /home/user/Documents/scripts/ror_sms.py

Redémarrez le service.

systemctl restart gammu-smsd

Envoyez un SMS quelconque vers le numéro de la carte SIM que vous utilisez. Vous devriez recevoir comme réponse la même chose que ce que vous avez envoyé.

Maintenant que vous savez recevoir et envoyez des SMS, on peut passer à la partie programmation. Je ne partagerai pas mon script complet, mais seulement les parties importantes. A vous ensuite d’adapter.

Le script Python

Formatage des messages reçus

Afin de pouvoir faire des comparaisons de chaines de caractère, on supprime toutes les majuscules, accents du message reçu. Pour éviter les erreurs, on échappe tous les quotes et double quotes.

sender_message = unidecode.unidecode(sender_message.lower())
sender_message = sender_message.replace("'", r"\'")
sender_message = sender_message.replace("\"", r"\"")

Distinction des différents services disponibles

Il faut que notre script puisse différencier le service auquel l’utilisateur fait appel des autres. C’est à dire :

  • Traduction
  • Marée
  • Météo

L’utilisateur dont donc respecter quelques règles sur le format des SMS. Ceux-ci fonctionnent un peu comme des commandes dans un terminal : chaque paramètre est différencié des autres grâce au caractère espace. On va donc split le message reçu et récupérer chaque paramètre (mot) dans le tableau sender_message_parts.

sender_message_parts = sender_message.split(" ")

Afin que les utilisateurs puissent obtenir de l’aide sur le fonctionnement du système, si le message reçu est help, on envoie un message avec les instructions d’utilisation. Si le message contient au moins deux mots, on regarde si le premier correspond à un des services.

if sender_message == "help":
	send_message ("Instructions...", sender_number)
if len(sender_message_parts)>1:
	if sender_message_parts[0] == "meteo":
		# Code
	if sender_message_parts[0] == "maree":
		# Code
	if sender_message_parts[0] == "traduction":
		# Code

Connexion à la base de données de marée

Je vous conseille d’utiliser Sqlite, qui est pratique et facile d’utilisation.

cwd = (os.path.dirname(os.path.realpath(sys.argv[0])))
os.chdir(cwd)
conn = sqlite3.connect("maree.db")
cursor = conn.cursor()
# Requêtes SQL...
conn.close()

Le format des requêtes SQL est assez spécial si vous utilisez des variables Python dans celles-ci.

cursor.execute("""SELECT * FROM {} WHERE date_ev=?""".format(sms_nom_port_conca), (sms_date_port,))

Service de météo

Pour le SEO, j’ai tout simplement fait des requêtes sur Google, et c’est ce dernier qui s’est chargé du SEO. La librairie que j’ai utilisé est BeautifulSoup, elle permet de récupérer le code source HTML et parser celui-ci. Bien sûr, n’oubliez pas d’autoriser les redirections, sinon vous n’obtiendrez pas de résultat correct.

r = requests.get("https://www.google.com/search?q=météo+" + sms_meteo_ville, allow_redirects=True)
page_source = html.unescape(r.content.decode('latin-1').encode("utf-8").decode())
soup = BeautifulSoup(page_source, "lxml")
# A compléter...

Service de traduction

Vous devez trouver un site de traduction qui utilise la méthode PHP GET, afin que vous puissiez placer le mot à traduire dans une URL. Récupérez la liste des langes de traduction disponibles sur ce site et placez-les dans un dictionnaire. Si l’utilisateur envoie traduction liste-langues, il obtient la liste des langues disponibles. Il doit ensuite envoyer un SMS du type : traduction fr-en mot_à_traduire.

response = urllib2.urlopen(url)				
page_source = response.read()
soup = BeautifulSoup(page_source, "lxml")

Alerte automatique au propriétaire du système

Il est pratique de savoir ce qu’envoient les utilisateurs, vous pouvez donc mettre en place un système qui vous renvoie sur votre numéro personnel les SMS des utilisateurs.

permanent_dest = ["numéro", "numéro"]
def alert_permanent_dest():
	for dest in permanent_dest:
		os.system("echo \"%s a envoyé le SMS suivant : \n%s\" | gammu-smsd-inject TEXT %s -len 1000 > /dev/null 2>&1" % (sender_number, sender_message , dest))
alert_permanent_dest()

Blacklist

Vous pouvez créer un système de blacklist automatique si trop de SMS sont envoyés d’un certain numéro en un temps précis. Dès qu’un message est reçu, il est ajouté dans la table blacklist. Si trop de messages sont reçus d’un numéro, « Numéro blacklisté (oui / non) » passe à True. Pour cela créez une nouvelle table dans la base de données avec les colonnes suivantes :

  • Primaire
  • Numéro blacklisté (oui / non)
  • Numéro
  • Date du blacklist
  • Raison du blacklist
  • Variable nombre de message (qui s’incrémente à chaque SMS reçu et qui est réinitialisée automatiquement et régulièrement par un Cron).

N’oubliez pas d’analyser les numéros desquels vous recevez des SMS pour évitez de répondre aux numéros étrangers ou surtaxés.

Résultat final

Si vous avez tout réalisé correctement, vous obtiendrez le résultat suivant.



En cas de problèmes avec le RunOnReceive, faites un systemctl status gammu-smsd pour voir les erreurs.
Si vous avez des questions n’hésitez pas à me contacter 🙂

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Post comment