33# Multiple contributors : see https://github.com/philippelt/netatmo-api-python
44# License : GPL V3
55"""
6- This API provides access to the Netatmo weather station or/and the Welcome camera
6+ This API provides access to the Netatmo weather station or/and other installed devices
77This package can be used with Python2 or Python3 applications and do not
88require anything else than standard libraries
99
121121 'NAModule2' : ["wind unit" , 'Weather' ],
122122 'NAModule3' : ["rain unit" , 'Weather' ],
123123 'NAModule4' : ["indoor unit" , 'Weather' ],
124- 'NAPlug' : ["thermostat relais station" , 'Energy' ], # A smart thermostat exist of a thermostat and a Relais module
125- # The relais module is also the bridge for thermostat and Valves
124+ 'NAPlug' : ["thermostat relais station" , 'Energy' ], # A smart thermostat exist of a thermostat module and a Relay device
125+ # The relay device is also the bridge for thermostat and Valves
126126 'NATherm1' : ["thermostat" , 'Energy' ],
127127 'NCO' : ["co2 sensor" , 'Home + Security' ], # The same API as smoke sensor
128128 'NDB' : ["doorbell" , 'Home + Security' ],
131131 'NSD' : ["smoke sensor" , 'Home + Security' ],
132132 'NHC' : ["home coach" , 'Aircare' ],
133133 'NIS' : ["indoor sirene" , 'Home + Security' ],
134+ 'NDL' : ["Doorlock" , 'Home + Security' ],
134135
135136 'NLC' : ["Cable Outlet" , 'Home+Control' ],
136137 'NLE' : ["Ecometer" , 'Home+Control' ],
@@ -231,9 +232,9 @@ def __init__(self, clientId=None,
231232
232233 self ._clientId = clientId or cred ["CLIENT_ID" ]
233234 self ._clientSecret = clientSecret or cred ["CLIENT_SECRET" ]
235+ self ._accessToken = None #accessToken or cred["ACCESS_TOKEN"] # Will be refreshed before any use
234236 self .refreshToken = refreshToken or cred ["REFRESH_TOKEN" ]
235237 self .expiration = 0 # Force refresh token
236- self ._accessToken = None # Will be refreshed before any use
237238
238239 @property
239240 def accessToken (self ):
@@ -341,8 +342,8 @@ def getModuleParam(self, module_id, param):
341342
342343class ThermostatData :
343344 """
344- List the Thermostat and temperature modules
345-
345+ List the Relay station and Thermostat modules
346+ Valves are controlled by HomesData and HomeStatus in new API
346347 Args:
347348 authData (clientAuth): Authentication information with a working access Token
348349 home : Home name or id of the home who's thermostat belongs to
@@ -375,36 +376,72 @@ def __init__(self, authData, home=None):
375376 # Standard the first Relaystation and Thermostat is returned
376377 # self.rawData is list all stations
377378
378- # FIXME : This code is wrong as it will always return the first Relay
379- # I don't own a thermostat I can't fix this code, help welcome
380- def Relay_Plug (self , _id = None ):
379+ # if no ID is given the Relaystation at index 0 is returned
380+ def Relay_Plug (self , Rid = "" ):
381381 for Relay in self .rawData :
382- if _id in Relay :
383- return Relay
384- else :
382+ if Rid in Relay [ '_id' ] :
383+ print ( ' Relay ' , Rid , 'in rawData' )
384+ #print (Relay.keys())
385385 #print (Relay['_id'])
386386 return Relay
387+ #dict_keys(['_id', 'applications', 'cipher_id', 'command', 'config_version', 'd_amount', 'date_creation', 'dev_has_init', 'device_group', 'firmware', 'firmware_private', 'homekit_nb_pairing', 'last_bilan', 'last_day_extremum', 'last_fw_update', 'last_measure_stored', 'last_setup', 'last_status_store', 'last_sync_asked', 'last_time_boiler_on', 'mg_station_name', 'migration_date', 'module_history', 'netcom_transport', 'new_historic_data', 'place', 'plug_connected_boiler', 'recompute_outdoor_time', 'record_storage', 'rf_amb_status', 'setpoint_order_history', 'skip_module_history_creation', 'subtype', 'type', 'u_amount', 'update_device', 'upgrade_record_ts', 'wifi_status', 'room', 'modules', 'station_name', 'udp_conn', 'last_plug_seen'])
387388
388- # FIXME : Probably wrong again, always returning "first" thermostat ?
389- def Thermostat_Data (self ):
390- for thermostat in self .Relay_Plug ()['modules' ]:
391- #
392- return thermostat
389+ # if no ID is given the Thermostatmodule at index 0 is returned
390+ def Thermostat_Data (self , tid = "" ):
391+ for Relay in self .rawData :
392+ for thermostat in Relay ['modules' ]:
393+ if tid in thermostat ['_id' ]:
394+ print ('Thermostat ' ,tid , 'in Relay' , Relay ['_id' ], Relay ['station_name' ])
395+ #print (thermostat['_id'])
396+ #print (thermostat.keys())
397+ return thermostat
398+ #dict_keys(['_id', 'module_name', 'type', 'firmware', 'last_message', 'rf_status', 'battery_vp', 'therm_orientation', 'therm_relay_cmd', 'anticipating', 'battery_percent', 'event_history', 'last_therm_seen', 'setpoint', 'therm_program_list', 'measured'])
393399
394- def getThermostat (self , name = None , tid = None ):
395- if self .rawData [0 ]['station_name' ] != name : return None # OLD ['name']
396- # FIXME: No thermostat property !!
397- return self .thermostat [self .defaultThermostatId ]
400+
401+ def getThermostat (self , name = None , id = "" ):
402+ for Relay in self .rawData :
403+ for module in Relay ['modules' ]:
404+ if id == Relay ['_id' ]:
405+ print ('Relay ' , id , 'found' )
406+ return Relay
407+ elif name == Relay ['station_name' ]:
408+ print ('Relay ' , name , 'found' )
409+ return Relay
410+ elif id == module ['_id' ]:
411+ print ('Thermostat ' , id , 'found in Relay' , Relay ['_id' ], Relay ['station_name' ])
412+ return module
413+ elif name == module ['module_name' ]:
414+ print ('Thermostat ' , name , 'found in Relay' , Relay ['_id' ], Relay ['station_name' ])
415+ return module
416+ else :
417+ #print ('Device NOT Found')
418+ pass
398419
399420 def moduleNamesList (self , name = None , tid = None ):
400- thermostat = self .getThermostat (name = name , tid = tid )
401- return [m ['name' ] for m in thermostat ['modules' ]] if thermostat else None
421+ l = []
422+ for Relay in self .rawData :
423+ if id == Relay ['_id' ] or name == Relay ['station_name' ]:
424+ RL = []
425+ for module in Relay ['modules' ]:
426+ RL .append (module ['module_name' ])
427+ return RL
428+ else :
429+ #print ("Cloud Data")
430+ for module in Relay ['modules' ]:
431+ l .append (module ['module_name' ])
432+ #This return a list off all connected Thermostat in the cloud.
433+ return l
402434
403- def getModuleByName (self , name , thermostatId = None ): # ERROR 'NoneType' object is not subscriptable
404- thermostat = self .getThermostat (tid = thermostatId )
405- for m in thermostat ['modules' ]:
406- if m ['name' ] == name : return m
407- return None
435+ def getModuleByName (self , name , tid = "" ):
436+ for Relay in self .rawData :
437+ for module in Relay ['modules' ]:
438+ #print (module['module_name'], module['_id'])
439+ if module ['module_name' ] == name :
440+ return module
441+ elif module ['_id' ] == tid :
442+ return module
443+ else :
444+ pass
408445
409446
410447class WeatherStationData :
@@ -595,31 +632,48 @@ class HomeData:
595632
596633 Args:
597634 authData (ClientAuth): Authentication information with a working access Token
635+ home : Home name of the home where's devices are installed
598636 """
599637 def __init__ (self , authData , home = None ):
638+ warnings .warn ("The 'HomeData' class is deprecated'" ,
639+ DeprecationWarning )
600640 self .getAuthToken = authData .accessToken
601641 postParams = {
602642 "access_token" : self .getAuthToken
603643 }
604644 resp = postRequest ("Home data" , _GETHOMEDATA_REQ , postParams )
605645 self .rawData = resp ['body' ]
606646 # Collect homes
607- self .homes = { d ['id' ] : d for d in self .rawData ['homes' ] }
608- # FIXME : Doesn't use the home parameter to select the appropriate home !
609- for k , v in self .homes .items ():
610- self .homeid = k
611- C = v .get ('cameras' )
612- P = v .get ('persons' )
613- S = v .get ('smokedetectors' )
614- E = v .get ('events' )
615- S or logger .warning ('No smoke detector found' )
616- C or logger .warning ('No Cameras found' )
617- P or logger .warning ('No Persons found' )
618- E or logger .warning ('No events found' )
619- if not (C or P or S or E ):
620- raise NoDevice ("No device found in home %s" % k )
647+ self .homes = self .rawData ['homes' ][0 ]
648+ for d in self .rawData ['homes' ] :
649+ if home == d ['name' ]:
650+ self .homes = d
651+ else :
652+ pass
653+ #
654+ #print (self.homes.keys())
655+ #dict_keys(['id', 'name', 'persons', 'place', 'cameras', 'smokedetectors', 'events'])
656+ self .homeid = self .homes ['id' ]
657+ C = self .homes ['cameras' ]
658+ P = self .homes ['persons' ]
659+ S = self .homes ['smokedetectors' ]
660+ E = None
661+ # events not always in self.homes
662+ if 'events' in self .homes .keys ():
663+ E = self .homes ['events' ]
664+ #
665+ if not S :
666+ logger .warning ('No smoke detector found' )
667+ if not C :
668+ logger .warning ('No Cameras found' )
669+ if not P :
670+ logger .warning ('No Persons found' )
671+ if not E :
672+ logger .warning ('No events found' )
673+ # if not (C or P or S or E):
674+ # raise NoDevice("No device found in home %s" % k)
621675 if S or C or P or E :
622- self .default_home = home or list ( self .homes . values ())[ 0 ] ['name' ]
676+ self .default_home = home or self .homes ['name' ]
623677 # Split homes data by category
624678 self .persons = {}
625679 self .events = {}
@@ -644,6 +698,7 @@ def __init__(self, authData, home=None):
644698 c ["home_id" ] = curHome ['id' ]
645699 for camera ,e in self .events .items ():
646700 self .lastEvent [camera ] = e [sorted (e )[- 1 ]]
701+ #self.default_home has no key homeId use homeName instead!
647702 if not self .cameras [self .default_home ] : raise NoDevice ("No camera available in default home" )
648703 self .default_camera = list (self .cameras [self .default_home ].values ())[0 ]
649704 else :
@@ -921,6 +976,9 @@ def __init__(self, authData, home=None):
921976 #print (h.keys())
922977 if home in (h ["name" ], h ["id" ]):
923978 self .Homes_Data = h
979+ else :
980+ self .Homes_Data = self .rawData [0 ]
981+ self .homeid = self .Homes_Data ['id' ]
924982 if not self .Homes_Data : raise NoDevice ("No Devices available" )
925983
926984
@@ -932,10 +990,10 @@ class HomeCoach:
932990 authData (clientAuth): Authentication information with a working access Token
933991 home : Home name or id of the home who's HomeCoach belongs to
934992 """
935- # FIXME: home parameter not used, unpredictible behavior
936- def __init__ (self , authData , home = None ):
937- # I don't own a HomeCoach thus I am not able to test the HomeCoach support
938993
994+ def __init__ (self , authData ):
995+ # I don't own a HomeCoach thus I am not able to test the HomeCoach support
996+ # Homecoach does not need or use HomeID parameter
939997# warnings.warn("The HomeCoach code is not tested due to the lack of test environment.\n", RuntimeWarning )
940998# "As Netatmo is continuously breaking API compatibility, risk that current bindings are wrong is high.\n" \
941999# "Please report found issues (https://github.com/philippelt/netatmo-api-python/issues)"
@@ -949,34 +1007,39 @@ def __init__(self, authData, home=None):
9491007 # homecoach data
9501008 if not self .rawData : raise NoDevice ("No HomeCoach available" )
9511009
952- for h in self .rawData :
953- # FIXME: This loop is nonsense (always end with the last value)
954- self .HomecoachDevice = h
955- # print ('Homecoach = ', self.HomecoachDevice)
956- # print (' ')
957- # print ('Homecoach_data = ', self.rawData[i]['dashboard_data'])
958- # print (' ')
1010+ def HomecoachDevice (self , hid = "" ):
1011+ for device in self .rawData :
1012+ if hid == device ['_id' ]:
1013+ return device
1014+ return None
9591015
960- def Dashboard (self ):
961- D = self .HomecoachDevice ['dashboard_data' ]
962- return D
1016+ def Dashboard (self , hid = "" ):
1017+ #D = self.HomecoachDevice['dashboard_data']
1018+ for device in self .rawData :
1019+ if hid == device ['_id' ]:
1020+ D = device ['dashboard_data' ]
1021+ return D
9631022
964- # FIXME: Exclusion of outdated info is not handled (exclude parameter unused)
9651023 def lastData (self , hid = None , exclude = 0 ):
966- if hid is not None :
967- s = self .HomecoachDevice ['dashboard_data' ]['time_utc' ]
968- _id = self .HomecoachDevice [hid ]
969- return {'When' :s }, {'_id' :_id }
970- return {'When' : 0 }, {'_id' : hid }
1024+ for device in self .rawData :
1025+ if hid == device ['_id' ]:
1026+ # LastData in HomeCoach
1027+ #s = self.HomecoachDevice['dashboard_data']['time_utc']
1028+ # Define oldest acceptable sensor measure event
1029+ limit = (time .time () - exclude ) if exclude else 0
1030+ ds = device ['dashboard_data' ]['time_utc' ]
1031+ return { '_id' : hid , 'When' : ds if device .get ('time_utc' ,limit + 10 ) > limit else 0 }
1032+ else :
1033+ pass
9711034
972- def checkNotUpdated (self , res , _id , delay = 3600 ):
1035+ def checkNotUpdated (self , res , hid , delay = 3600 ):
9731036 ret = []
974- if time .time ()- res ['When' ] > delay : ret .append ({_id : 'Device Not Updated' })
1037+ if time .time ()- res ['When' ] > delay : ret .append ({hid : 'Device Not Updated' })
9751038 return ret if ret else None
9761039
977- def checkUpdated (self , res , _id , delay = 3600 ):
1040+ def checkUpdated (self , res , hid , delay = 3600 ):
9781041 ret = []
979- if time .time ()- res ['When' ] < delay : ret .append ({_id : 'Device up-to-date' })
1042+ if time .time ()- res ['When' ] < delay : ret .append ({hid : 'Device up-to-date' })
9801043 return ret if ret else None
9811044
9821045
@@ -1099,23 +1162,38 @@ def getStationMinMaxTH(station=None, module=None, home=None):
10991162
11001163 try :
11011164 homes = HomeData (authorization )
1102- homeid = homes .homeid
11031165 except NoDevice :
11041166 logger .warning ("No home available for testing" )
11051167
11061168 try :
11071169 thermostat = ThermostatData (authorization )
11081170 Default_relay = thermostat .Relay_Plug ()
11091171 Default_thermostat = thermostat .Thermostat_Data ()
1172+ thermostat .getThermostat ()
1173+ print (thermostat .moduleNamesList ())
1174+ #print (thermostat.getModuleByName(name))
11101175 except NoDevice :
11111176 logger .warning ("No thermostat avaible for testing" )
11121177
11131178 try :
1179+ print (' ' )
1180+ logger .info ("Homes Data" )
1181+ #homesdata = HomesData(authorization, homeid)
11141182 homesdata = HomesData (authorization )
1183+ homeid = homesdata .homeid
11151184 except NoDevice :
11161185 logger .warning ("No HomesData avaible for testing" )
11171186
11181187 try :
1188+ print (' ' )
1189+ logger .info ("Home Status" )
1190+ HomeStatus (authorization , homeid )
1191+ except NoDevice :
1192+ logger .warning ("No Home available for testing" )
1193+
1194+ try :
1195+ print (' ' )
1196+ logger .info ("HomeCoach" )
11191197 Homecoach = HomeCoach (authorization )
11201198 except NoDevice :
11211199 logger .warning ("No HomeCoach avaible for testing" )
0 commit comments