Addind Common class for common ressources
Python2: - Adding Common class for common ressources - Inheritance from Common to HomeSFR and Sensors (and it's inherited class Camera) - Adding the Sensors.get_mac () method - Adding the Camera.get_snapshot () method - Addind str2bytes () function for Python3 code compatibility (this library and the Python3 ones are quite identical) Python3: - Adding Common class for common ressources - Inheritance from Common to HomeSFR and Sensors (and it's inherited class Camera) - Adding the Sensors.get_mac () method - Adding the Camera.get_snapshot () method - Addind str2bytes () function for Python2 code compatibility (this library and the Python2 ones are quite identical) - Changing all "readall" to "read", the method "readall" does not seem to exist in Python2, from what I tested, there's no problem with it
This commit is contained in:
parent
beae1f2b97
commit
9dd904b4ea
|
@ -34,6 +34,7 @@ REMOTE_CONTROLER = 'REMOTE'
|
|||
KEYPAD_CONTROLER = 'KEYPAD'
|
||||
PRESENCE_CAMERA_DETECTOR = 'PIR_CAMERA'
|
||||
|
||||
# This part of code must be the one to adapt to make work the underneath part
|
||||
from cookielib import CookieJar
|
||||
import urllib2 as request
|
||||
from urllib2 import HTTPError
|
||||
|
@ -41,28 +42,119 @@ from urllib import urlencode
|
|||
from xml.etree import ElementTree as ET
|
||||
from datetime import datetime
|
||||
|
||||
def bytes2file (b):
|
||||
# Python2 and 3 adaptations
|
||||
# BEGIN
|
||||
def str2bytes (s):
|
||||
'''
|
||||
Gives a file-like class from a Bytes
|
||||
Bind to the bytes build-in function
|
||||
'''
|
||||
from io import BytesIO
|
||||
r = BytesIO ()
|
||||
r.write (b)
|
||||
r.seek (0)
|
||||
return (r)
|
||||
return (bytes (s))
|
||||
# END
|
||||
|
||||
def bytes2image (b):
|
||||
'''
|
||||
Gives a Image object from bytes
|
||||
Uses the bytes2file function
|
||||
'''
|
||||
from PIL import Image
|
||||
f = bytes2file (b)
|
||||
r = Image ()
|
||||
r.open (f)
|
||||
return (r)
|
||||
# The next must stay a copy from the python3 library, starting from class Common
|
||||
|
||||
class HomeSFR ():
|
||||
class Common ():
|
||||
'''
|
||||
Common ressources to the library's classes
|
||||
'''
|
||||
|
||||
def __init__ (self):
|
||||
|
||||
# Specific configuration
|
||||
|
||||
self.base_url = 'https://home.sfr.fr'
|
||||
|
||||
# path to login test
|
||||
self.auth_path = '/mysensors'
|
||||
self.auth_ok = '/accueil' # if logged
|
||||
self.auth_post_url = 'https://boutique.home.sfr.fr/authentification'
|
||||
self.auth_referer = 'https://boutique.home.sfr.fr/authentification?back=service'
|
||||
self.auth_user_field = 'email'
|
||||
self.auth_pass_field = 'passwd'
|
||||
self.auth_extra_fields = {'back': 'service', 'token_sso': '', 'error_sso': '', 'SubmitLogin': 'OK'}
|
||||
self.auth_logout_path = '/deconnexion'
|
||||
|
||||
# Path to sensors and mode
|
||||
self.sensors_list = '/mysensors'
|
||||
|
||||
# Path to list of alerts
|
||||
self.alerts_path = '/listalert'
|
||||
|
||||
# Path to get and set modes
|
||||
self.mode_get_path = '/mysensors'
|
||||
self.mode_get_label = 'alarm_mode'
|
||||
self.mode_set_path = '/alarmmode'
|
||||
self.mode_set_field = 'action' # Name for GET field
|
||||
self.mode_off = 'OFF' # Value for off
|
||||
self.mode_custom = 'CUSTOM' # Value for custom
|
||||
self.mode_on = 'ON' # Value for on
|
||||
|
||||
# Cameras
|
||||
self.cameras_list = '/homescope/mycams'
|
||||
self.camera_snapshot = '/homescope/snapshot'
|
||||
self.camera_snapshot_mac = 'mac'
|
||||
self.camera_video = '/homescope/flv'
|
||||
self.camera_vide_mac = 'mac'
|
||||
self.camera_recordings_list = '/listenr'
|
||||
self.camera_recordings_delete = '/delenr'
|
||||
self.camera_recordings_start = '/homescope/record'
|
||||
self.camera_recordings_stop = '/homescope/stoprecord'
|
||||
self.camera_recordings_mac = 'mac'
|
||||
self.camera_get_config_path = '/homescope/camsettings'
|
||||
self.camera_get_config_mac = 'mac'
|
||||
self.camera_get_config_flip = 'FLIP'
|
||||
self.camera_get_config_leds = 'LEDMODE' # set to 0 to turn the leds on
|
||||
self.camera_get_config_detectionsensibility = 'DP' # from 1 to 4
|
||||
self.camera_get_config_recording = 'REC24'
|
||||
self.camera_get_config_name = 'NAME'
|
||||
self.camera_set_config_path = '/homescope/camsettings'
|
||||
self.camera_set_config_mac = 'mac'
|
||||
self.camera_set_config_flip = 'flip'
|
||||
self.camera_set_config_leds = 'led_mode' # set to 0 to turn the leds on
|
||||
self.camera_set_config_detectionsensibility = 'dp' # from 1 to 4
|
||||
self.camera_set_config_recording = 'rec24'
|
||||
self.camera_set_config_name = 'name'
|
||||
|
||||
# Sensors
|
||||
self.sensors_list = '/mysensors'
|
||||
self.sensors_label = 'Sensor'
|
||||
self.sensors_label_id = 'id'
|
||||
self.sensors_mac_field = 'deviceMac'
|
||||
self.sensors_type_field = 'deviceType'
|
||||
self.sensors_model_field = 'deviceModel'
|
||||
self.sensors_version_field = 'deviceVersion'
|
||||
self.sensors_name_field = 'name'
|
||||
self.sensors_longname_field = 'long_name'
|
||||
self.sensors_namegender_field = 'name_gender' # Only usefull for French for the moment
|
||||
self.sensors_batterylevel_field = 'batteryLevel'
|
||||
self.sensors_signal_field = 'signalLevel'
|
||||
self.sensors_lasttrigger_field = 'lastTriggerTime'
|
||||
self.sensors_lasttrigger_dateformat = '%Y-%m-%d %H:%M:%S'
|
||||
self.sensors_status_field = 'status'
|
||||
self.sensors_status_value_ok = 'OK'
|
||||
# I don't have any other value for the moment
|
||||
|
||||
def bytes2file (self, b):
|
||||
'''
|
||||
Gives a file-like class from a Bytes
|
||||
'''
|
||||
from io import BytesIO
|
||||
r = BytesIO ()
|
||||
r.write (b)
|
||||
r.seek (0)
|
||||
return (r)
|
||||
|
||||
def bytes2image (self, b):
|
||||
'''
|
||||
Gives a Image object from bytes
|
||||
Uses the bytes2file function
|
||||
'''
|
||||
from PIL import Image
|
||||
f = self.bytes2file (b)
|
||||
r = Image.open (f)
|
||||
return (r)
|
||||
|
||||
class HomeSFR (Common):
|
||||
def __init__ (self, username = None, password = None, cookies = None, debug = False, autologin = True):
|
||||
'''
|
||||
Sets the class with username and password couple, or cookies
|
||||
|
@ -71,6 +163,7 @@ class HomeSFR ():
|
|||
The autologin parameter defines if the class will manage the login by itself, if False, the user must call login () to login and test_login () to check the login
|
||||
The autologin paramater will always be False if no username and password are defined, and the login () method will always return False
|
||||
'''
|
||||
Common.__init__ (self)
|
||||
self.DEBUG = debug
|
||||
if self.DEBUG:
|
||||
print (name + ' ' + version)
|
||||
|
@ -97,49 +190,7 @@ class HomeSFR ():
|
|||
else:
|
||||
self.cookies = cookies
|
||||
self.opener = request.build_opener (request.HTTPCookieProcessor (self.cookies))
|
||||
|
||||
# Specific configuration
|
||||
self.base_url = 'https://home.sfr.fr'
|
||||
|
||||
# path to login test
|
||||
self.auth_path = '/mysensors'
|
||||
self.auth_ok = '/accueil' # if logged
|
||||
self.auth_post_url = 'https://boutique.home.sfr.fr/authentification'
|
||||
self.auth_referer = 'https://boutique.home.sfr.fr/authentification?back=service'
|
||||
self.auth_user_field = 'email'
|
||||
self.auth_pass_field = 'passwd'
|
||||
self.auth_extra_fields = {'back': 'service', 'token_sso': '', 'error_sso': '', 'SubmitLogin': 'OK'}
|
||||
self.auth_logout_path = '/deconnexion'
|
||||
|
||||
# Path to sensors and mode
|
||||
self.sensors_list = '/mysensors'
|
||||
self.sensors_label = 'Sensor'
|
||||
self.sensors_label_id = 'id'
|
||||
|
||||
# Path to list of alerts
|
||||
self.alerts_path = '/listalert'
|
||||
|
||||
# Path to get and set modes
|
||||
self.mode_get_path = '/mysensors'
|
||||
self.mode_get_label = 'alarm_mode'
|
||||
self.mode_set_path = '/alarmmode'
|
||||
self.mode_set_field = 'action' # Name for GET field
|
||||
self.mode_off = 'OFF' # Value for off
|
||||
self.mode_custom = 'CUSTOM' # Value for custom
|
||||
self.mode_on = 'ON' # Value for on
|
||||
|
||||
# Cameras
|
||||
# mac=00:0e:8f:c9:59:44&flip=0&led_mode=0&alert_pan=1&rec24=0&da=1&dp=4&name=Salon
|
||||
self.cameras_list = '/homescope/mycams'
|
||||
self.camera_snapshot = '/homescope/snapshot'
|
||||
self.camera_video = '/homescope/flv'
|
||||
self.camera_get_config_path = '/homescope/camsettings'
|
||||
self.camera_set_config_path = '/homescope/camsettings'
|
||||
self.camera_set_config_mac = 'mac'
|
||||
self.camera_set_config_flip = 'flip'
|
||||
self.camera_set_config_leds = 'led_mode' # set to 0 to turn the leds on
|
||||
self.camera_set_config_detectionsensibility = 'dp' # from 1 to 4,
|
||||
|
||||
|
||||
def __str__ (self):
|
||||
'''
|
||||
Shows name, version, defined user and debug state
|
||||
|
@ -178,7 +229,7 @@ class HomeSFR ():
|
|||
data = self.auth_extra_fields
|
||||
data [self.auth_user_field] = self.username
|
||||
data [self.auth_pass_field] = self.password
|
||||
data = bytes (urlencode (data))
|
||||
data = str2bytes (urlencode (data))
|
||||
if self.DEBUG:
|
||||
print ('Cookies ' + str( len(self.cookies)))
|
||||
print ('Sending data ' + str (data))
|
||||
|
@ -243,7 +294,7 @@ class HomeSFR ():
|
|||
r = self.base_url + self.mode_get_path
|
||||
if self.DEBUG:
|
||||
print ('Getting ' + r)
|
||||
a = bytes2file (self.opener.open (r).read ())
|
||||
a = self.bytes2file (self.opener.open (r).read ())
|
||||
b = ET.parse (a).getroot ()
|
||||
c = b.get (self.mode_get_label)
|
||||
if self.DEBUG:
|
||||
|
@ -263,7 +314,7 @@ class HomeSFR ():
|
|||
if (self.autologin and self.test_login () == False):
|
||||
self.login ()
|
||||
r = self.base_url + self.sensors_list
|
||||
a = bytes2file (self.opener.open (r).read ())
|
||||
a = self.bytes2file (self.opener.open (r).read ())
|
||||
b = ET.parse (a)
|
||||
r = []
|
||||
for i in b.findall (self.sensors_label):
|
||||
|
@ -275,29 +326,11 @@ class HomeSFR ():
|
|||
Returns a Sensor object for the sensor id or None if sensor is not found
|
||||
The available ids can be got from the list_sensors method
|
||||
'''
|
||||
def build_tree (element):
|
||||
r = {}
|
||||
if self.DEBUG:
|
||||
print ('Diving in the element ' + element.tag)
|
||||
for i in element.getchildren ():
|
||||
if i.getchildren () == []:
|
||||
r.update ({i.tag: i.text})
|
||||
else:
|
||||
r.update ({i.tag: build_tree (i)})
|
||||
return (r)
|
||||
if (self.autologin and self.test_login () == False):
|
||||
self.login ()
|
||||
r = self.base_url + self.sensors_list
|
||||
a = bytes2file (self.opener.open (r).read ())
|
||||
b = ET.parse (a)
|
||||
r = None
|
||||
for i in b.findall (self.sensors_label):
|
||||
if self.DEBUG:
|
||||
print ('Testing sensors ' + i.get (self.sensors_label_id))
|
||||
if (i.get (self.sensors_label_id) == id):
|
||||
r = build_tree (i)
|
||||
break
|
||||
return (Sensor (r))
|
||||
r = Sensor (id, self.opener)
|
||||
r.refresh ()
|
||||
return (r)
|
||||
|
||||
def get_all_sensors (self):
|
||||
'''
|
||||
|
@ -307,32 +340,52 @@ class HomeSFR ():
|
|||
for i in self.list_sensors ():
|
||||
r.append (self.get_sensor (i))
|
||||
return (tuple (r))
|
||||
|
||||
def get_camera (self, id):
|
||||
'''
|
||||
Get a Camera object from the sensor's id
|
||||
'''
|
||||
if (self.autologin and self.test_login () == False):
|
||||
self.login ()
|
||||
r = Camera (id, self.opener)
|
||||
r.refresh ()
|
||||
return (r)
|
||||
|
||||
class Sensor:
|
||||
class Sensor (Common):
|
||||
'''
|
||||
Class used to read easily the sensors
|
||||
'''
|
||||
def __init__ (self, sensor_dict):
|
||||
def __init__ (self, id, opener):
|
||||
'''
|
||||
Initialize the class with the dict producted by HomeSFR.get_sensors ()
|
||||
'''
|
||||
Common.__init__ (self)
|
||||
|
||||
self.sensor_dict = sensor_dict
|
||||
|
||||
# Field names
|
||||
self.type_field = 'deviceType'
|
||||
self.model_field = 'deviceModel'
|
||||
self.version_field = 'deviceVersion'
|
||||
self.name_field = 'name'
|
||||
self.longname_field = 'long_name'
|
||||
self.namegender_field = 'name_gender' # Only usefull for French for the moment
|
||||
self.batterylevel_field = 'batteryLevel'
|
||||
self.signal_field = 'signalLevel'
|
||||
self.lasttrigger_field = 'lastTriggerTime'
|
||||
self.lasttrigger_dateformat = '%Y-%m-%d %H:%M:%S'
|
||||
self.status_field = 'status'
|
||||
self.status_value_ok = 'OK'
|
||||
# I don't have any other value for the moment
|
||||
self.id = id
|
||||
self.sensor_dict = None
|
||||
self.opener = opener
|
||||
|
||||
def refresh (self):
|
||||
'''
|
||||
Gets or refresh the data for the sensor
|
||||
'''
|
||||
def build_tree (element):
|
||||
r = {}
|
||||
for i in element.getchildren ():
|
||||
if i.getchildren () == []:
|
||||
r.update ({i.tag: i.text})
|
||||
else:
|
||||
r.update ({i.tag: build_tree (i)})
|
||||
return (r)
|
||||
r = self.base_url + self.sensors_list
|
||||
a = self.bytes2file (self.opener.open (r).read ())
|
||||
a.seek (0)
|
||||
b = ET.parse (a)
|
||||
self.sensor_dict = None
|
||||
for i in b.findall (self.sensors_label):
|
||||
if (i.get (self.sensors_label_id) == self.id):
|
||||
self.sensor_dict = build_tree (i)
|
||||
break
|
||||
|
||||
def get_raw (self):
|
||||
'''
|
||||
|
@ -340,72 +393,97 @@ class Sensor:
|
|||
'''
|
||||
return (self.sensor_dict)
|
||||
|
||||
def get_mac (self):
|
||||
'''
|
||||
Returns the sensor's model, if any, None either
|
||||
'''
|
||||
return (self.sensor_dict [self.sensors_mac_field])
|
||||
|
||||
def get_type (self):
|
||||
'''
|
||||
Returns the sensor's type
|
||||
'''
|
||||
return (self.sensor_dict [self.type_field])
|
||||
return (self.sensor_dict [self.sensors_type_field])
|
||||
|
||||
def get_model (self):
|
||||
'''
|
||||
Returns the sensor's model, if any, None either
|
||||
'''
|
||||
return (self.sensor_dict [self.model_field])
|
||||
return (self.sensor_dict [self.sensors_model_field])
|
||||
|
||||
def get_version (self):
|
||||
'''
|
||||
Returns the sensor's version
|
||||
'''
|
||||
return (self.sensor_dict [self.version_field])
|
||||
return (self.sensor_dict [self.sensors_version_field])
|
||||
|
||||
def get_name (self):
|
||||
'''
|
||||
Returns the sensor's name
|
||||
'''
|
||||
return (self.sensor_dict [self.name_field])
|
||||
return (self.sensor_dict [self.sensors_name_field])
|
||||
|
||||
def get_longname (self):
|
||||
'''
|
||||
Returns the sensor's type name in system's language and the sensor's name
|
||||
'''
|
||||
return (self.sensor_dict [self.longname_field])
|
||||
return (self.sensor_dict [self.sensors_longname_field])
|
||||
|
||||
def get_namegender (self):
|
||||
'''
|
||||
Return M for male and F for female.
|
||||
Only usefull for languages with gender on nouns
|
||||
'''
|
||||
return (self.sensor_dict [self.namegender_field])
|
||||
return (self.sensor_dict [self.sensors_namegender_field])
|
||||
|
||||
def get_batterylevel (self):
|
||||
'''
|
||||
Returns the sensor's battery level, out of 10
|
||||
It seems that batteryless sensors return 255
|
||||
'''
|
||||
return (int (self.sensor_dict [self.batterylevel_field]))
|
||||
return (int (self.sensor_dict [self.sensors_batterylevel_field]))
|
||||
|
||||
def get_signal (self):
|
||||
'''
|
||||
Returns the sensor's signal quality, out of 10
|
||||
'''
|
||||
return (int (self.sensor_dict [self.signal_field]))
|
||||
return (int (self.sensor_dict [self.sensors_signal_field]))
|
||||
|
||||
def get_lasttrigger (self):
|
||||
'''
|
||||
Return the timestamp of the sensor's last triger
|
||||
The sensors always trigger, even when the alarm's mode is off
|
||||
'''
|
||||
a = self.sensor_dict [self.lasttrigger_field]
|
||||
a = self.sensor_dict [self.sensors_lasttrigger_field]
|
||||
# Try because camera return the date '0000-00-00 00:00:00' that is ununderstandable
|
||||
try:
|
||||
b = datetime.strptime (a, self.lasttrigger_dateformat)
|
||||
b = datetime.strptime (a, self.sensors_lasttrigger_dateformat)
|
||||
except ValueError:
|
||||
return (0)
|
||||
r = int (b.strftime ('%s'))
|
||||
r = int (b.timestamp ())
|
||||
return (r)
|
||||
|
||||
def get_status (self):
|
||||
'''
|
||||
Returns True is the sensor is OK, False either
|
||||
'''
|
||||
return (self.sensor_dict [self.status_field] == self.status_value_ok)
|
||||
return (self.sensor_dict [self.sensors_status_field] == self.sensors_status_value_ok)
|
||||
|
||||
class Camera (Sensor):
|
||||
'''
|
||||
Class used to manipulate easily cameras
|
||||
'''
|
||||
def __init__ (self, sensor_dict, opener):
|
||||
'''
|
||||
Initialize the class with the dict producted by HomeSFR.get_camera ()
|
||||
'''
|
||||
Sensor.__init__ (self, sensor_dict, opener)
|
||||
|
||||
def get_snapshot (self):
|
||||
'''
|
||||
Get a snapshot from the camera
|
||||
Return a PIL.Image object
|
||||
'''
|
||||
r = self.base_url + self.camera_snapshot + '?' + self.camera_snapshot_mac + '=' + self.get_mac ()
|
||||
a = self.bytes2image (self.opener.open (r).read ())
|
||||
return (a)
|
|
@ -7,6 +7,7 @@ This is a wrap aroud website, this could stop working without prior notice
|
|||
'''
|
||||
|
||||
# TODO:
|
||||
## Put common ressources in the Common class
|
||||
## Manage cameras
|
||||
### Get image
|
||||
### Get video
|
||||
|
@ -39,28 +40,117 @@ from xml.etree import ElementTree as ET
|
|||
from urllib.error import HTTPError
|
||||
from datetime import datetime
|
||||
|
||||
def bytes2file (b):
|
||||
# Python2 and 3 adaptations
|
||||
# BEGIN
|
||||
def str2bytes (s):
|
||||
'''
|
||||
Gives a file-like class from a Bytes
|
||||
Bind to the bytes build-in function
|
||||
'''
|
||||
from io import BytesIO
|
||||
r = BytesIO ()
|
||||
r.write (b)
|
||||
r.seek (0)
|
||||
return (r)
|
||||
return (bytes (s, 'UTF8'))
|
||||
# END
|
||||
|
||||
def bytes2image (b):
|
||||
class Common ():
|
||||
'''
|
||||
Gives a Image object from bytes
|
||||
Uses the bytes2file function
|
||||
Common ressources to the library's classes
|
||||
'''
|
||||
from PIL import Image
|
||||
f = bytes2file (b)
|
||||
r = Image ()
|
||||
r.open (f)
|
||||
return (r)
|
||||
|
||||
def __init__ (self):
|
||||
|
||||
# Specific configuration
|
||||
|
||||
self.base_url = 'https://home.sfr.fr'
|
||||
|
||||
# path to login test
|
||||
self.auth_path = '/mysensors'
|
||||
self.auth_ok = '/accueil' # if logged
|
||||
self.auth_post_url = 'https://boutique.home.sfr.fr/authentification'
|
||||
self.auth_referer = 'https://boutique.home.sfr.fr/authentification?back=service'
|
||||
self.auth_user_field = 'email'
|
||||
self.auth_pass_field = 'passwd'
|
||||
self.auth_extra_fields = {'back': 'service', 'token_sso': '', 'error_sso': '', 'SubmitLogin': 'OK'}
|
||||
self.auth_logout_path = '/deconnexion'
|
||||
|
||||
# Path to sensors and mode
|
||||
self.sensors_list = '/mysensors'
|
||||
|
||||
# Path to list of alerts
|
||||
self.alerts_path = '/listalert'
|
||||
|
||||
# Path to get and set modes
|
||||
self.mode_get_path = '/mysensors'
|
||||
self.mode_get_label = 'alarm_mode'
|
||||
self.mode_set_path = '/alarmmode'
|
||||
self.mode_set_field = 'action' # Name for GET field
|
||||
self.mode_off = 'OFF' # Value for off
|
||||
self.mode_custom = 'CUSTOM' # Value for custom
|
||||
self.mode_on = 'ON' # Value for on
|
||||
|
||||
# Cameras
|
||||
self.cameras_list = '/homescope/mycams'
|
||||
self.camera_snapshot = '/homescope/snapshot'
|
||||
self.camera_snapshot_mac = 'mac'
|
||||
self.camera_video = '/homescope/flv'
|
||||
self.camera_vide_mac = 'mac'
|
||||
self.camera_recordings_list = '/listenr'
|
||||
self.camera_recordings_delete = '/delenr'
|
||||
self.camera_recordings_start = '/homescope/record'
|
||||
self.camera_recordings_stop = '/homescope/stoprecord'
|
||||
self.camera_recordings_mac = 'mac'
|
||||
self.camera_get_config_path = '/homescope/camsettings'
|
||||
self.camera_get_config_mac = 'mac'
|
||||
self.camera_get_config_flip = 'FLIP'
|
||||
self.camera_get_config_leds = 'LEDMODE' # set to 0 to turn the leds on
|
||||
self.camera_get_config_detectionsensibility = 'DP' # from 1 to 4
|
||||
self.camera_get_config_recording = 'REC24'
|
||||
self.camera_get_config_name = 'NAME'
|
||||
self.camera_set_config_path = '/homescope/camsettings'
|
||||
self.camera_set_config_mac = 'mac'
|
||||
self.camera_set_config_flip = 'flip'
|
||||
self.camera_set_config_leds = 'led_mode' # set to 0 to turn the leds on
|
||||
self.camera_set_config_detectionsensibility = 'dp' # from 1 to 4
|
||||
self.camera_set_config_recording = 'rec24'
|
||||
self.camera_set_config_name = 'name'
|
||||
|
||||
# Sensors
|
||||
self.sensors_list = '/mysensors'
|
||||
self.sensors_label = 'Sensor'
|
||||
self.sensors_label_id = 'id'
|
||||
self.sensors_mac_field = 'deviceMac'
|
||||
self.sensors_type_field = 'deviceType'
|
||||
self.sensors_model_field = 'deviceModel'
|
||||
self.sensors_version_field = 'deviceVersion'
|
||||
self.sensors_name_field = 'name'
|
||||
self.sensors_longname_field = 'long_name'
|
||||
self.sensors_namegender_field = 'name_gender' # Only usefull for French for the moment
|
||||
self.sensors_batterylevel_field = 'batteryLevel'
|
||||
self.sensors_signal_field = 'signalLevel'
|
||||
self.sensors_lasttrigger_field = 'lastTriggerTime'
|
||||
self.sensors_lasttrigger_dateformat = '%Y-%m-%d %H:%M:%S'
|
||||
self.sensors_status_field = 'status'
|
||||
self.sensors_status_value_ok = 'OK'
|
||||
# I don't have any other value for the moment
|
||||
|
||||
class HomeSFR ():
|
||||
def bytes2file (self, b):
|
||||
'''
|
||||
Gives a file-like class from a Bytes
|
||||
'''
|
||||
from io import BytesIO
|
||||
r = BytesIO ()
|
||||
r.write (b)
|
||||
r.seek (0)
|
||||
return (r)
|
||||
|
||||
def bytes2image (self, b):
|
||||
'''
|
||||
Gives a Image object from bytes
|
||||
Uses the bytes2file function
|
||||
'''
|
||||
from PIL import Image
|
||||
f = self.bytes2file (b)
|
||||
r = Image.open (f)
|
||||
return (r)
|
||||
|
||||
class HomeSFR (Common):
|
||||
def __init__ (self, username = None, password = None, cookies = None, debug = False, autologin = True):
|
||||
'''
|
||||
Sets the class with username and password couple, or cookies
|
||||
|
@ -69,6 +159,7 @@ class HomeSFR ():
|
|||
The autologin parameter defines if the class will manage the login by itself, if False, the user must call login () to login and test_login () to check the login
|
||||
The autologin paramater will always be False if no username and password are defined, and the login () method will always return False
|
||||
'''
|
||||
Common.__init__ (self)
|
||||
self.DEBUG = debug
|
||||
if self.DEBUG:
|
||||
print (name + ' ' + version)
|
||||
|
@ -95,48 +186,6 @@ class HomeSFR ():
|
|||
else:
|
||||
self.cookies = cookies
|
||||
self.opener = request.build_opener (request.HTTPCookieProcessor (self.cookies))
|
||||
|
||||
# Specific configuration
|
||||
self.base_url = 'https://home.sfr.fr'
|
||||
|
||||
# path to login test
|
||||
self.auth_path = '/mysensors'
|
||||
self.auth_ok = '/accueil' # if logged
|
||||
self.auth_post_url = 'https://boutique.home.sfr.fr/authentification'
|
||||
self.auth_referer = 'https://boutique.home.sfr.fr/authentification?back=service'
|
||||
self.auth_user_field = 'email'
|
||||
self.auth_pass_field = 'passwd'
|
||||
self.auth_extra_fields = {'back': 'service', 'token_sso': '', 'error_sso': '', 'SubmitLogin': 'OK'}
|
||||
self.auth_logout_path = '/deconnexion'
|
||||
|
||||
# Path to sensors and mode
|
||||
self.sensors_list = '/mysensors'
|
||||
self.sensors_label = 'Sensor'
|
||||
self.sensors_label_id = 'id'
|
||||
|
||||
# Path to list of alerts
|
||||
self.alerts_path = '/listalert'
|
||||
|
||||
# Path to get and set modes
|
||||
self.mode_get_path = '/mysensors'
|
||||
self.mode_get_label = 'alarm_mode'
|
||||
self.mode_set_path = '/alarmmode'
|
||||
self.mode_set_field = 'action' # Name for GET field
|
||||
self.mode_off = 'OFF' # Value for off
|
||||
self.mode_custom = 'CUSTOM' # Value for custom
|
||||
self.mode_on = 'ON' # Value for on
|
||||
|
||||
# Cameras
|
||||
# mac=00:0e:8f:c9:59:44&flip=0&led_mode=0&alert_pan=1&rec24=0&da=1&dp=4&name=Salon
|
||||
self.cameras_list = '/homescope/mycams'
|
||||
self.camera_snapshot = '/homescope/snapshot'
|
||||
self.camera_video = '/homescope/flv'
|
||||
self.camera_get_config_path = '/homescope/camsettings'
|
||||
self.camera_set_config_path = '/homescope/camsettings'
|
||||
self.camera_set_config_mac = 'mac'
|
||||
self.camera_set_config_flip = 'flip'
|
||||
self.camera_set_config_leds = 'led_mode' # set to 0 to turn the leds on
|
||||
self.camera_set_config_detectionsensibility = 'dp' # from 1 to 4,
|
||||
|
||||
def __str__ (self):
|
||||
'''
|
||||
|
@ -176,7 +225,7 @@ class HomeSFR ():
|
|||
data = self.auth_extra_fields
|
||||
data [self.auth_user_field] = self.username
|
||||
data [self.auth_pass_field] = self.password
|
||||
data = bytes (urlencode (data), 'UTF8')
|
||||
data = str2bytes (urlencode (data))
|
||||
if self.DEBUG:
|
||||
print ('Cookies ' + str( len(self.cookies)))
|
||||
print ('Sending data ' + str (data))
|
||||
|
@ -241,7 +290,7 @@ class HomeSFR ():
|
|||
r = self.base_url + self.mode_get_path
|
||||
if self.DEBUG:
|
||||
print ('Getting ' + r)
|
||||
a = bytes2file (self.opener.open (r).readall ())
|
||||
a = self.bytes2file (self.opener.open (r).read ())
|
||||
b = ET.parse (a).getroot ()
|
||||
c = b.get (self.mode_get_label)
|
||||
if self.DEBUG:
|
||||
|
@ -261,7 +310,7 @@ class HomeSFR ():
|
|||
if (self.autologin and self.test_login () == False):
|
||||
self.login ()
|
||||
r = self.base_url + self.sensors_list
|
||||
a = bytes2file (self.opener.open (r).readall ())
|
||||
a = self.bytes2file (self.opener.open (r).read ())
|
||||
b = ET.parse (a)
|
||||
r = []
|
||||
for i in b.findall (self.sensors_label):
|
||||
|
@ -273,29 +322,11 @@ class HomeSFR ():
|
|||
Returns a Sensor object for the sensor id or None if sensor is not found
|
||||
The available ids can be got from the list_sensors method
|
||||
'''
|
||||
def build_tree (element):
|
||||
r = {}
|
||||
if self.DEBUG:
|
||||
print ('Diving in the element ' + element.tag)
|
||||
for i in element.getchildren ():
|
||||
if i.getchildren () == []:
|
||||
r.update ({i.tag: i.text})
|
||||
else:
|
||||
r.update ({i.tag: build_tree (i)})
|
||||
return (r)
|
||||
if (self.autologin and self.test_login () == False):
|
||||
self.login ()
|
||||
r = self.base_url + self.sensors_list
|
||||
a = bytes2file (self.opener.open (r).readall ())
|
||||
b = ET.parse (a)
|
||||
r = None
|
||||
for i in b.findall (self.sensors_label):
|
||||
if self.DEBUG:
|
||||
print ('Testing sensors ' + i.get (self.sensors_label_id))
|
||||
if (i.get (self.sensors_label_id) == id):
|
||||
r = build_tree (i)
|
||||
break
|
||||
return (Sensor (r))
|
||||
r = Sensor (id, self.opener)
|
||||
r.refresh ()
|
||||
return (r)
|
||||
|
||||
def get_all_sensors (self):
|
||||
'''
|
||||
|
@ -305,32 +336,52 @@ class HomeSFR ():
|
|||
for i in self.list_sensors ():
|
||||
r.append (self.get_sensor (i))
|
||||
return (tuple (r))
|
||||
|
||||
def get_camera (self, id):
|
||||
'''
|
||||
Get a Camera object from the sensor's id
|
||||
'''
|
||||
if (self.autologin and self.test_login () == False):
|
||||
self.login ()
|
||||
r = Camera (id, self.opener)
|
||||
r.refresh ()
|
||||
return (r)
|
||||
|
||||
class Sensor:
|
||||
class Sensor (Common):
|
||||
'''
|
||||
Class used to read easily the sensors
|
||||
'''
|
||||
def __init__ (self, sensor_dict):
|
||||
def __init__ (self, id, opener):
|
||||
'''
|
||||
Initialize the class with the dict producted by HomeSFR.get_sensors ()
|
||||
'''
|
||||
Common.__init__ (self)
|
||||
|
||||
self.sensor_dict = sensor_dict
|
||||
|
||||
# Field names
|
||||
self.type_field = 'deviceType'
|
||||
self.model_field = 'deviceModel'
|
||||
self.version_field = 'deviceVersion'
|
||||
self.name_field = 'name'
|
||||
self.longname_field = 'long_name'
|
||||
self.namegender_field = 'name_gender' # Only usefull for French for the moment
|
||||
self.batterylevel_field = 'batteryLevel'
|
||||
self.signal_field = 'signalLevel'
|
||||
self.lasttrigger_field = 'lastTriggerTime'
|
||||
self.lasttrigger_dateformat = '%Y-%m-%d %H:%M:%S'
|
||||
self.status_field = 'status'
|
||||
self.status_value_ok = 'OK'
|
||||
# I don't have any other value for the moment
|
||||
self.id = id
|
||||
self.sensor_dict = None
|
||||
self.opener = opener
|
||||
|
||||
def refresh (self):
|
||||
'''
|
||||
Gets or refresh the data for the sensor
|
||||
'''
|
||||
def build_tree (element):
|
||||
r = {}
|
||||
for i in element.getchildren ():
|
||||
if i.getchildren () == []:
|
||||
r.update ({i.tag: i.text})
|
||||
else:
|
||||
r.update ({i.tag: build_tree (i)})
|
||||
return (r)
|
||||
r = self.base_url + self.sensors_list
|
||||
a = self.bytes2file (self.opener.open (r).read ())
|
||||
a.seek (0)
|
||||
b = ET.parse (a)
|
||||
self.sensor_dict = None
|
||||
for i in b.findall (self.sensors_label):
|
||||
if (i.get (self.sensors_label_id) == self.id):
|
||||
self.sensor_dict = build_tree (i)
|
||||
break
|
||||
|
||||
def get_raw (self):
|
||||
'''
|
||||
|
@ -338,65 +389,71 @@ class Sensor:
|
|||
'''
|
||||
return (self.sensor_dict)
|
||||
|
||||
def get_mac (self):
|
||||
'''
|
||||
Returns the sensor's model, if any, None either
|
||||
'''
|
||||
return (self.sensor_dict [self.sensors_mac_field])
|
||||
|
||||
def get_type (self):
|
||||
'''
|
||||
Returns the sensor's type
|
||||
'''
|
||||
return (self.sensor_dict [self.type_field])
|
||||
return (self.sensor_dict [self.sensors_type_field])
|
||||
|
||||
def get_model (self):
|
||||
'''
|
||||
Returns the sensor's model, if any, None either
|
||||
'''
|
||||
return (self.sensor_dict [self.model_field])
|
||||
return (self.sensor_dict [self.sensors_model_field])
|
||||
|
||||
def get_version (self):
|
||||
'''
|
||||
Returns the sensor's version
|
||||
'''
|
||||
return (self.sensor_dict [self.version_field])
|
||||
return (self.sensor_dict [self.sensors_version_field])
|
||||
|
||||
def get_name (self):
|
||||
'''
|
||||
Returns the sensor's name
|
||||
'''
|
||||
return (self.sensor_dict [self.name_field])
|
||||
return (self.sensor_dict [self.sensors_name_field])
|
||||
|
||||
def get_longname (self):
|
||||
'''
|
||||
Returns the sensor's type name in system's language and the sensor's name
|
||||
'''
|
||||
return (self.sensor_dict [self.longname_field])
|
||||
return (self.sensor_dict [self.sensors_longname_field])
|
||||
|
||||
def get_namegender (self):
|
||||
'''
|
||||
Return M for male and F for female.
|
||||
Only usefull for languages with gender on nouns
|
||||
'''
|
||||
return (self.sensor_dict [self.namegender_field])
|
||||
return (self.sensor_dict [self.sensors_namegender_field])
|
||||
|
||||
def get_batterylevel (self):
|
||||
'''
|
||||
Returns the sensor's battery level, out of 10
|
||||
It seems that batteryless sensors return 255
|
||||
'''
|
||||
return (int (self.sensor_dict [self.batterylevel_field]))
|
||||
return (int (self.sensor_dict [self.sensors_batterylevel_field]))
|
||||
|
||||
def get_signal (self):
|
||||
'''
|
||||
Returns the sensor's signal quality, out of 10
|
||||
'''
|
||||
return (int (self.sensor_dict [self.signal_field]))
|
||||
return (int (self.sensor_dict [self.sensors_signal_field]))
|
||||
|
||||
def get_lasttrigger (self):
|
||||
'''
|
||||
Return the timestamp of the sensor's last triger
|
||||
The sensors always trigger, even when the alarm's mode is off
|
||||
'''
|
||||
a = self.sensor_dict [self.lasttrigger_field]
|
||||
a = self.sensor_dict [self.sensors_lasttrigger_field]
|
||||
# Try because camera return the date '0000-00-00 00:00:00' that is ununderstandable
|
||||
try:
|
||||
b = datetime.strptime (a, self.lasttrigger_dateformat)
|
||||
b = datetime.strptime (a, self.sensors_lasttrigger_dateformat)
|
||||
except ValueError:
|
||||
return (0)
|
||||
r = int (b.timestamp ())
|
||||
|
@ -406,4 +463,23 @@ class Sensor:
|
|||
'''
|
||||
Returns True is the sensor is OK, False either
|
||||
'''
|
||||
return (self.sensor_dict [self.status_field] == self.status_value_ok)
|
||||
return (self.sensor_dict [self.sensors_status_field] == self.sensors_status_value_ok)
|
||||
|
||||
class Camera (Sensor):
|
||||
'''
|
||||
Class used to manipulate easily cameras
|
||||
'''
|
||||
def __init__ (self, sensor_dict, opener):
|
||||
'''
|
||||
Initialize the class with the dict producted by HomeSFR.get_camera ()
|
||||
'''
|
||||
Sensor.__init__ (self, sensor_dict, opener)
|
||||
|
||||
def get_snapshot (self):
|
||||
'''
|
||||
Get a snapshot from the camera
|
||||
Return a PIL.Image object
|
||||
'''
|
||||
r = self.base_url + self.camera_snapshot + '?' + self.camera_snapshot_mac + '=' + self.get_mac ()
|
||||
a = self.bytes2image (self.opener.open (r).read ())
|
||||
return (a)
|
Loading…
Reference in New Issue