2020-09-27 16:52:56 +02:00
|
|
|
|
# WARNING: ce code s'appuie sur une API découverte par rétro-ingénérie.
|
|
|
|
|
# Il peut cesser de fonctionner sans préavis.
|
2016-05-19 15:15:55 +02:00
|
|
|
|
|
2016-05-21 16:59:51 +02:00
|
|
|
|
# TODO:
|
2020-09-27 16:52:56 +02:00
|
|
|
|
##
|
2016-05-21 16:59:51 +02:00
|
|
|
|
|
2018-02-22 21:29:53 +01:00
|
|
|
|
from urllib import request
|
|
|
|
|
from http.cookiejar import CookieJar
|
|
|
|
|
from urllib.parse import urlencode
|
|
|
|
|
from urllib.error import HTTPError
|
2020-10-20 17:02:34 +02:00
|
|
|
|
from time import time
|
2018-02-22 21:29:53 +01:00
|
|
|
|
|
2016-05-19 15:15:55 +02:00
|
|
|
|
authors = (
|
|
|
|
|
'Gilles "Almtesh" Émilien MOREL',
|
2018-02-22 21:29:53 +01:00
|
|
|
|
)
|
2020-09-27 16:52:56 +02:00
|
|
|
|
name = 'homesfr pour Python 3'
|
2020-10-20 17:02:34 +02:00
|
|
|
|
version = '1.4'
|
2016-05-19 15:15:55 +02:00
|
|
|
|
|
2020-09-27 16:52:56 +02:00
|
|
|
|
# Modes utilisables
|
2016-05-19 15:15:55 +02:00
|
|
|
|
MODE_OFF = 0
|
|
|
|
|
MODE_CUSTOM = 1
|
|
|
|
|
MODE_ON = 2
|
|
|
|
|
|
2020-09-27 16:52:56 +02:00
|
|
|
|
# Types de capteurs
|
|
|
|
|
PRESENCE_DETECTOR = 'PIR_DETECTOR' # https://boutique.home.sfr.fr/detecteur-de-mouvement
|
|
|
|
|
MAGNETIC_OPENNING_DETECTOR = 'MAGNETIC' # https://boutique.home.sfr.fr/detecteur-d-ouverture-de-porte-ou-fenetre
|
|
|
|
|
SMOKE_DETECTOR = 'SMOKE' # https://boutique.home.sfr.fr/detecteur-de-fumee
|
|
|
|
|
SIREN = 'SIREN' # https://boutique.home.sfr.fr/sirene-interieure (et peut-être https://boutique.home.sfr.fr/sirene-exterieure)
|
|
|
|
|
REMOTE_CONTROLER = 'REMOTE' # https://boutique.home.sfr.fr/telecommande
|
|
|
|
|
KEYPAD_CONTROLER = 'KEYPAD' # https://boutique.home.sfr.fr/clavier-de-commande
|
|
|
|
|
PRESENCE_CAMERA_DETECTOR = 'PIR_CAMERA' # https://boutique.home.sfr.fr/camera
|
2020-10-20 10:28:22 +02:00
|
|
|
|
TEMPHUM_SENSOR = 'TEMP_HUM' # https://boutique.home.sfr.fr/thermometre
|
|
|
|
|
ONOFF_PLUG = 'ON_OFF_PLUG' # https://boutique.home.sfr.fr/prise-commandee-connectee-legrand
|
2020-09-27 16:52:56 +02:00
|
|
|
|
|
|
|
|
|
base_url = 'https://home.sfr.fr'
|
|
|
|
|
|
|
|
|
|
# Authentification
|
|
|
|
|
auth_path = '/mysensors'
|
|
|
|
|
auth_ok_url = 'https://home.sfr.fr/logged'
|
|
|
|
|
auth_post_url = 'https://boutique.home.sfr.fr/authentification'
|
|
|
|
|
auth_referer = 'https://boutique.home.sfr.fr/authentification?back=service'
|
|
|
|
|
auth_user_field = 'email'
|
|
|
|
|
auth_pass_field = 'passwd'
|
|
|
|
|
auth_extra_fields = {'back': 'service', 'token_sso': '', 'error_sso': '', 'SubmitLogin': 'OK'}
|
|
|
|
|
auth_logout_path = '/deconnexion'
|
|
|
|
|
|
|
|
|
|
# Chemin pour la liste des capteurs
|
|
|
|
|
sensors_list = '/mysensors'
|
|
|
|
|
|
|
|
|
|
# Chemin pour les alertes
|
|
|
|
|
alerts_path = '/listalert'
|
|
|
|
|
|
|
|
|
|
# Détection
|
|
|
|
|
mode_get_label = 'alarm_mode'
|
|
|
|
|
mode_set_path = '/alarmmode'
|
|
|
|
|
mode_set_field = 'action' # Name for GET field
|
|
|
|
|
mode_off = 'OFF' # Value for off
|
|
|
|
|
mode_custom = 'CUSTOM' # Value for custom
|
|
|
|
|
mode_on = 'ON' # Value for on
|
|
|
|
|
|
2020-10-20 10:28:22 +02:00
|
|
|
|
# Caméra
|
2020-09-27 16:52:56 +02:00
|
|
|
|
cameras_list = '/homescope/mycams'
|
|
|
|
|
camera_snapshot = '/homescope/snapshot?size=4'
|
|
|
|
|
camera_snapshot_mac = 'mac'
|
|
|
|
|
camera_video = '/homescope/flv'
|
|
|
|
|
camera_video_mac = 'mac'
|
|
|
|
|
camera_recordings_list = '/listenr'
|
|
|
|
|
camera_recordings_delete = '/delenr'
|
|
|
|
|
camera_recordings_start = '/homescope/record'
|
|
|
|
|
camera_recordings_stop = '/homescope/stoprecord'
|
|
|
|
|
camera_recordings_mac = 'mac'
|
|
|
|
|
camera_get_config_path = '/homescope/camsettings'
|
|
|
|
|
camera_get_config_mac = 'mac'
|
|
|
|
|
camera_get_config_flip = 'FLIP'
|
|
|
|
|
camera_get_config_leds = 'LEDMODE'
|
|
|
|
|
camera_get_config_petmode = 'pet_mode'
|
|
|
|
|
camera_get_config_recording = 'rec24'
|
|
|
|
|
camera_get_config_privacy = 'privacy'
|
|
|
|
|
camera_get_config_name = 'NAME'
|
|
|
|
|
camera_set_config_path = '/homescope/camsettings'
|
|
|
|
|
camera_set_config_mac = 'mac'
|
|
|
|
|
camera_set_config_flip = 'flip'
|
|
|
|
|
camera_set_config_leds = 'led_mode'
|
|
|
|
|
camera_set_config_petmode = 'pet_mode'
|
|
|
|
|
camera_set_config_recording = 'rec24'
|
|
|
|
|
# Le paramètre privacy se gère avec le bouton derrière la caméra.
|
|
|
|
|
camera_set_config_name = 'name'
|
2016-05-22 22:57:39 +02:00
|
|
|
|
|
2020-09-27 16:52:56 +02:00
|
|
|
|
# Capteurs
|
|
|
|
|
sensors_list = '/mysensors'
|
|
|
|
|
sensors_label = 'Sensor'
|
|
|
|
|
sensors_label_id = 'id'
|
|
|
|
|
sensors_mac_field = 'deviceMac'
|
|
|
|
|
sensors_type_field = 'deviceType'
|
|
|
|
|
sensors_model_field = 'deviceModel'
|
|
|
|
|
sensors_version_field = 'deviceVersion'
|
|
|
|
|
sensors_name_field = 'name'
|
|
|
|
|
sensors_longname_field = 'long_name'
|
|
|
|
|
sensors_namegender_field = 'name_gender'
|
|
|
|
|
sensors_batterylevel_field = 'batteryLevel'
|
|
|
|
|
sensors_signal_field = 'signalLevel'
|
|
|
|
|
sensors_status_field = 'status'
|
|
|
|
|
sensors_status_value_ok = 'OK'
|
2019-01-15 23:02:35 +01:00
|
|
|
|
|
2020-09-27 17:41:15 +02:00
|
|
|
|
# Capteurs de température et humidité
|
|
|
|
|
sensors_temphum_root_field = 'sensorValues'
|
|
|
|
|
sensors_temp_name = 'name'
|
|
|
|
|
sensors_temp_text = 'Temperature'
|
|
|
|
|
sensors_hum_name = 'name'
|
|
|
|
|
sensors_hum_text = 'Humidity'
|
|
|
|
|
|
2020-10-20 10:28:22 +02:00
|
|
|
|
# Prise connectée (ON_OFF_PLUG)
|
|
|
|
|
sensors_oop_stateroot = 'automation'
|
|
|
|
|
sensors_oop_state = 'on_off'
|
|
|
|
|
sensors_oop_power = 'power_level'
|
2020-10-21 09:20:17 +02:00
|
|
|
|
sensors_oop_control = '/plugcontrol'
|
|
|
|
|
sensors_oop_control_sensors_id_field = 'uid'
|
|
|
|
|
sensors_oop_control_action_field = 'action'
|
|
|
|
|
sensors_oop_control_action_value_on = 'on'
|
|
|
|
|
sensors_oop_control_action_value_off = 'off'
|
2020-10-20 10:28:22 +02:00
|
|
|
|
|
2020-09-27 16:52:56 +02:00
|
|
|
|
# Fil d'événements
|
|
|
|
|
logs_path = '/getlog?page=1&nbparpage=10000' # je pense qu'on récupère tous les événements avec cette valeur
|
|
|
|
|
logs_labels = 'LOG'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def bytes2file (b):
|
2016-05-21 16:59:51 +02:00
|
|
|
|
'''
|
2020-09-27 16:52:56 +02:00
|
|
|
|
Retourne une classe semblable à un fichier contant b
|
2016-05-21 16:59:51 +02:00
|
|
|
|
'''
|
2020-09-27 16:52:56 +02:00
|
|
|
|
from io import BytesIO
|
|
|
|
|
r = BytesIO ()
|
|
|
|
|
r.write (b)
|
|
|
|
|
r.seek (0)
|
|
|
|
|
return (r)
|
2016-05-25 17:19:16 +02:00
|
|
|
|
|
|
|
|
|
|
2020-09-27 16:52:56 +02:00
|
|
|
|
def bytes2image (b):
|
|
|
|
|
'''
|
|
|
|
|
Retourne une Image PIL contenant l'image donnée en b
|
|
|
|
|
'''
|
|
|
|
|
from PIL import Image
|
|
|
|
|
f = bytes2file (b)
|
|
|
|
|
r = Image.open (f)
|
|
|
|
|
return (r)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_xml_tree (fp):
|
|
|
|
|
'''
|
|
|
|
|
Retourne une variable itérable contenant les données d'un arbre XML
|
|
|
|
|
'''
|
|
|
|
|
def build_tree (element):
|
|
|
|
|
if tuple (element) == ():
|
|
|
|
|
return (element.tag, dict (element.items ()), element.text)
|
2016-05-26 16:57:48 +02:00
|
|
|
|
else:
|
2020-09-27 16:52:56 +02:00
|
|
|
|
sub = []
|
|
|
|
|
for i in element:
|
|
|
|
|
sub.append (build_tree (i))
|
|
|
|
|
return (element.tag, dict (element.items ()), sub)
|
|
|
|
|
from xml.etree import ElementTree as ET
|
|
|
|
|
fp.seek (0)
|
|
|
|
|
root = ET.parse (fp).getroot ()
|
|
|
|
|
return (build_tree (root))
|
2016-05-25 17:19:16 +02:00
|
|
|
|
|
2019-01-15 23:02:35 +01:00
|
|
|
|
|
2020-09-27 16:52:56 +02:00
|
|
|
|
class HomeSFR ():
|
2020-10-20 17:02:34 +02:00
|
|
|
|
class MySensors ():
|
|
|
|
|
'''
|
|
|
|
|
Sous-classe gérant la récupération et le stockage temporaire des données issue du fichier https://home.sfr.fr/mysensors
|
|
|
|
|
'''
|
|
|
|
|
def __init__ (self, __parent__, max_age = 60):
|
|
|
|
|
self.__parent__ = __parent__
|
|
|
|
|
self.max_age = max_age
|
|
|
|
|
self.data = None
|
|
|
|
|
self.date = None
|
|
|
|
|
|
|
|
|
|
def __add__ (self, value):
|
|
|
|
|
self.autorefresh ()
|
|
|
|
|
return (self.data + value)
|
|
|
|
|
|
|
|
|
|
def __contains__ (self, key):
|
|
|
|
|
self.autorefresh ()
|
|
|
|
|
return (key in self.data)
|
|
|
|
|
|
|
|
|
|
def __eq__ (self, value):
|
|
|
|
|
self.autorefresh ()
|
|
|
|
|
return (self.data == value)
|
|
|
|
|
|
|
|
|
|
def __ge__ (self, value):
|
|
|
|
|
self.autorefresh ()
|
|
|
|
|
return (self.data >= value)
|
|
|
|
|
|
|
|
|
|
def __getitem__ (self, key):
|
|
|
|
|
self.autorefresh ()
|
|
|
|
|
return (self.data [key])
|
|
|
|
|
|
|
|
|
|
def __gt__ (self, value):
|
|
|
|
|
self.autorefresh ()
|
|
|
|
|
return (self.data > value)
|
|
|
|
|
|
|
|
|
|
def __hash__ (self):
|
|
|
|
|
self.autorefresh ()
|
|
|
|
|
return (hash (self.data))
|
|
|
|
|
|
|
|
|
|
def __iter__ (self):
|
|
|
|
|
self.autorefresh ()
|
|
|
|
|
return (iter (self.data))
|
|
|
|
|
|
|
|
|
|
def __le__ (self, value):
|
|
|
|
|
self.autorefresh ()
|
|
|
|
|
return (self.data <= value)
|
|
|
|
|
|
|
|
|
|
def __len__ (self):
|
|
|
|
|
self.autorefresh ()
|
|
|
|
|
return (len (self.data))
|
|
|
|
|
|
|
|
|
|
def __lt__ (self, value):
|
|
|
|
|
self.autorefresh ()
|
|
|
|
|
return (self.data < value)
|
|
|
|
|
|
|
|
|
|
def __mul__ (self, value):
|
|
|
|
|
self.autorefresh ()
|
|
|
|
|
return (self.data * value)
|
|
|
|
|
|
|
|
|
|
def __ne__ (self, value):
|
|
|
|
|
self.autorefresh ()
|
|
|
|
|
return (self.data != value)
|
|
|
|
|
|
|
|
|
|
def __repr__ (self):
|
|
|
|
|
self.autorefresh ()
|
|
|
|
|
return (repr (self.data))
|
|
|
|
|
|
|
|
|
|
def __rmul__ (self, value):
|
|
|
|
|
self.autorefresh ()
|
|
|
|
|
return (value * self.data)
|
|
|
|
|
|
|
|
|
|
def count (self, value):
|
|
|
|
|
self.autorefresh ()
|
|
|
|
|
return (self.data.count (value))
|
|
|
|
|
|
|
|
|
|
def get_age (self):
|
|
|
|
|
if self.date is None:
|
|
|
|
|
return (None)
|
|
|
|
|
return (time () - self.date)
|
|
|
|
|
|
|
|
|
|
def refresh (self):
|
|
|
|
|
r = base_url + sensors_list
|
|
|
|
|
self.data = get_xml_tree (bytes2file (self.__parent__.get_or_autologin (r).read ()))
|
|
|
|
|
self.date = time ()
|
|
|
|
|
|
|
|
|
|
def autorefresh (self):
|
|
|
|
|
if self.date is None or self.max_age != 0 and self.get_age () > self.max_age:
|
|
|
|
|
self.refresh ()
|
|
|
|
|
|
|
|
|
|
def get_data (self):
|
|
|
|
|
self.autorefresh ()
|
|
|
|
|
return (self.data)
|
|
|
|
|
|
2016-05-25 17:19:16 +02:00
|
|
|
|
def __init__ (self, username = None, password = None, cookies = None, debug = False, autologin = True):
|
|
|
|
|
'''
|
2020-09-27 16:52:56 +02:00
|
|
|
|
Instancie la classe avec un identifiant et un mot de passe, ou des cookies
|
|
|
|
|
On peut définir les identifiants et les cookies, la classe utilisera les cookies par défaut et les identifiants si on n'est pas connecté
|
|
|
|
|
debug fourni des informations supplémentaires sur la sortie standard, s'il est à False, cette classe n'envoie rien sur la sortie standard
|
2016-05-25 17:19:16 +02:00
|
|
|
|
'''
|
|
|
|
|
self.DEBUG = debug
|
|
|
|
|
if self.DEBUG:
|
|
|
|
|
print (name + ' ' + version)
|
2020-09-27 16:52:56 +02:00
|
|
|
|
print ('Auteurs:')
|
2016-05-25 17:19:16 +02:00
|
|
|
|
for i in authors:
|
|
|
|
|
print (' - ' + i)
|
2018-02-22 21:29:53 +01:00
|
|
|
|
if username is not None:
|
2020-09-27 16:52:56 +02:00
|
|
|
|
print ('initalisé avec l\'identifiant ' + username)
|
2018-02-22 21:29:53 +01:00
|
|
|
|
if cookies is not None:
|
2020-09-27 16:52:56 +02:00
|
|
|
|
print ('initialisé avec des cookies')
|
2016-05-25 17:19:16 +02:00
|
|
|
|
print ('debug = ' + str (debug))
|
|
|
|
|
print ('autologin = ' + str (autologin))
|
|
|
|
|
|
2018-02-22 21:29:53 +01:00
|
|
|
|
if (username is None or password is None) and cookies is None:
|
2020-09-27 16:52:56 +02:00
|
|
|
|
raise TypeError ('Vous devez définir des identifiant ou des cookies !')
|
2016-05-25 17:19:16 +02:00
|
|
|
|
self.username = username
|
|
|
|
|
self.password = password
|
2018-02-22 21:29:53 +01:00
|
|
|
|
if self.username is not None and self.password is not None:
|
2016-05-25 17:19:16 +02:00
|
|
|
|
self.autologin = autologin
|
|
|
|
|
else:
|
|
|
|
|
self.autologin = False
|
2018-02-22 21:29:53 +01:00
|
|
|
|
if cookies is None:
|
2016-05-25 17:19:16 +02:00
|
|
|
|
self.cookies = CookieJar ()
|
2016-12-01 11:27:01 +01:00
|
|
|
|
elif type (cookies) == CookieJar:
|
2016-05-25 17:19:16 +02:00
|
|
|
|
self.cookies = cookies
|
2016-12-01 11:27:01 +01:00
|
|
|
|
else:
|
2020-09-27 16:52:56 +02:00
|
|
|
|
raise TypeError ('Les cookies doivent être de type CookieJar !')
|
2016-05-25 17:19:16 +02:00
|
|
|
|
self.opener = request.build_opener (request.HTTPCookieProcessor (self.cookies))
|
2020-10-20 17:02:34 +02:00
|
|
|
|
self.mysensors = self.MySensors (self)
|
2016-05-19 15:15:55 +02:00
|
|
|
|
|
|
|
|
|
def __str__ (self):
|
|
|
|
|
'''
|
2020-09-27 16:52:56 +02:00
|
|
|
|
Returne des informations sur l'état de l'instance
|
2016-05-19 15:15:55 +02:00
|
|
|
|
'''
|
2018-02-22 21:29:53 +01:00
|
|
|
|
if self.username is not None:
|
2020-09-27 16:52:56 +02:00
|
|
|
|
return (name + ' ' + version + '\nUtilisateur : ' + self.username + '\nDebug : ' + str (self.DEBUG))
|
2016-05-23 09:23:26 +02:00
|
|
|
|
else:
|
2020-09-27 16:52:56 +02:00
|
|
|
|
return (name + ' ' + version + '\nUtilisateur : Inconnu, authentifié avec des cookies.\nDebug : ' + str (self.DEBUG))
|
|
|
|
|
|
2016-05-19 15:15:55 +02:00
|
|
|
|
def test_login (self):
|
|
|
|
|
'''
|
2020-09-27 16:52:56 +02:00
|
|
|
|
Retourne l'état de l'authentification
|
2016-05-19 15:15:55 +02:00
|
|
|
|
'''
|
|
|
|
|
try:
|
|
|
|
|
if self.DEBUG:
|
2020-09-27 16:52:56 +02:00
|
|
|
|
print ('Test de l\'authentification')
|
|
|
|
|
self.opener.open (base_url + auth_path)
|
2016-05-19 15:15:55 +02:00
|
|
|
|
except HTTPError:
|
|
|
|
|
if self.DEBUG:
|
2020-09-27 16:52:56 +02:00
|
|
|
|
print ('Non connecté')
|
2016-05-19 15:15:55 +02:00
|
|
|
|
return (False)
|
|
|
|
|
if self.DEBUG:
|
2020-09-27 16:52:56 +02:00
|
|
|
|
print ('Connecté')
|
2016-05-19 15:15:55 +02:00
|
|
|
|
return (True)
|
|
|
|
|
|
|
|
|
|
def login (self):
|
|
|
|
|
'''
|
2020-09-27 16:52:56 +02:00
|
|
|
|
S'authentifier auprès du service
|
|
|
|
|
Retourne True si c'est réussi, False sinon
|
|
|
|
|
Si seuls les cookies sont définis, la méthode retournera False, même si nous sommes authentifiés, pour savoir si on est authentifiés, utiliser test_login ()
|
2016-05-19 15:15:55 +02:00
|
|
|
|
'''
|
2018-02-22 21:29:53 +01:00
|
|
|
|
if self.username is not None and self.password is not None:
|
2020-09-27 16:52:56 +02:00
|
|
|
|
self.opener.open (auth_referer)
|
|
|
|
|
data = auth_extra_fields
|
|
|
|
|
data [auth_user_field] = self.username
|
|
|
|
|
data [auth_pass_field] = self.password
|
2018-01-27 16:22:13 +01:00
|
|
|
|
data = bytes (urlencode (data), 'UTF8')
|
2016-05-23 09:23:26 +02:00
|
|
|
|
if self.DEBUG:
|
2018-02-22 21:29:53 +01:00
|
|
|
|
print ('Cookies ' + str (len (self.cookies)))
|
2020-09-27 16:52:56 +02:00
|
|
|
|
print ('Envoi de ' + str (data))
|
|
|
|
|
a = self.opener.open (auth_post_url, data = data)
|
2016-05-23 09:23:26 +02:00
|
|
|
|
if self.DEBUG:
|
2020-09-27 16:52:56 +02:00
|
|
|
|
print ('Authentification redirigée vers ' + a.geturl ())
|
|
|
|
|
return (a.geturl () == auth_ok_url)
|
2016-05-23 09:23:26 +02:00
|
|
|
|
else:
|
|
|
|
|
return (False)
|
2016-05-19 15:15:55 +02:00
|
|
|
|
|
2020-09-27 16:52:56 +02:00
|
|
|
|
def get_or_autologin (self, url, data = None):
|
2016-05-26 16:57:48 +02:00
|
|
|
|
'''
|
2020-09-27 16:52:56 +02:00
|
|
|
|
Fais une requête, si une erreur 403 est retourné, tente une authentification automatiquement (si réglé dans le paramètre autologin), sinon lève une exception
|
2016-05-26 16:57:48 +02:00
|
|
|
|
'''
|
2020-09-27 16:52:56 +02:00
|
|
|
|
try:
|
2020-10-20 17:02:34 +02:00
|
|
|
|
if self.DEBUG:
|
|
|
|
|
print ('Getting ' + url)
|
2020-09-27 16:52:56 +02:00
|
|
|
|
return (self.opener.open (url, data = data))
|
|
|
|
|
except HTTPError as e:
|
|
|
|
|
if '403' in str (e) and self.autologin:
|
2020-10-20 17:02:34 +02:00
|
|
|
|
if self.DEBUG:
|
|
|
|
|
print ('Not logged, logging…')
|
2020-09-27 16:52:56 +02:00
|
|
|
|
self.login ()
|
2020-10-20 17:02:34 +02:00
|
|
|
|
if self.DEBUG:
|
|
|
|
|
print ('Getting ' + url)
|
2020-09-27 16:52:56 +02:00
|
|
|
|
return (self.opener.open (url, data = data))
|
|
|
|
|
else:
|
|
|
|
|
raise e
|
2016-05-26 16:57:48 +02:00
|
|
|
|
|
2016-05-23 15:11:43 +02:00
|
|
|
|
def logout (self):
|
|
|
|
|
'''
|
2020-09-27 16:52:56 +02:00
|
|
|
|
Se déconnecter
|
|
|
|
|
L'instance devrait être supprimée après l'appel de cette fonction
|
2016-05-23 15:11:43 +02:00
|
|
|
|
'''
|
|
|
|
|
if self.DEBUG:
|
2020-09-27 16:52:56 +02:00
|
|
|
|
print ('Demande de déconnexion')
|
|
|
|
|
self.opener.open (base_url + auth_logout_path)
|
2016-05-23 15:11:43 +02:00
|
|
|
|
if self.DEBUG:
|
2020-09-27 16:52:56 +02:00
|
|
|
|
print ('Destruction des cookies')
|
2016-05-23 15:11:43 +02:00
|
|
|
|
del self.cookies
|
|
|
|
|
self.cookies = None
|
|
|
|
|
return (None)
|
|
|
|
|
|
|
|
|
|
def get_cookies (self):
|
|
|
|
|
'''
|
2020-09-27 16:52:56 +02:00
|
|
|
|
Récupérer les cookies
|
|
|
|
|
Il est recommandé de supprimer l'instance après cette fonction
|
2016-05-23 15:11:43 +02:00
|
|
|
|
'''
|
|
|
|
|
return (self.cookies)
|
|
|
|
|
|
2016-05-19 15:15:55 +02:00
|
|
|
|
def set_mode (self, mode):
|
|
|
|
|
'''
|
2020-09-27 16:52:56 +02:00
|
|
|
|
Modifie le mode de détection
|
2016-05-19 15:15:55 +02:00
|
|
|
|
'''
|
|
|
|
|
if mode == MODE_OFF:
|
2020-09-27 16:52:56 +02:00
|
|
|
|
m = mode_off
|
2016-05-19 15:15:55 +02:00
|
|
|
|
elif mode == MODE_CUSTOM:
|
2020-09-27 16:52:56 +02:00
|
|
|
|
m = mode_custom
|
2016-05-19 15:15:55 +02:00
|
|
|
|
elif mode == MODE_ON:
|
2020-09-27 16:52:56 +02:00
|
|
|
|
m = mode_on
|
2016-05-19 15:15:55 +02:00
|
|
|
|
else:
|
|
|
|
|
if self.DEBUG:
|
2020-09-27 16:52:56 +02:00
|
|
|
|
print ('Vous devriez utiliser les constantes MODE_OFF, MODE_ON et MODE_CUSTOM.')
|
2016-05-19 15:15:55 +02:00
|
|
|
|
raise ValueError
|
2020-09-27 16:52:56 +02:00
|
|
|
|
r = base_url + mode_set_path + '?' + mode_set_field + '=' + m
|
|
|
|
|
self.get_or_autologin (r)
|
2016-05-19 15:15:55 +02:00
|
|
|
|
return (True)
|
|
|
|
|
|
|
|
|
|
def get_mode (self):
|
|
|
|
|
'''
|
2020-09-27 16:52:56 +02:00
|
|
|
|
Retourne le mode de détection
|
2016-05-19 15:15:55 +02:00
|
|
|
|
'''
|
2020-10-20 17:02:34 +02:00
|
|
|
|
b = self.mysensors [1]
|
2020-09-27 16:52:56 +02:00
|
|
|
|
c = b [mode_get_label]
|
2016-05-19 15:15:55 +02:00
|
|
|
|
if self.DEBUG:
|
2020-09-27 16:52:56 +02:00
|
|
|
|
print ('Mode de détection ' + c)
|
|
|
|
|
if (c == mode_off):
|
2016-05-19 15:15:55 +02:00
|
|
|
|
return (MODE_OFF)
|
2020-09-27 16:52:56 +02:00
|
|
|
|
if (c == mode_custom):
|
2016-05-19 15:15:55 +02:00
|
|
|
|
return (MODE_CUSTOM)
|
2020-09-27 16:52:56 +02:00
|
|
|
|
if (c == mode_on):
|
2016-05-19 15:15:55 +02:00
|
|
|
|
return (MODE_ON)
|
|
|
|
|
return (None)
|
|
|
|
|
|
|
|
|
|
def list_sensors (self):
|
|
|
|
|
'''
|
2020-09-27 16:52:56 +02:00
|
|
|
|
Retourne une liste des IDs des capteurs
|
2016-05-19 15:15:55 +02:00
|
|
|
|
'''
|
2020-09-27 16:52:56 +02:00
|
|
|
|
r = base_url + sensors_list
|
|
|
|
|
a = bytes2file (self.get_or_autologin (r).read ())
|
|
|
|
|
b = get_xml_tree (a)
|
2016-05-19 15:15:55 +02:00
|
|
|
|
r = []
|
2020-09-27 16:52:56 +02:00
|
|
|
|
for i in b [2]:
|
|
|
|
|
try:
|
|
|
|
|
r.append (i [1] [sensors_label_id])
|
|
|
|
|
except KeyError:
|
|
|
|
|
pass
|
2016-05-19 15:15:55 +02:00
|
|
|
|
return (list (r))
|
|
|
|
|
|
|
|
|
|
def get_sensor (self, id):
|
|
|
|
|
'''
|
2020-09-27 16:52:56 +02:00
|
|
|
|
Retourne un objet Sensor à partir de l'ID
|
2016-05-19 15:15:55 +02:00
|
|
|
|
'''
|
2020-10-20 17:02:34 +02:00
|
|
|
|
return (Sensor (id, self.mysensors, self.get_or_autologin))
|
2016-05-19 15:15:55 +02:00
|
|
|
|
|
|
|
|
|
def get_all_sensors (self):
|
|
|
|
|
'''
|
2020-09-27 16:52:56 +02:00
|
|
|
|
Retourne un tuple d'objet Sensor contenant tous les capteurs
|
2016-05-19 15:15:55 +02:00
|
|
|
|
'''
|
|
|
|
|
r = []
|
|
|
|
|
for i in self.list_sensors ():
|
|
|
|
|
r.append (self.get_sensor (i))
|
2016-05-23 08:41:07 +02:00
|
|
|
|
return (tuple (r))
|
2016-05-22 22:57:39 +02:00
|
|
|
|
|
2019-01-15 23:02:35 +01:00
|
|
|
|
|
2020-09-27 16:52:56 +02:00
|
|
|
|
class Sensor ():
|
2020-10-20 17:02:34 +02:00
|
|
|
|
def __init__ (self, id, mysensors, get_or_autologin):
|
2016-05-22 22:57:39 +02:00
|
|
|
|
|
2016-05-25 17:19:16 +02:00
|
|
|
|
self.id = id
|
2020-10-20 17:02:34 +02:00
|
|
|
|
self.mysensors = mysensors
|
2020-09-27 16:52:56 +02:00
|
|
|
|
self.get_or_autologin = get_or_autologin
|
2016-05-25 17:19:16 +02:00
|
|
|
|
|
|
|
|
|
def refresh (self):
|
|
|
|
|
'''
|
2020-09-27 16:52:56 +02:00
|
|
|
|
Mets à jour les données du capteur
|
2016-05-25 17:19:16 +02:00
|
|
|
|
'''
|
2020-10-20 17:02:34 +02:00
|
|
|
|
self.mysensors.refresh ()
|
2016-05-22 22:57:39 +02:00
|
|
|
|
|
|
|
|
|
def get_raw (self):
|
|
|
|
|
'''
|
2020-09-27 16:52:56 +02:00
|
|
|
|
Retourne les données brutes du capteur
|
2016-05-22 22:57:39 +02:00
|
|
|
|
'''
|
2020-10-20 17:02:34 +02:00
|
|
|
|
for i in self.mysensors [2]:
|
|
|
|
|
if i [0] == sensors_label and i [1] [sensors_label_id] == self.id:
|
|
|
|
|
return (i [2])
|
2016-05-22 22:57:39 +02:00
|
|
|
|
|
2020-10-20 10:28:22 +02:00
|
|
|
|
def get_attributes (self, lst, key):
|
|
|
|
|
for i in lst:
|
|
|
|
|
if i [0] == key:
|
|
|
|
|
return (i [1])
|
|
|
|
|
raise KeyError ('no key ' + key)
|
|
|
|
|
|
2020-09-27 16:52:56 +02:00
|
|
|
|
def get_value (self, lst, key):
|
|
|
|
|
for i in lst:
|
|
|
|
|
if i [0] == key:
|
|
|
|
|
return (i [2])
|
2020-10-20 10:28:22 +02:00
|
|
|
|
raise KeyError ('no key ' + key)
|
2020-09-27 16:52:56 +02:00
|
|
|
|
|
2016-05-25 17:19:16 +02:00
|
|
|
|
def get_mac (self):
|
|
|
|
|
'''
|
2020-09-27 16:52:56 +02:00
|
|
|
|
Retourne l'adresse matérielle du capteur, s'il en a une
|
2016-05-25 17:19:16 +02:00
|
|
|
|
'''
|
2020-10-20 17:02:34 +02:00
|
|
|
|
return (self.get_value (self.get_raw (), sensors_mac_field))
|
2016-05-25 17:19:16 +02:00
|
|
|
|
|
2016-05-22 22:57:39 +02:00
|
|
|
|
def get_type (self):
|
|
|
|
|
'''
|
2020-09-27 16:52:56 +02:00
|
|
|
|
Retourne le type du capteur
|
|
|
|
|
Les types sont ceux définis dans les constantes
|
2016-05-22 22:57:39 +02:00
|
|
|
|
'''
|
2020-10-20 17:02:34 +02:00
|
|
|
|
return (self.get_value (self.get_raw (), sensors_type_field))
|
2016-05-22 22:57:39 +02:00
|
|
|
|
|
|
|
|
|
def get_model (self):
|
|
|
|
|
'''
|
2020-09-27 16:52:56 +02:00
|
|
|
|
Retourne le modèle du capteur
|
2016-05-22 22:57:39 +02:00
|
|
|
|
'''
|
2020-10-20 17:02:34 +02:00
|
|
|
|
return (self.get_value (self.get_raw (), sensors_model_field))
|
2016-05-22 22:57:39 +02:00
|
|
|
|
|
|
|
|
|
def get_version (self):
|
|
|
|
|
'''
|
2020-09-27 16:52:56 +02:00
|
|
|
|
Retourne la version du capteur
|
2016-05-22 22:57:39 +02:00
|
|
|
|
'''
|
2020-10-20 17:02:34 +02:00
|
|
|
|
return (self.get_value (self.get_raw (), sensors_version_field))
|
2016-05-22 22:57:39 +02:00
|
|
|
|
|
|
|
|
|
def get_name (self):
|
|
|
|
|
'''
|
2020-09-27 16:52:56 +02:00
|
|
|
|
Retourne le nom du capteur
|
2016-05-22 22:57:39 +02:00
|
|
|
|
'''
|
2020-10-20 17:02:34 +02:00
|
|
|
|
return (self.get_value (self.get_raw (), sensors_name_field))
|
2016-05-22 22:57:39 +02:00
|
|
|
|
|
|
|
|
|
def get_longname (self):
|
|
|
|
|
'''
|
2020-09-27 16:52:56 +02:00
|
|
|
|
Retourne un nom long du capteur composé de son type en français et de son nom
|
2016-05-22 22:57:39 +02:00
|
|
|
|
'''
|
2020-10-20 17:02:34 +02:00
|
|
|
|
return (self.get_value (self.get_raw (), sensors_longname_field))
|
2016-05-22 22:57:39 +02:00
|
|
|
|
|
|
|
|
|
def get_namegender (self):
|
|
|
|
|
'''
|
2020-09-27 16:52:56 +02:00
|
|
|
|
Retourne le genre du nom du type de capteur en français
|
|
|
|
|
M pour masculin et F pour féminin
|
2016-05-22 22:57:39 +02:00
|
|
|
|
'''
|
2020-10-20 17:02:34 +02:00
|
|
|
|
return (self.get_value (self.get_raw (), sensors_namegender_field))
|
2016-05-22 22:57:39 +02:00
|
|
|
|
|
|
|
|
|
def get_batterylevel (self):
|
|
|
|
|
'''
|
2020-09-27 16:52:56 +02:00
|
|
|
|
Retourne le niveau de batterie sur 10
|
|
|
|
|
Toute autre valeur doit être considérée comme venant d'un capteur n'ayant pas de batterie
|
2016-05-22 22:57:39 +02:00
|
|
|
|
'''
|
2020-10-20 17:02:34 +02:00
|
|
|
|
return (int (self.get_value (self.get_raw (), sensors_batterylevel_field)))
|
2016-05-22 22:57:39 +02:00
|
|
|
|
|
|
|
|
|
def get_signal (self):
|
|
|
|
|
'''
|
2020-09-27 16:52:56 +02:00
|
|
|
|
Retourne le niveau de signal sur 10
|
|
|
|
|
Tout autre valeur est pour un capteur connecté par câble
|
2016-05-22 22:57:39 +02:00
|
|
|
|
'''
|
2020-10-20 17:02:34 +02:00
|
|
|
|
return (int (self.get_value (self.get_raw (), sensors_signal_field)))
|
2016-05-22 22:57:39 +02:00
|
|
|
|
|
|
|
|
|
def get_status (self):
|
|
|
|
|
'''
|
2020-09-27 16:52:56 +02:00
|
|
|
|
Retourne True si le capteur est considéré comme opérationnel par le système
|
2016-05-22 22:57:39 +02:00
|
|
|
|
'''
|
2020-10-20 17:02:34 +02:00
|
|
|
|
return (self.get_value (self.get_raw (), sensors_status_field) == sensors_status_value_ok)
|
2016-05-25 17:19:16 +02:00
|
|
|
|
|
2020-09-26 07:51:52 +02:00
|
|
|
|
def get_camera_snapshot (self):
|
2016-05-25 17:19:16 +02:00
|
|
|
|
'''
|
2020-09-27 16:52:56 +02:00
|
|
|
|
Retourne une capture de la caméra dans un objet PIL.Image
|
2016-05-25 17:19:16 +02:00
|
|
|
|
'''
|
2020-09-27 16:52:56 +02:00
|
|
|
|
r = base_url + camera_snapshot + '&' + camera_snapshot_mac + '=' + self.get_mac ()
|
|
|
|
|
a = bytes2image (self.get_or_autologin (r).read ())
|
2020-09-26 07:51:52 +02:00
|
|
|
|
return (a)
|
|
|
|
|
|
|
|
|
|
def get_camera_petmode (self):
|
|
|
|
|
'''
|
2020-09-27 17:41:15 +02:00
|
|
|
|
Retourne l'état du mode animaux domestiques
|
|
|
|
|
Ce mode réduit la sensibilité du capteur pour éviter des déclanchements d'alarme dus aux animaux
|
2020-09-26 07:51:52 +02:00
|
|
|
|
'''
|
2020-10-20 17:02:34 +02:00
|
|
|
|
return (self.get_raw () [camera_get_config_petmode] == '1')
|
2020-09-26 07:51:52 +02:00
|
|
|
|
|
|
|
|
|
def get_camera_recording (self):
|
|
|
|
|
'''
|
2020-09-27 17:41:15 +02:00
|
|
|
|
Retourne l'état de l'enregistrement vidéo 24/24
|
2020-09-26 07:51:52 +02:00
|
|
|
|
'''
|
2020-10-20 17:02:34 +02:00
|
|
|
|
return (self.get_raw () [camera_get_config_recording] == '1')
|
2020-09-26 07:51:52 +02:00
|
|
|
|
|
|
|
|
|
def get_camera_privacy (self):
|
|
|
|
|
'''
|
2020-09-27 17:41:15 +02:00
|
|
|
|
Si cette méthode retourne True, la caméra est paramétrée pour ne pas capture d'image
|
|
|
|
|
'''
|
2020-10-20 17:02:34 +02:00
|
|
|
|
return (self.get_raw () [camera_get_config_privacy] == '1')
|
2020-09-27 17:41:15 +02:00
|
|
|
|
|
|
|
|
|
def get_temperature (self):
|
|
|
|
|
'''
|
|
|
|
|
Retourne la température donnée par le capteur
|
|
|
|
|
'''
|
2020-10-20 17:02:34 +02:00
|
|
|
|
a = self.get_value (self.get_raw (), sensors_temphum_root_field)
|
2020-09-27 17:41:15 +02:00
|
|
|
|
for i in a:
|
|
|
|
|
if i [1] [sensors_temp_name] == sensors_temp_text:
|
|
|
|
|
return (float (i [2].replace ('°C', '')))
|
|
|
|
|
|
|
|
|
|
def get_humidity (self):
|
|
|
|
|
'''
|
|
|
|
|
Retourne l'humidité donnée par le capteur
|
2020-09-26 07:51:52 +02:00
|
|
|
|
'''
|
2020-10-20 17:02:34 +02:00
|
|
|
|
a = self.get_value (self.get_raw (), sensors_temphum_root_field)
|
2020-09-27 17:41:15 +02:00
|
|
|
|
for i in a:
|
|
|
|
|
if i [1] [sensors_hum_name] == sensors_hum_text:
|
2020-10-20 10:28:22 +02:00
|
|
|
|
return (int (i [2].replace ('%', '')))
|
|
|
|
|
|
|
|
|
|
def get_on_off_state (self):
|
|
|
|
|
'''
|
|
|
|
|
Retourne l'état d'une prise connectée, True sur la prise est fermée
|
|
|
|
|
'''
|
2020-10-20 17:02:34 +02:00
|
|
|
|
a = self.get_attributes (self.get_raw (), sensors_oop_stateroot)
|
2020-10-20 10:28:22 +02:00
|
|
|
|
return (True if a [sensors_oop_state] == '1' else False)
|
|
|
|
|
|
2020-10-21 09:20:17 +02:00
|
|
|
|
def set_on_off_state (self, state):
|
|
|
|
|
'''
|
|
|
|
|
Défini l'état d'une prise connectée, True pour fermer la prise
|
|
|
|
|
'''
|
|
|
|
|
r = base_url + sensors_oop_control + '?' + sensors_oop_control_sensors_id_field + '=' + self.id + '&' + sensors_oop_control_action_field + '=' + (sensors_oop_control_action_value_on if state else sensors_oop_control_action_value_off)
|
|
|
|
|
self.get_or_autologin (r)
|
|
|
|
|
|
2020-10-20 10:28:22 +02:00
|
|
|
|
def get_on_off_power (self):
|
|
|
|
|
'''
|
|
|
|
|
Retourne la puissance active qui traverse la prise, en watts
|
|
|
|
|
'''
|
2020-10-20 17:02:34 +02:00
|
|
|
|
a = self.get_attributes (self.get_raw (), sensors_oop_stateroot)
|
2020-10-20 10:28:22 +02:00
|
|
|
|
return (int (a [sensors_oop_power]))
|