3
3
# Multiple contributors : see https://github.com/philippelt/netatmo-api-python
4
4
# License : GPL V3
5
5
"""
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
7
7
This package can be used with Python2 or Python3 applications and do not
8
8
require anything else than standard libraries
9
9
121
121
'NAModule2' : ["wind unit" , 'Weather' ],
122
122
'NAModule3' : ["rain unit" , 'Weather' ],
123
123
'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
126
126
'NATherm1' : ["thermostat" , 'Energy' ],
127
127
'NCO' : ["co2 sensor" , 'Home + Security' ], # The same API as smoke sensor
128
128
'NDB' : ["doorbell" , 'Home + Security' ],
131
131
'NSD' : ["smoke sensor" , 'Home + Security' ],
132
132
'NHC' : ["home coach" , 'Aircare' ],
133
133
'NIS' : ["indoor sirene" , 'Home + Security' ],
134
+ 'NDL' : ["Doorlock" , 'Home + Security' ],
134
135
135
136
'NLC' : ["Cable Outlet" , 'Home+Control' ],
136
137
'NLE' : ["Ecometer" , 'Home+Control' ],
@@ -231,9 +232,9 @@ def __init__(self, clientId=None,
231
232
232
233
self ._clientId = clientId or cred ["CLIENT_ID" ]
233
234
self ._clientSecret = clientSecret or cred ["CLIENT_SECRET" ]
235
+ self ._accessToken = None #accessToken or cred["ACCESS_TOKEN"] # Will be refreshed before any use
234
236
self .refreshToken = refreshToken or cred ["REFRESH_TOKEN" ]
235
237
self .expiration = 0 # Force refresh token
236
- self ._accessToken = None # Will be refreshed before any use
237
238
238
239
@property
239
240
def accessToken (self ):
@@ -341,8 +342,8 @@ def getModuleParam(self, module_id, param):
341
342
342
343
class ThermostatData :
343
344
"""
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
346
347
Args:
347
348
authData (clientAuth): Authentication information with a working access Token
348
349
home : Home name or id of the home who's thermostat belongs to
@@ -375,36 +376,72 @@ def __init__(self, authData, home=None):
375
376
# Standard the first Relaystation and Thermostat is returned
376
377
# self.rawData is list all stations
377
378
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 = "" ):
381
381
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())
385
385
#print (Relay['_id'])
386
386
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'])
387
388
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'])
393
399
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
398
419
399
420
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
402
434
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
408
445
409
446
410
447
class WeatherStationData :
@@ -595,31 +632,48 @@ class HomeData:
595
632
596
633
Args:
597
634
authData (ClientAuth): Authentication information with a working access Token
635
+ home : Home name of the home where's devices are installed
598
636
"""
599
637
def __init__ (self , authData , home = None ):
638
+ warnings .warn ("The 'HomeData' class is deprecated'" ,
639
+ DeprecationWarning )
600
640
self .getAuthToken = authData .accessToken
601
641
postParams = {
602
642
"access_token" : self .getAuthToken
603
643
}
604
644
resp = postRequest ("Home data" , _GETHOMEDATA_REQ , postParams )
605
645
self .rawData = resp ['body' ]
606
646
# 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)
621
675
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' ]
623
677
# Split homes data by category
624
678
self .persons = {}
625
679
self .events = {}
@@ -644,6 +698,7 @@ def __init__(self, authData, home=None):
644
698
c ["home_id" ] = curHome ['id' ]
645
699
for camera ,e in self .events .items ():
646
700
self .lastEvent [camera ] = e [sorted (e )[- 1 ]]
701
+ #self.default_home has no key homeId use homeName instead!
647
702
if not self .cameras [self .default_home ] : raise NoDevice ("No camera available in default home" )
648
703
self .default_camera = list (self .cameras [self .default_home ].values ())[0 ]
649
704
else :
@@ -921,6 +976,9 @@ def __init__(self, authData, home=None):
921
976
#print (h.keys())
922
977
if home in (h ["name" ], h ["id" ]):
923
978
self .Homes_Data = h
979
+ else :
980
+ self .Homes_Data = self .rawData [0 ]
981
+ self .homeid = self .Homes_Data ['id' ]
924
982
if not self .Homes_Data : raise NoDevice ("No Devices available" )
925
983
926
984
@@ -932,10 +990,10 @@ class HomeCoach:
932
990
authData (clientAuth): Authentication information with a working access Token
933
991
home : Home name or id of the home who's HomeCoach belongs to
934
992
"""
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
938
993
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
939
997
# warnings.warn("The HomeCoach code is not tested due to the lack of test environment.\n", RuntimeWarning )
940
998
# "As Netatmo is continuously breaking API compatibility, risk that current bindings are wrong is high.\n" \
941
999
# "Please report found issues (https://github.com/philippelt/netatmo-api-python/issues)"
@@ -949,34 +1007,39 @@ def __init__(self, authData, home=None):
949
1007
# homecoach data
950
1008
if not self .rawData : raise NoDevice ("No HomeCoach available" )
951
1009
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
959
1015
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
963
1022
964
- # FIXME: Exclusion of outdated info is not handled (exclude parameter unused)
965
1023
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
971
1034
972
- def checkNotUpdated (self , res , _id , delay = 3600 ):
1035
+ def checkNotUpdated (self , res , hid , delay = 3600 ):
973
1036
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' })
975
1038
return ret if ret else None
976
1039
977
- def checkUpdated (self , res , _id , delay = 3600 ):
1040
+ def checkUpdated (self , res , hid , delay = 3600 ):
978
1041
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' })
980
1043
return ret if ret else None
981
1044
982
1045
@@ -1099,23 +1162,38 @@ def getStationMinMaxTH(station=None, module=None, home=None):
1099
1162
1100
1163
try :
1101
1164
homes = HomeData (authorization )
1102
- homeid = homes .homeid
1103
1165
except NoDevice :
1104
1166
logger .warning ("No home available for testing" )
1105
1167
1106
1168
try :
1107
1169
thermostat = ThermostatData (authorization )
1108
1170
Default_relay = thermostat .Relay_Plug ()
1109
1171
Default_thermostat = thermostat .Thermostat_Data ()
1172
+ thermostat .getThermostat ()
1173
+ print (thermostat .moduleNamesList ())
1174
+ #print (thermostat.getModuleByName(name))
1110
1175
except NoDevice :
1111
1176
logger .warning ("No thermostat avaible for testing" )
1112
1177
1113
1178
try :
1179
+ print (' ' )
1180
+ logger .info ("Homes Data" )
1181
+ #homesdata = HomesData(authorization, homeid)
1114
1182
homesdata = HomesData (authorization )
1183
+ homeid = homesdata .homeid
1115
1184
except NoDevice :
1116
1185
logger .warning ("No HomesData avaible for testing" )
1117
1186
1118
1187
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" )
1119
1197
Homecoach = HomeCoach (authorization )
1120
1198
except NoDevice :
1121
1199
logger .warning ("No HomeCoach avaible for testing" )
0 commit comments