Domotique par mail

Récupérer le sujet

a marqué ce sujet comme résolu.

@nohar,

Ah je pensais que tu voulais oublier l'histoire des dictionnaires… Je pense que ça serait pas plus mal dans un sens, que de les éviter. Pourquoi pas mettre cela dans un fichier de configuration, plutôt que de se taper du texte en dur dans le code, non?

En dur dans le code ou dans une config, pour moi ça revient au même en termes de conception.

L'idée que je voulais mettre en avant, c'était d'utiliser proprement la bonne structure de données, peu importe la façon dont elle est remplie à l'initialisation :

  • 1 dictionnaire pour relier les mots-clés aux devices.
  • 1 dictionnaire pour relier les devices à leur état.

De cette façon, activer/désactiver un device à partir du mot-clé se fait en temps constant : on s'épargne une recherche linéaire, en la remplaçant par deux lookups en O(1) dans des tables de hashage. C'est pas forcément révolutionnaire sur le plan des performances dans cette appli, mais c'est conceptuellement beaucoup plus clean et souple.

+0 -0

L'idée que je voulais mettre en avant, c'était d'utiliser proprement la bonne structure de données, peu importe la façon dont elle est remplie à l'initialisation :

  • 1 dictionnaire pour relier les mots-clés aux devices.
  • 1 dictionnaire pour relier les devices à leur état.

nohar

C'est une bonne idée je trouve mais je ne comprend pas le code que tu as mis, et plus précisément l'encastration d'un dictionnaire dans un autre

+0 -0

Je n'ai rien encastré. C'est juste que les valeurs du dictionnaire SWITCHES sont les clés du dictionnaire DEVICES :

1
2
3
4
5
6
7
>>> mus = SWITCHES["MUS"]
>>> mus
'music' 
>>> DEVICES[mus]
True
>>> DEVICES[SWITCHES["MUS"]]
True
+0 -0

Ok, donc j'ai réussi à coder qelque chose de correct et qui marche avec les dicos :

domolib.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# -*- coding: utf-8 -*-
import RPi.GPIO as GPIO, imaplib, time, sys, random, email, pickle

RAD = 17
LAMPE = 27
MUSIQUE = 22

def init(devices):
    GPIO.setwarnings(False)
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(RAD, GPIO.OUT)
    GPIO.setup(LAMPE, GPIO.OUT)            ##GPIO initialisation
    GPIO.setup(MUSIQUE, GPIO.OUT)
    with open("/root/domotique_mail/files/save.txt", "rb") as fichier:
        depickler = pickle.Unpickler(fichier)
        devices = depickler.load()
        print devices
    fichier.close()
    return devices
    

def create_mail(username, password):
    m = imaplib.IMAP4_SSL('imap.gmail.com')
    m.login(username, password)
    return m

def is_new_mails(m):
    m.list()
    m.select('inbox')
    type, data = m.search(None, "UNSEEN")
    if type == "NO":
        print "Disable to connect"
        exit()
    else:
        count = len(data[0].split())
        if count > 0:
            return True
        else:
            return False

def get_data(m):
    type, data = m.fetch(1, '(RFC822)')
    for reponse_part in data:
        if isinstance(reponse_part, tuple):
            msg = email.message_from_string(reponse_part[1])
            subject = msg['subject']
            sender = msg['from']
    i = 0
    for char in sender:
        if char == '<':
            sender = sender[i+1: len(sender)-1]
        else:
            i+=1
    i = 0
    for char in subject:
         if char == ' ':
              subject = subject[0: i]
         else:
               i+=1
    subject = subject.lower()
    subsend = [subject, sender]
    return subsend

def data_traitement(true_sender, data_mail, switches, devices):
    if data_mail[1] == true_sender:
        devices[switches[data_mail[0]]] = not devices[switches[data_mail[0]]]
    else:
        print "Bad sender"

def print_data(devices, switches):
    GPIO.output(RAD, devices[switches["rad"]])
    GPIO.output(LAMPE, devices[switches["lampe"]])
    GPIO.output(MUSIQUE, devices[switches["musique"]])
    print devices
    with open("/root/domotique_mail/files/save.txt", "wb") as fichier: #On enregistre notre dico dans un fichier
        pickler = pickle.Pickler(fichier)
        pickler.dump(devices)
    fichier.close()

def delete_mails(m):
    m.store(1, '+FLAGS', r'\Deleted')

mail_domo.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# -*- coding: utf-8 -*-
from domolib import *

USERNAME = "raspdomopi@gmail.com"
PASSWORD = "monmdp"
TRUE_SENDER = "axelrousselot@outlook.fr"
DELAY = 1
DEVICES = {"heat": False, "light": False, "music": False}
DICO = {
        ("radiateur", "rad"): "heat",
        ("lampe", "lum", "lumière"): "light",
        ("musique", "mus"): "music"
        }
SWITCHES = {}

for key, val in DICO.items():
    for elt in key:
        SWITCHES[elt] = val

if __name__ == '__main__':
    DEVICES = init(DEVICES)
    print_data(DEVICES, SWITCHES)
    print DEVICES
    mail = create_mail(USERNAME, PASSWORD)
    while 1:
        if is_new_mails(mail):
            data_mail = get_data(mail)
            data_traitement(TRUE_SENDER, data_mail, SWITCHES, DEVICES)
            print_data(DEVICES, SWITCHES)
            delete_mails(mail)
        else:
            print "Any new mail"
        time.sleep(DELAY)

Mais l'idée du fichier de confique m'interresse, à quoi tu pensais ? Un fichier avec les mots clés pour le radiateur, un autre avec ceux de la lampe, et un autre avec ceux de la musique ?

+0 -0

Je sais que ce n’est qu’un compte de bidouille mais on ne met jamais un mot de passe en clair sur un espace publique.

Bon je l’ai modifié, tu pourras reprendre la main sur le compte via ta vrai adresse.

edit: et en plus c’est le même mot de passe sur ton main, tss… dépêche toi de changer tout ça.

re-edit: et le même mot de passe pour zds aussi, du coup je me suis loggé pour éditer ton message.

+0 -0

@Simbilou, tu pars d'une bonne intention, mais tu vas trop loin là ! C'est au Staff ou à l'auteur de régler ce genre de problème, et tu ne dois pas investiguer aussi loin. Et quand bien même tu le fais, tu ne dois surtout pas en dévoiler ici dans ton message les circonstances !

Imagine une minute : si quelqu'un a eu le temps de lire son message avant ta manipulation, il sait maintenant que le pass qu'il a vu est celui des 2 comptes mail et du compte ZdS, alors qu'avant il n'en savait rien et ne se doutait probablement de rien ou presque.

La prochaine fois, merci de te contenter de signaler au Staff et de MP l'auteur pour expliquer les conséquences (comptes mails, ZdS, etc.).


Et @Roumil, la prochaine fois fais attention à ne pas divulguer tes informations de connexion, surtout si tu partages un seul et même mot de passe pour des services différents.

+0 -0

Tu retombes dans ce que nohar reprochait à ton code. Il ne faut pas que les clés dans tes dicos soit des conteneurs :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
KEYS = {
    "music" : "music",
    "musique" : "music",
    "mus" : "music",
    #etc...
}
VALUES = {
    "music" : True,
    "light" : True,
    "heat" : True
}

# ...
value = VALUES[KEYS[key]]

Le but étant d'accéder en temps constant aux valeurs que tu cherches. Si tes clés sont des conteneurs tu es obligé de parcourir le dictionnaire et les clés. Alors que tout l'intérêt d'un dico ici, est d'avoir plusieurs clés pour un même objet. La chaîne 'music' est un seul et même objet, même si elle est déclarée à plusieurs endroit.

J'ai édité mon code, et j'ai enfin compris le fonctionnement de l'accès en temps constant :)

Et pour ce qui est du mot de passe, tout est de ma faute, mais oui, il aurait été préférable de me MP, plutôt que de le dire en publique et de changer mon mot de passe (même si ça partait d'une bonne intention)

Et pour le fichier de config ?

+0 -0

Bah on peut imaginer que tu veuilles configurer ton application sans changer son code source. Par exemple avec un fichier Yaml pour définir les mots-clé :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
keywords:
    music:
        - mus
        - music
        - musique
    light:
        - lumière
        - lumiere
        - lum
        - light
        - lampe
    heat:
        - chauffage
        - chauff

Ce fichier de configuration peut être facilement transformé en un dictionnaire en Python avec la bibliothèque PyYAML.

1
2
3
4
5
>>> import yaml
>>> conf_file = open("config.yml", "r")
>>> config = yaml.load(conf_file)
>>> config
{'keywords': {'heat': ['chauffage', 'chauff'], 'music': ['mus', 'music', 'musique'], 'light': ['lumière', 'lumiere', 'lum', 'light', 'lampe']}}
+0 -0

Pour l'exemple de nohar avec et sans accent, il faudrait peut-être directement traiter la chaine pour enlever les accents, avant de faire la comparaison.

Pour ce qui est de ton étourderie, tu devrais prendre le soin de toujours mettre tes identifiants dans un autres fichiers réservé à cette usage (.py ou fichier config .yml).

Et si jamais tu publies encore des données "secrètes/sensible", change les. Pour éviter le pire.

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte