diff --git a/fixtures/getmeasure_12_34_56_00_00_a1_4c_da.json b/fixtures/getmeasure_sum_energy_buy_from_grid$0_12_34_56_00_00_a1_4c_da.json similarity index 100% rename from fixtures/getmeasure_12_34_56_00_00_a1_4c_da.json rename to fixtures/getmeasure_sum_energy_buy_from_grid$0_12_34_56_00_00_a1_4c_da.json diff --git a/fixtures/getmeasure_sum_energy_buy_from_grid$1_98_76_54_32_10_00_00_49.json b/fixtures/getmeasure_sum_energy_buy_from_grid$1_98_76_54_32_10_00_00_49.json new file mode 100644 index 00000000..0f60f8f8 --- /dev/null +++ b/fixtures/getmeasure_sum_energy_buy_from_grid$1_98_76_54_32_10_00_00_49.json @@ -0,0 +1 @@ +{"body":[{"beg_time":1710459891,"step_time":1800,"value":[[0],[0]]},{"beg_time":1710465291,"step_time":1800,"value":[[0],[222],[328],[333],[7],[0],[0],[0],[0],[0],[0],[0],[0],[0]]}],"status":"ok","time_exec":0.08218002319335938,"time_server":1710510905} \ No newline at end of file diff --git a/fixtures/getmeasure_sum_energy_buy_from_grid$1_98_76_54_32_10_00_00_73.json b/fixtures/getmeasure_sum_energy_buy_from_grid$1_98_76_54_32_10_00_00_73.json new file mode 100644 index 00000000..c1d788b2 --- /dev/null +++ b/fixtures/getmeasure_sum_energy_buy_from_grid$1_98_76_54_32_10_00_00_73.json @@ -0,0 +1,286 @@ +{ + "body": [ + { + "beg_time": 1709421900, + "step_time": 1800, + "value": [ + [ + 0 + ], + [ + 0 + ], + [ + 0 + ], + [ + 0 + ] + ] + }, + { + "beg_time": 1709459700, + "step_time": 1800, + "value": [ + [ + 526 + ], + [ + 1194 + ], + [ + 1195 + ], + [ + 1176 + ], + [ + 1177 + ], + [ + 1164 + ], + [ + 1524 + ], + [ + 416 + ], + [ + 659 + ], + [ + 743 + ], + [ + 1033 + ], + [ + 1052 + ], + [ + 964 + ], + [ + 985 + ], + [ + 1031 + ], + [ + 924 + ], + [ + 73 + ], + [ + 156 + ], + [ + 320 + ], + [ + 268 + ], + [ + 0 + ], + [ + 0 + ], + [ + 0 + ], + [ + 0 + ], + [ + 0 + ], + [ + 768 + ], + [ + 912 + ], + [ + 889 + ], + [ + 399 + ], + [ + 0 + ], + [ + 0 + ], + [ + 0 + ], + [ + 0 + ], + [ + 0 + ], + [ + 0 + ], + [ + 38 + ], + [ + 38 + ], + [ + 0 + ], + [ + 218 + ], + [ + 186 + ], + [ + 250 + ], + [ + 363 + ], + [ + 479 + ], + [ + 579 + ], + [ + 680 + ], + [ + 832 + ], + [ + 872 + ], + [ + 699 + ], + [ + 37 + ], + [ + 77 + ], + [ + 248 + ], + [ + 209 + ], + [ + 0 + ], + [ + 0 + ], + [ + 0 + ], + [ + 0 + ], + [ + 0 + ], + [ + 418 + ], + [ + 690 + ], + [ + 714 + ], + [ + 327 + ], + [ + 0 + ], + [ + 0 + ], + [ + 0 + ], + [ + 0 + ], + [ + 0 + ], + [ + 0 + ], + [ + 0 + ], + [ + 0 + ], + [ + 0 + ], + [ + 173 + ], + [ + 0 + ], + [ + 0 + ], + [ + 0 + ], + [ + 91 + ], + [ + 212 + ], + [ + 199 + ], + [ + 161 + ], + [ + 291 + ], + [ + 394 + ], + [ + 617 + ], + [ + 688 + ], + [ + 526 + ], + [ + 428 + ], + [ + 0 + ] + ] + } + ], + "status": "ok", + "time_exec": 0.10495901107788086, + "time_server": 1709768260 +} \ No newline at end of file diff --git a/fixtures/getmeasure_sum_energy_buy_from_grid$2_98_76_54_32_10_00_00_49.json b/fixtures/getmeasure_sum_energy_buy_from_grid$2_98_76_54_32_10_00_00_49.json new file mode 100644 index 00000000..03784fcd --- /dev/null +++ b/fixtures/getmeasure_sum_energy_buy_from_grid$2_98_76_54_32_10_00_00_49.json @@ -0,0 +1 @@ +{"body":[{"beg_time":1710465291,"step_time":1800,"value":[[0],[0],[0],[0],[0],[0],[0],[0],[332],[448]]}],"status":"ok","time_exec":0.02698206901550293,"time_server":1710511044} \ No newline at end of file diff --git a/fixtures/getmeasure_sum_energy_buy_from_grid$2_98_76_54_32_10_00_00_73.json b/fixtures/getmeasure_sum_energy_buy_from_grid$2_98_76_54_32_10_00_00_73.json new file mode 100644 index 00000000..10f8884a --- /dev/null +++ b/fixtures/getmeasure_sum_energy_buy_from_grid$2_98_76_54_32_10_00_00_73.json @@ -0,0 +1,154 @@ +{ + "body": [ + { + "beg_time": 1709421900, + "step_time": 1800, + "value": [ + [ + 0 + ], + [ + 0 + ], + [ + 0 + ], + [ + 0 + ], + [ + 0 + ], + [ + 0 + ], + [ + 0 + ] + ] + }, + { + "beg_time": 1709459700, + "step_time": 1800, + "value": [ + [ + 728 + ], + [ + 1196 + ], + [ + 1197 + ], + [ + 1206 + ], + [ + 1167 + ], + [ + 887 + ], + [ + 0 + ], + [ + 0 + ], + [ + 0 + ], + [ + 0 + ], + [ + 0 + ], + [ + 0 + ], + [ + 0 + ], + [ + 0 + ], + [ + 200 + ], + [ + 1532 + ], + [ + 165 + ], + [ + 252 + ], + [ + 282 + ], + [ + 195 + ], + [ + 307 + ], + [ + 396 + ], + [ + 0 + ], + [ + 0 + ], + [ + 0 + ], + [ + 0 + ], + [ + 0 + ], + [ + 0 + ], + [ + 0 + ], + [ + 0 + ], + [ + 201 + ], + [ + 1308 + ], + [ + 0 + ], + [ + 0 + ], + [ + 0 + ], + [ + 0 + ], + [ + 0 + ], + [ + 0 + ] + ] + } + ], + "status": "ok", + "time_exec": 0.13054180145263672, + "time_server": 1709768401 +} \ No newline at end of file diff --git a/fixtures/home_multi_status_error_disconnected.json b/fixtures/home_multi_status_error_disconnected.json new file mode 100644 index 00000000..f94595bf --- /dev/null +++ b/fixtures/home_multi_status_error_disconnected.json @@ -0,0 +1,15 @@ +{ + "status":"ok", + "body":{ + "errors":[ + { + "code":6, + "id":"aa:aa:aa:aa:aa:aa" + } + ], + "home":{ + "id":"aaaaaaaaaaabbbbbbbbbbccc" + } + }, + "time_server":1559292039 +} \ No newline at end of file diff --git a/fixtures/homesdata_multi.json b/fixtures/homesdata_multi.json new file mode 100644 index 00000000..7a686ed0 --- /dev/null +++ b/fixtures/homesdata_multi.json @@ -0,0 +1,6442 @@ +{ + "body": { + "homes": [ + { + "id": "aaaaaaaaaaabbbbbbbbbbccc", + "name": "A BIG HOME", + "altitude": 284, + "coordinates": [ + 26.234678, + 83.234678 + ], + "country": "FR", + "timezone": "Europe/Paris", + "rooms": [ + { + "id": "3707962039", + "name": "Cuisine", + "type": "kitchen", + "module_ids": [ + "98:76:54:32:10:00:00:03", + "98:76:54:32:10:00:00:05", + "98:76:54:32:10:00:00:06", + "98:76:54:32:10:00:00:07", + "98:76:54:32:10:00:00:09", + "98:76:54:32:10:00:00:28", + "98:76:54:32:10:00:00:37", + "98:76:54:32:10:00:00:38", + "98:76:54:32:10:00:00:40", + "98:76:54:32:10:00:00:50" + ] + }, + { + "id": "596817675", + "name": "Vestibule", + "type": "lobby", + "module_ids": [ + "98:76:54:32:10:00:00:01", + "98:76:54:32:10:00:00:02", + "98:76:54:32:10:00:00:08", + "98:76:54:32:10:00:00:13", + "98:76:54:32:10:00:00:14", + "98:76:54:32:10:00:00:15", + "98:76:54:32:10:00:00:16", + "98:76:54:32:10:00:00:27", + "98:76:54:32:10:00:00:29", + "98:76:54:32:10:00:00:32", + "98:76:54:32:10:00:00:36", + "98:76:54:32:10:00:00:39" + ] + }, + { + "id": "1462100035", + "name": "Salon", + "type": "livingroom", + "module_ids": [ + "98:76:54:32:10:00:00:04", + "98:76:54:32:10:00:00:24#1", + "98:76:54:32:10:00:00:34", + "98:76:54:32:10:00:00:52", + "98:76:54:32:10:00:00:60", + "98:76:54:32:10:00:00:63", + "98:76:54:32:10:00:00:66", + "98:76:54:32:10:00:00:73", + "98:76:54:32:10:00:00:75" + ] + }, + { + "id": "3435163850", + "name": "Chambre A", + "type": "bedroom", + "module_ids": [ + "98:76:54:32:10:00:00:26", + "98:76:54:32:10:00:00:51" + ] + }, + { + "id": "737850817", + "name": "Extérieur", + "type": "outdoor", + "module_ids": [ + "98:76:54:32:10:00:00:10", + "98:76:54:32:10:00:00:41", + "98:76:54:32:10:00:00:42", + "98:76:54:32:10:00:00:53", + "98:76:54:32:10:00:00:54", + "98:76:54:32:10:00:00:55", + "98:76:54:32:10:00:00:56" + ] + }, + { + "id": "842662884", + "name": "Bibliothèque", + "type": "home_office", + "module_ids": [ + "98:76:54:32:10:00:00:11", + "98:76:54:32:10:00:00:12", + "98:76:54:32:10:00:00:24#2", + "98:76:54:32:10:00:00:25" + ] + }, + { + "id": "3194154910", + "name": "Salle à manger", + "type": "dining_room", + "module_ids": [ + "98:76:54:32:10:00:00:30", + "98:76:54:32:10:00:00:31", + "98:76:54:32:10:00:00:33", + "98:76:54:32:10:00:00:72" + ] + }, + { + "id": "2370728183", + "name": "Atelier", + "type": "custom", + "module_ids": [ + "98:76:54:32:10:00:00:18", + "98:76:54:32:10:00:00:23" + ] + }, + { + "id": "2042969726", + "name": "Chambre B", + "type": "bedroom", + "module_ids": [ + "98:76:54:32:10:00:00:19", + "98:76:54:32:10:00:00:20", + "98:76:54:32:10:00:00:46" + ] + }, + { + "id": "2754296835", + "name": "Placard Technique", + "type": "electrical_cabinet", + "module_ids": [ + "aa:aa:aa:aa:aa:aa", + "98:76:54:32:10:00:00:22", + "98:76:54:32:10:00:00:48", + "98:76:54:32:10:00:00:59", + "98:76:54:32:10:00:00:67", + "98:76:54:32:10:00:00:68", + "98:76:54:32:10:00:00:69", + "98:76:54:32:10:00:00:77", + "98:76:54:32:10:00:00:78", + "98:76:54:32:10:00:00:79" + ] + }, + { + "id": "1662974901", + "name": "Dressing", + "type": "custom", + "module_ids": [ + "98:76:54:32:10:00:00:21", + "98:76:54:32:10:00:00:35" + ] + }, + { + "id": "873035982", + "name": "Chambre C", + "type": "bedroom", + "module_ids": [ + "98:76:54:32:10:00:00:17", + "98:76:54:32:10:00:00:43", + "98:76:54:32:10:00:00:76" + ] + }, + { + "id": "3795659199", + "name": "Chambre D", + "type": "bedroom", + "module_ids": [ + "98:76:54:32:10:00:00:57" + ] + }, + { + "id": "2102454491", + "name": "Buanderie", + "type": "custom", + "module_ids": [ + "98:76:54:32:10:00:00:45" + ] + }, + { + "id": "93888250", + "name": "Salle de bains Des Enfants", + "type": "bathroom", + "module_ids": [ + "98:76:54:32:10:00:00:44" + ] + }, + { + "id": "3497055021", + "name": "Chambre Brice", + "type": "bedroom", + "module_ids": [ + "98:76:54:32:10:00:00:47", + "98:76:54:32:10:00:00:74" + ] + }, + { + "id": "2061006239", + "name": "Pool house", + "type": "custom", + "module_ids": [ + "98:76:54:32:10:00:00:58", + "98:76:54:32:10:00:00:61", + "98:76:54:32:10:00:00:62", + "98:76:54:32:10:00:00:64", + "98:76:54:32:10:00:00:65", + "98:76:54:32:10:00:00:71" + ] + }, + { + "id": "927970817", + "name": "Salle de bains Des Parents", + "type": "bathroom", + "module_ids": [ + "98:76:54:32:10:00:00:49" + ] + }, + { + "id": "1641945290", + "name": "Salle de bains D’Arthur", + "type": "bathroom", + "module_ids": [ + "98:76:54:32:10:00:00:70" + ] + } + ], + "modules": [ + { + "id": "aa:aa:aa:aa:aa:aa", + "type": "NLG", + "name": "Legrand Gateway", + "setup_date": 1572624665, + "room_id": "2754296835", + "modules_bridged": [ + "98:76:54:32:10:00:00:01", + "98:76:54:32:10:00:00:02", + "98:76:54:32:10:00:00:03", + "98:76:54:32:10:00:00:04", + "98:76:54:32:10:00:00:05", + "98:76:54:32:10:00:00:06", + "98:76:54:32:10:00:00:07", + "98:76:54:32:10:00:00:08", + "98:76:54:32:10:00:00:09", + "98:76:54:32:10:00:00:10", + "98:76:54:32:10:00:00:11", + "98:76:54:32:10:00:00:12", + "98:76:54:32:10:00:00:13", + "98:76:54:32:10:00:00:14", + "98:76:54:32:10:00:00:15", + "98:76:54:32:10:00:00:16", + "98:76:54:32:10:00:00:17", + "98:76:54:32:10:00:00:18", + "98:76:54:32:10:00:00:19", + "98:76:54:32:10:00:00:20", + "98:76:54:32:10:00:00:21", + "98:76:54:32:10:00:00:22", + "98:76:54:32:10:00:00:23", + "98:76:54:32:10:00:00:24", + "98:76:54:32:10:00:00:24#1", + "98:76:54:32:10:00:00:24#2", + "98:76:54:32:10:00:00:25", + "98:76:54:32:10:00:00:26", + "98:76:54:32:10:00:00:27", + "98:76:54:32:10:00:00:28", + "98:76:54:32:10:00:00:29", + "98:76:54:32:10:00:00:30", + "98:76:54:32:10:00:00:31", + "98:76:54:32:10:00:00:32", + "98:76:54:32:10:00:00:33", + "98:76:54:32:10:00:00:34", + "98:76:54:32:10:00:00:35", + "98:76:54:32:10:00:00:36", + "98:76:54:32:10:00:00:37", + "98:76:54:32:10:00:00:38", + "98:76:54:32:10:00:00:39", + "98:76:54:32:10:00:00:40", + "98:76:54:32:10:00:00:41", + "98:76:54:32:10:00:00:42", + "98:76:54:32:10:00:00:43", + "98:76:54:32:10:00:00:44", + "98:76:54:32:10:00:00:45", + "98:76:54:32:10:00:00:46", + "98:76:54:32:10:00:00:47", + "98:76:54:32:10:00:00:48", + "98:76:54:32:10:00:00:49", + "98:76:54:32:10:00:00:50", + "98:76:54:32:10:00:00:51", + "98:76:54:32:10:00:00:52", + "98:76:54:32:10:00:00:53", + "98:76:54:32:10:00:00:54", + "98:76:54:32:10:00:00:55", + "98:76:54:32:10:00:00:03#1", + "98:76:54:32:10:00:00:03#2", + "98:76:54:32:10:00:00:07#1", + "98:76:54:32:10:00:00:07#2", + "98:76:54:32:10:00:00:08#1", + "98:76:54:32:10:00:00:08#2", + "98:76:54:32:10:00:00:28#1", + "98:76:54:32:10:00:00:28#2", + "98:76:54:32:10:00:00:29#1", + "98:76:54:32:10:00:00:29#2", + "98:76:54:32:10:00:00:30#1", + "98:76:54:32:10:00:00:30#2", + "98:76:54:32:10:00:00:50#1", + "98:76:54:32:10:00:00:50#2", + "98:76:54:32:10:00:00:56", + "98:76:54:32:10:00:00:57", + "98:76:54:32:10:00:00:58", + "98:76:54:32:10:00:00:59", + "98:76:54:32:10:00:00:60", + "98:76:54:32:10:00:00:61", + "98:76:54:32:10:00:00:62", + "98:76:54:32:10:00:00:63", + "98:76:54:32:10:00:00:63#1", + "98:76:54:32:10:00:00:63#2", + "98:76:54:32:10:00:00:64", + "98:76:54:32:10:00:00:64#1", + "98:76:54:32:10:00:00:64#2", + "98:76:54:32:10:00:00:65", + "98:76:54:32:10:00:00:65#1", + "98:76:54:32:10:00:00:65#2", + "98:76:54:32:10:00:00:66", + "98:76:54:32:10:00:00:66#1", + "98:76:54:32:10:00:00:66#2", + "98:76:54:32:10:00:00:67", + "98:76:54:32:10:00:00:68", + "98:76:54:32:10:00:00:69", + "98:76:54:32:10:00:00:70", + "98:76:54:32:10:00:00:71", + "98:76:54:32:10:00:00:72", + "98:76:54:32:10:00:00:73", + "98:76:54:32:10:00:00:74", + "98:76:54:32:10:00:00:75", + "98:76:54:32:10:00:00:76", + "98:76:54:32:10:00:00:77", + "98:76:54:32:10:00:00:78", + "98:76:54:32:10:00:00:79" + ] + }, + { + "id": "98:76:54:32:10:00:00:01", + "type": "NLP", + "name": "Buffet", + "setup_date": 1572624686, + "room_id": "596817675", + "bridge": "aa:aa:aa:aa:aa:aa", + "appliance_type": "light" + }, + { + "id": "98:76:54:32:10:00:00:02", + "type": "NLT", + "name": "Commande entrée sortie principale", + "setup_date": 1572624686, + "room_id": "596817675", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:03", + "type": "NLD", + "name": "Boutons Cuisine Haut", + "setup_date": 1572629067, + "room_id": "3707962039", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:04", + "type": "NLV", + "name": "volet", + "setup_date": 1572798965, + "room_id": "1462100035", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:05", + "type": "NLM", + "name": "Verrière", + "setup_date": 1574591975, + "room_id": "3707962039", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:06", + "type": "NLM", + "name": "ilot", + "setup_date": 1574591975, + "room_id": "3707962039", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:07", + "type": "NLD", + "name": "Bouton Cuisine milieu", + "setup_date": 1574592863, + "room_id": "3707962039", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:08", + "type": "NLD", + "name": "Entrée Bouton Portail et dehors", + "setup_date": 1574593140, + "room_id": "596817675", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:09", + "type": "NLT", + "name": "Bouton pour exterieur porte fenetre", + "setup_date": 1605358560, + "room_id": "3707962039", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:10", + "type": "NLM", + "name": "Olivier", + "setup_date": 1605359274, + "room_id": "737850817", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:11", + "type": "NLP", + "name": "Lampadaire", + "setup_date": 1605369621, + "room_id": "842662884", + "bridge": "aa:aa:aa:aa:aa:aa", + "appliance_type": "light" + }, + { + "id": "98:76:54:32:10:00:00:12", + "type": "NLT", + "name": "Bouton Bibliotheque", + "setup_date": 1607175439, + "room_id": "842662884", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:13", + "type": "NLF", + "name": "Couloir enfants", + "setup_date": 1612299365, + "room_id": "596817675", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:14", + "type": "NLT", + "name": "Couloir enfants 2", + "setup_date": 1612299777, + "room_id": "596817675", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:15", + "type": "NLT", + "name": "Couloir enfants 3", + "setup_date": 1612299852, + "room_id": "596817675", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:16", + "type": "NLT", + "name": "Couloir enfants 1", + "setup_date": 1613565493, + "room_id": "596817675", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:17", + "type": "NLF", + "name": "Lumière C", + "setup_date": 1617390843, + "room_id": "873035982", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:18", + "type": "NLM", + "name": "Néon", + "setup_date": 1643544945, + "room_id": "2370728183", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:19", + "type": "NLT", + "name": "Couloir A interrupteur", + "setup_date": 1643794135, + "room_id": "2042969726", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:20", + "type": "NLF", + "name": "Couloir A", + "setup_date": 1643794453, + "room_id": "2042969726", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:21", + "type": "NLPT", + "name": "Dressing", + "setup_date": 1643809582, + "room_id": "1662974901", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:22", + "type": "NLPM", + "name": "Onduleur Serveurs Mais Local Technique", + "setup_date": 1643911516, + "room_id": "2754296835", + "bridge": "aa:aa:aa:aa:aa:aa", + "appliance_type": "other" + }, + { + "id": "98:76:54:32:10:00:00:23", + "type": "NLT", + "name": "Interrupteur neon", + "setup_date": 1644008400, + "room_id": "2370728183", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:24", + "type": "NLIS", + "setup_date": 1645651106, + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:24#1", + "type": "NLIS", + "name": "Niche", + "setup_date": 1645651106, + "room_id": "1462100035", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:24#2", + "type": "NLIS", + "name": "Escalier", + "setup_date": 1645651106, + "room_id": "842662884", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:25", + "type": "NLT", + "name": "Bouton Escalier", + "setup_date": 1645651356, + "room_id": "842662884", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:26", + "type": "NLPT", + "name": "Lumière Chambre E", + "setup_date": 1645657974, + "room_id": "3435163850", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:27", + "type": "NLPT", + "name": "Entrée", + "setup_date": 1645658118, + "room_id": "596817675", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:28", + "type": "NLD", + "name": "Bouton cuisine bas", + "setup_date": 1645659939, + "room_id": "3707962039", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:29", + "type": "NLD", + "name": "Bouton double entrée porte", + "setup_date": 1645660346, + "room_id": "596817675", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:30", + "type": "NLD", + "name": "Bouton double salle à manger", + "setup_date": 1645660684, + "room_id": "3194154910", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:31", + "type": "NLF", + "name": "Placard", + "setup_date": 1645662093, + "room_id": "3194154910", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:32", + "type": "NLT", + "name": "Bouton plafond entrée couloir", + "setup_date": 1645662629, + "room_id": "596817675", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:33", + "type": "NLPT", + "name": "Table", + "setup_date": 1645889017, + "room_id": "3194154910", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:34", + "type": "NLPT", + "name": "Baie Vitrée", + "setup_date": 1645889069, + "room_id": "1462100035", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:35", + "type": "NLC", + "name": "Radiateur Dressing", + "setup_date": 1645894862, + "room_id": "1662974901", + "bridge": "aa:aa:aa:aa:aa:aa", + "appliance_type": "radiator" + }, + { + "id": "98:76:54:32:10:00:00:36", + "type": "NLC", + "name": "Radiateur Entrée", + "setup_date": 1645899253, + "room_id": "596817675", + "bridge": "aa:aa:aa:aa:aa:aa", + "appliance_type": "radiator" + }, + { + "id": "98:76:54:32:10:00:00:37", + "type": "NLC", + "name": "Radiateur Cuisine Dressing", + "setup_date": 1645902157, + "room_id": "3707962039", + "bridge": "aa:aa:aa:aa:aa:aa", + "appliance_type": "radiator" + }, + { + "id": "98:76:54:32:10:00:00:38", + "type": "NLC", + "name": "Radiateur Cuisine Entrée", + "setup_date": 1645902199, + "room_id": "3707962039", + "bridge": "aa:aa:aa:aa:aa:aa", + "appliance_type": "radiator" + }, + { + "id": "98:76:54:32:10:00:00:39", + "type": "NLT", + "name": "Commande sans fil couloir enfant pour entrée", + "setup_date": 1646074736, + "room_id": "596817675", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:40", + "type": "NLPT", + "name": "Couloir Parents", + "setup_date": 1646568523, + "room_id": "3707962039", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:41", + "type": "NLPT", + "name": "Jardin", + "setup_date": 1646568567, + "room_id": "737850817", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:42", + "type": "NLPT", + "name": "Facade", + "setup_date": 1646568594, + "room_id": "737850817", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:43", + "type": "NLC", + "name": "Radiateur FF", + "setup_date": 1646581781, + "room_id": "873035982", + "bridge": "aa:aa:aa:aa:aa:aa", + "appliance_type": "radiator" + }, + { + "id": "98:76:54:32:10:00:00:44", + "type": "NLC", + "name": "Radiateur Salle De Bain Enfants", + "setup_date": 1646828219, + "room_id": "93888250", + "bridge": "aa:aa:aa:aa:aa:aa", + "appliance_type": "radiator" + }, + { + "id": "98:76:54:32:10:00:00:45", + "type": "NLC", + "name": "Radiateur Buanderie", + "setup_date": 1646828251, + "room_id": "2102454491", + "bridge": "aa:aa:aa:aa:aa:aa", + "appliance_type": "radiator" + }, + { + "id": "98:76:54:32:10:00:00:46", + "type": "NLC", + "name": "Radiateur A", + "setup_date": 1646828278, + "room_id": "2042969726", + "bridge": "aa:aa:aa:aa:aa:aa", + "appliance_type": "radiator" + }, + { + "id": "98:76:54:32:10:00:00:47", + "type": "NLC", + "name": "Radiateur B", + "setup_date": 1646828308, + "room_id": "3497055021", + "bridge": "aa:aa:aa:aa:aa:aa", + "appliance_type": "radiator" + }, + { + "id": "98:76:54:32:10:00:00:48", + "type": "NLM", + "name": "Cam Porte", + "setup_date": 1653579677, + "room_id": "2754296835", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:49", + "type": "NLC", + "name": "Radiateur Sèche Serviette E", + "setup_date": 1667205824, + "room_id": "927970817", + "bridge": "aa:aa:aa:aa:aa:aa", + "appliance_type": "radiator" + }, + { + "id": "98:76:54:32:10:00:00:50", + "type": "NLAO", + "name": "Porte vitree sans pile", + "setup_date": 1668001531, + "room_id": "3707962039", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:51", + "type": "NLC", + "name": "Radiateur E", + "setup_date": 1668339519, + "room_id": "3435163850", + "bridge": "aa:aa:aa:aa:aa:aa", + "appliance_type": "radiator" + }, + { + "id": "98:76:54:32:10:00:00:52", + "type": "NLM", + "name": "Meurtrières", + "setup_date": 1671560972, + "room_id": "1462100035", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:53", + "type": "NLPT", + "name": "Parking", + "setup_date": 1672948768, + "room_id": "737850817", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:54", + "type": "NLM", + "name": "Barbecue", + "setup_date": 1672948768, + "room_id": "737850817", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:55", + "type": "NLAO", + "name": "Bouton sans pile cuisine ete", + "setup_date": 1672949735, + "room_id": "737850817", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:03#1", + "type": "NLD", + "setup_date": 1673873611, + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:03#2", + "type": "NLD", + "setup_date": 1673873611, + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:07#1", + "type": "NLD", + "setup_date": 1673873611, + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:07#2", + "type": "NLD", + "setup_date": 1673873611, + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:08#1", + "type": "NLD", + "setup_date": 1673873611, + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:08#2", + "type": "NLD", + "setup_date": 1673873611, + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:28#1", + "type": "NLD", + "setup_date": 1673873611, + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:28#2", + "type": "NLD", + "setup_date": 1673873611, + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:29#1", + "type": "NLD", + "setup_date": 1673873611, + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:29#2", + "type": "NLD", + "setup_date": 1673873611, + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:30#1", + "type": "NLD", + "setup_date": 1673873611, + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:30#2", + "type": "NLD", + "setup_date": 1673873611, + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:50#1", + "type": "NLAO", + "setup_date": 1673873611, + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:50#2", + "type": "NLAO", + "setup_date": 1673873611, + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:56", + "type": "NLM", + "name": "Piscine", + "setup_date": 1691244631, + "room_id": "737850817", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:57", + "type": "NLC", + "name": "Radiateur DD", + "setup_date": 1692093769, + "room_id": "3795659199", + "bridge": "aa:aa:aa:aa:aa:aa", + "appliance_type": "radiator" + }, + { + "id": "98:76:54:32:10:00:00:58", + "type": "NLM", + "name": "Salle de bain", + "setup_date": 1692094781, + "room_id": "2061006239", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:59", + "type": "NLP", + "name": "Prise Pool House Relais Zigbee", + "setup_date": 1692095384, + "room_id": "2754296835", + "bridge": "aa:aa:aa:aa:aa:aa", + "appliance_type": "router" + }, + { + "id": "98:76:54:32:10:00:00:60", + "type": "NLPT", + "name": "Plafonnier", + "setup_date": 1692095856, + "room_id": "1462100035", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:61", + "type": "NLPT", + "name": "Terrasse Piscine", + "setup_date": 1693848343, + "room_id": "2061006239", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:62", + "type": "NLPT", + "name": "Lumière", + "setup_date": 1693848357, + "room_id": "2061006239", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:63", + "type": "NLD", + "name": "Salon Commande Double Haut", + "setup_date": 1694336106, + "room_id": "1462100035", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:63#1", + "type": "NLD", + "setup_date": 1694336106, + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:63#2", + "type": "NLD", + "setup_date": 1694336106, + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:64", + "type": "NLD", + "name": "Commande Pool House Gauche", + "setup_date": 1694336110, + "room_id": "2061006239", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:64#1", + "type": "NLD", + "setup_date": 1694336110, + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:64#2", + "type": "NLD", + "setup_date": 1694336110, + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:65", + "type": "NLD", + "name": "Commande Pool House Droite", + "setup_date": 1694336143, + "room_id": "2061006239", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:65#1", + "type": "NLD", + "setup_date": 1694336143, + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:65#2", + "type": "NLD", + "setup_date": 1694336143, + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:66", + "type": "NLD", + "name": "Commande Double Bas", + "setup_date": 1698577707, + "room_id": "1462100035", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:66#1", + "type": "NLD", + "setup_date": 1698577707, + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:66#2", + "type": "NLD", + "setup_date": 1698577707, + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:67", + "type": "NLPO", + "name": "Cumulus Parents", + "setup_date": 1699128857, + "room_id": "2754296835", + "bridge": "aa:aa:aa:aa:aa:aa", + "appliance_type": "water_heater" + }, + { + "id": "98:76:54:32:10:00:00:68", + "type": "NLPO", + "name": "Cumulus DD", + "setup_date": 1699132798, + "room_id": "2754296835", + "bridge": "aa:aa:aa:aa:aa:aa", + "appliance_type": "water_heater" + }, + { + "id": "98:76:54:32:10:00:00:69", + "type": "NLPO", + "name": "Filtration Piscine", + "setup_date": 1699136266, + "room_id": "2754296835", + "bridge": "aa:aa:aa:aa:aa:aa", + "appliance_type": "other" + }, + { + "id": "98:76:54:32:10:00:00:70", + "type": "NLC", + "name": "Radiateur Sèche Serviette A", + "setup_date": 1702133314, + "room_id": "1641945290", + "bridge": "aa:aa:aa:aa:aa:aa", + "appliance_type": "radiator" + }, + { + "id": "98:76:54:32:10:00:00:71", + "type": "NLPO", + "name": "Cumulus Pool House", + "setup_date": 1702135239, + "room_id": "2061006239", + "bridge": "aa:aa:aa:aa:aa:aa", + "appliance_type": "water_heater" + }, + { + "id": "98:76:54:32:10:00:00:72", + "type": "NLC", + "name": "Radiateur Sol Salle À Manger", + "setup_date": 1707332251, + "room_id": "3194154910", + "bridge": "aa:aa:aa:aa:aa:aa", + "appliance_type": "radiator_without_pilot_wire" + }, + { + "id": "98:76:54:32:10:00:00:73", + "type": "NLC", + "name": "Radiateur Sol Salon", + "setup_date": 1707332251, + "room_id": "1462100035", + "bridge": "aa:aa:aa:aa:aa:aa", + "appliance_type": "radiator_without_pilot_wire" + }, + { + "id": "98:76:54:32:10:00:00:74", + "type": "NLP", + "name": "Setup PC B", + "setup_date": 1707332620, + "room_id": "3497055021", + "bridge": "aa:aa:aa:aa:aa:aa", + "appliance_type": "router" + }, + { + "id": "98:76:54:32:10:00:00:75", + "type": "NLPM", + "name": "Setup TV Apple TV Switch Salon", + "setup_date": 1707333771, + "room_id": "1462100035", + "bridge": "aa:aa:aa:aa:aa:aa", + "appliance_type": "multimedia" + }, + { + "id": "98:76:54:32:10:00:00:76", + "type": "NLP", + "name": "Mesure PC Switch C", + "setup_date": 1707335636, + "room_id": "873035982", + "bridge": "aa:aa:aa:aa:aa:aa", + "appliance_type": "router" + }, + { + "id": "98:76:54:32:10:00:00:77", + "type": "NLPC", + "name": "Compteur Plaque Sèche-Linge PC Buanderie Plan Cuisine", + "setup_date": 1707339526, + "room_id": "2754296835", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:78", + "type": "NLPC", + "name": "Compteur Congel Micro-onde Frigo PC WC", + "setup_date": 1708185348, + "room_id": "2754296835", + "bridge": "aa:aa:aa:aa:aa:aa" + }, + { + "id": "98:76:54:32:10:00:00:79", + "type": "NLPC", + "name": "Compteur Lave-linge Four Lave Lave-Vaisselle PC TV Chambre ", + "setup_date": 1708185369, + "room_id": "2754296835", + "bridge": "aa:aa:aa:aa:aa:aa" + } + ], + "temperature_control_mode": "heating", + "therm_mode": "schedule", + "therm_setpoint_default_duration": 180, + "schedules": [ + { + "timetable": [ + { + "zone_id": 1, + "m_offset": 0 + }, + { + "zone_id": 0, + "m_offset": 360 + }, + { + "zone_id": 1, + "m_offset": 510 + }, + { + "zone_id": 5, + "m_offset": 960 + }, + { + "zone_id": 1, + "m_offset": 1200 + }, + { + "zone_id": 2, + "m_offset": 1260 + }, + { + "zone_id": 1, + "m_offset": 1380 + }, + { + "zone_id": 0, + "m_offset": 1800 + }, + { + "zone_id": 1, + "m_offset": 1950 + }, + { + "zone_id": 5, + "m_offset": 2400 + }, + { + "zone_id": 1, + "m_offset": 2640 + }, + { + "zone_id": 2, + "m_offset": 2700 + }, + { + "zone_id": 1, + "m_offset": 2820 + }, + { + "zone_id": 0, + "m_offset": 3240 + }, + { + "zone_id": 1, + "m_offset": 3390 + }, + { + "zone_id": 5, + "m_offset": 3840 + }, + { + "zone_id": 1, + "m_offset": 4080 + }, + { + "zone_id": 2, + "m_offset": 4140 + }, + { + "zone_id": 1, + "m_offset": 4260 + }, + { + "zone_id": 0, + "m_offset": 4680 + }, + { + "zone_id": 1, + "m_offset": 4830 + }, + { + "zone_id": 5, + "m_offset": 5280 + }, + { + "zone_id": 1, + "m_offset": 5520 + }, + { + "zone_id": 2, + "m_offset": 5580 + }, + { + "zone_id": 1, + "m_offset": 5700 + }, + { + "zone_id": 0, + "m_offset": 6120 + }, + { + "zone_id": 1, + "m_offset": 6270 + }, + { + "zone_id": 5, + "m_offset": 6720 + }, + { + "zone_id": 1, + "m_offset": 6960 + }, + { + "zone_id": 2, + "m_offset": 7020 + }, + { + "zone_id": 1, + "m_offset": 7140 + }, + { + "zone_id": 0, + "m_offset": 7560 + }, + { + "zone_id": 1, + "m_offset": 7710 + }, + { + "zone_id": 5, + "m_offset": 8160 + }, + { + "zone_id": 1, + "m_offset": 8400 + }, + { + "zone_id": 2, + "m_offset": 8460 + }, + { + "zone_id": 1, + "m_offset": 8580 + }, + { + "zone_id": 0, + "m_offset": 9000 + }, + { + "zone_id": 1, + "m_offset": 9150 + }, + { + "zone_id": 5, + "m_offset": 9600 + }, + { + "zone_id": 1, + "m_offset": 9840 + }, + { + "zone_id": 2, + "m_offset": 9900 + }, + { + "zone_id": 1, + "m_offset": 10020 + } + ], + "zones": [ + { + "name": "Comfort", + "id": 0, + "type": 0, + "rooms_temp": [ + { + "room_id": "3707962039", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "2042969726", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3497055021", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3435163850", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "873035982", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "1662974901", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "93888250", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "596817675", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3795659199", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "927970817", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "1641945290", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "2102454491", + "therm_setpoint_fp": "comfort" + } + ], + "rooms": [ + { + "id": "3707962039", + "therm_setpoint_fp": "comfort" + }, + { + "id": "2042969726", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3497055021", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3435163850", + "therm_setpoint_fp": "comfort" + }, + { + "id": "873035982", + "therm_setpoint_fp": "comfort" + }, + { + "id": "1662974901", + "therm_setpoint_fp": "comfort" + }, + { + "id": "93888250", + "therm_setpoint_fp": "comfort" + }, + { + "id": "596817675", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3795659199", + "therm_setpoint_fp": "comfort" + }, + { + "id": "927970817", + "therm_setpoint_fp": "comfort" + }, + { + "id": "1641945290", + "therm_setpoint_fp": "comfort" + }, + { + "id": "2102454491", + "therm_setpoint_fp": "comfort" + } + ] + }, + { + "name": "Comfort+", + "id": 3, + "type": 8, + "rooms_temp": [ + { + "room_id": "3707962039", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "2042969726", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3497055021", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3435163850", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "873035982", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "1662974901", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "93888250", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "596817675", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3795659199", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "927970817", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "1641945290", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "2102454491", + "therm_setpoint_fp": "comfort" + } + ], + "rooms": [ + { + "id": "3707962039", + "therm_setpoint_fp": "comfort" + }, + { + "id": "2042969726", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3497055021", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3435163850", + "therm_setpoint_fp": "comfort" + }, + { + "id": "873035982", + "therm_setpoint_fp": "comfort" + }, + { + "id": "1662974901", + "therm_setpoint_fp": "comfort" + }, + { + "id": "93888250", + "therm_setpoint_fp": "comfort" + }, + { + "id": "596817675", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3795659199", + "therm_setpoint_fp": "comfort" + }, + { + "id": "927970817", + "therm_setpoint_fp": "comfort" + }, + { + "id": "1641945290", + "therm_setpoint_fp": "comfort" + }, + { + "id": "2102454491", + "therm_setpoint_fp": "comfort" + } + ] + }, + { + "name": "Éco", + "id": 4, + "type": 5, + "rooms_temp": [ + { + "room_id": "3707962039", + "therm_setpoint_fp": "away" + }, + { + "room_id": "2042969726", + "therm_setpoint_fp": "away" + }, + { + "room_id": "3497055021", + "therm_setpoint_fp": "away" + }, + { + "room_id": "3435163850", + "therm_setpoint_fp": "away" + }, + { + "room_id": "873035982", + "therm_setpoint_fp": "away" + }, + { + "room_id": "1662974901", + "therm_setpoint_fp": "away" + }, + { + "room_id": "93888250", + "therm_setpoint_fp": "away" + }, + { + "room_id": "596817675", + "therm_setpoint_fp": "away" + }, + { + "room_id": "3795659199", + "therm_setpoint_fp": "away" + }, + { + "room_id": "927970817", + "therm_setpoint_fp": "away" + }, + { + "room_id": "1641945290", + "therm_setpoint_fp": "away" + }, + { + "room_id": "2102454491", + "therm_setpoint_fp": "away" + } + ], + "modules": [], + "rooms": [ + { + "id": "3707962039", + "therm_setpoint_fp": "away" + }, + { + "id": "2042969726", + "therm_setpoint_fp": "away" + }, + { + "id": "3497055021", + "therm_setpoint_fp": "away" + }, + { + "id": "3435163850", + "therm_setpoint_fp": "away" + }, + { + "id": "873035982", + "therm_setpoint_fp": "away" + }, + { + "id": "1662974901", + "therm_setpoint_fp": "away" + }, + { + "id": "93888250", + "therm_setpoint_fp": "away" + }, + { + "id": "596817675", + "therm_setpoint_fp": "away" + }, + { + "id": "3795659199", + "therm_setpoint_fp": "away" + }, + { + "id": "927970817", + "therm_setpoint_fp": "away" + }, + { + "id": "1641945290", + "therm_setpoint_fp": "away" + }, + { + "id": "2102454491", + "therm_setpoint_fp": "away" + } + ] + }, + { + "name": "Nuit", + "id": 1, + "type": 1, + "rooms_temp": [ + { + "room_id": "3707962039", + "therm_setpoint_fp": "away" + }, + { + "room_id": "2042969726", + "therm_setpoint_fp": "away" + }, + { + "room_id": "3497055021", + "therm_setpoint_fp": "away" + }, + { + "room_id": "3435163850", + "therm_setpoint_fp": "away" + }, + { + "room_id": "873035982", + "therm_setpoint_fp": "away" + }, + { + "room_id": "1662974901", + "therm_setpoint_fp": "away" + }, + { + "room_id": "93888250", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "596817675", + "therm_setpoint_fp": "away" + }, + { + "room_id": "3795659199", + "therm_setpoint_fp": "away" + }, + { + "room_id": "927970817", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "1641945290", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "2102454491", + "therm_setpoint_fp": "away" + } + ], + "modules": [], + "rooms": [ + { + "id": "3707962039", + "therm_setpoint_fp": "away" + }, + { + "id": "2042969726", + "therm_setpoint_fp": "away" + }, + { + "id": "3497055021", + "therm_setpoint_fp": "away" + }, + { + "id": "3435163850", + "therm_setpoint_fp": "away" + }, + { + "id": "873035982", + "therm_setpoint_fp": "away" + }, + { + "id": "1662974901", + "therm_setpoint_fp": "away" + }, + { + "id": "93888250", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "596817675", + "therm_setpoint_fp": "away" + }, + { + "id": "3795659199", + "therm_setpoint_fp": "away" + }, + { + "id": "927970817", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "1641945290", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "2102454491", + "therm_setpoint_fp": "away" + } + ] + }, + { + "name": "Confort sauf chambres parents", + "id": 5, + "type": 4, + "rooms_temp": [ + { + "room_id": "1641945290", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "1662974901", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "2042969726", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3435163850", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3497055021", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3707962039", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3795659199", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "596817675", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "873035982", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "927970817", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "93888250", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "2102454491", + "therm_setpoint_fp": "comfort" + } + ], + "modules": [], + "rooms": [ + { + "id": "1641945290", + "therm_setpoint_fp": "comfort" + }, + { + "id": "1662974901", + "therm_setpoint_fp": "comfort" + }, + { + "id": "2042969726", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3435163850", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3497055021", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3707962039", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3795659199", + "therm_setpoint_fp": "comfort" + }, + { + "id": "596817675", + "therm_setpoint_fp": "comfort" + }, + { + "id": "873035982", + "therm_setpoint_fp": "comfort" + }, + { + "id": "927970817", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "93888250", + "therm_setpoint_fp": "comfort" + }, + { + "id": "2102454491", + "therm_setpoint_fp": "comfort" + } + ] + }, + { + "name": "Chambre parents only", + "id": 2, + "type": 4, + "rooms_temp": [ + { + "room_id": "1641945290", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "1662974901", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "2042969726", + "therm_setpoint_fp": "away" + }, + { + "room_id": "3435163850", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3497055021", + "therm_setpoint_fp": "away" + }, + { + "room_id": "3707962039", + "therm_setpoint_fp": "away" + }, + { + "room_id": "3795659199", + "therm_setpoint_fp": "away" + }, + { + "room_id": "596817675", + "therm_setpoint_fp": "away" + }, + { + "room_id": "873035982", + "therm_setpoint_fp": "away" + }, + { + "room_id": "927970817", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "93888250", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "2102454491", + "therm_setpoint_fp": "comfort" + } + ], + "modules": [], + "rooms": [ + { + "id": "1641945290", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "1662974901", + "therm_setpoint_fp": "comfort" + }, + { + "id": "2042969726", + "therm_setpoint_fp": "away" + }, + { + "id": "3435163850", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3497055021", + "therm_setpoint_fp": "away" + }, + { + "id": "3707962039", + "therm_setpoint_fp": "away" + }, + { + "id": "3795659199", + "therm_setpoint_fp": "away" + }, + { + "id": "596817675", + "therm_setpoint_fp": "away" + }, + { + "id": "873035982", + "therm_setpoint_fp": "away" + }, + { + "id": "927970817", + "therm_setpoint_fp": "comfort" + }, + { + "id": "93888250", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "2102454491", + "therm_setpoint_fp": "comfort" + } + ] + } + ], + "name": "Planning Hiver", + "default": false, + "away_temp": 12, + "hg_temp": 7, + "id": "61fa621cdd99943657260882", + "type": "therm", + "selected": true + }, + { + "timetable": [ + { + "zone_id": 2, + "m_offset": 0 + } + ], + "zones": [ + { + "name": "Comfort", + "id": 0, + "type": 0, + "rooms_temp": [ + { + "room_id": "3707962039", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "2042969726", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3497055021", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3435163850", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "873035982", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "1662974901", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "93888250", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "596817675", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3795659199", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "927970817", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "1641945290", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "2102454491", + "therm_setpoint_fp": "comfort" + } + ], + "rooms": [ + { + "id": "3707962039", + "therm_setpoint_fp": "comfort" + }, + { + "id": "2042969726", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3497055021", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3435163850", + "therm_setpoint_fp": "comfort" + }, + { + "id": "873035982", + "therm_setpoint_fp": "comfort" + }, + { + "id": "1662974901", + "therm_setpoint_fp": "comfort" + }, + { + "id": "93888250", + "therm_setpoint_fp": "comfort" + }, + { + "id": "596817675", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3795659199", + "therm_setpoint_fp": "comfort" + }, + { + "id": "927970817", + "therm_setpoint_fp": "comfort" + }, + { + "id": "1641945290", + "therm_setpoint_fp": "comfort" + }, + { + "id": "2102454491", + "therm_setpoint_fp": "comfort" + } + ] + }, + { + "name": "Comfort+", + "id": 3, + "type": 8, + "rooms_temp": [ + { + "room_id": "3707962039", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "2042969726", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3497055021", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3435163850", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "873035982", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "1662974901", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "93888250", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "596817675", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3795659199", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "927970817", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "1641945290", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "2102454491", + "therm_setpoint_fp": "comfort" + } + ], + "rooms": [ + { + "id": "3707962039", + "therm_setpoint_fp": "comfort" + }, + { + "id": "2042969726", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3497055021", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3435163850", + "therm_setpoint_fp": "comfort" + }, + { + "id": "873035982", + "therm_setpoint_fp": "comfort" + }, + { + "id": "1662974901", + "therm_setpoint_fp": "comfort" + }, + { + "id": "93888250", + "therm_setpoint_fp": "comfort" + }, + { + "id": "596817675", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3795659199", + "therm_setpoint_fp": "comfort" + }, + { + "id": "927970817", + "therm_setpoint_fp": "comfort" + }, + { + "id": "1641945290", + "therm_setpoint_fp": "comfort" + }, + { + "id": "2102454491", + "therm_setpoint_fp": "comfort" + } + ] + }, + { + "name": "Éco", + "id": 4, + "type": 5, + "rooms_temp": [ + { + "room_id": "3707962039", + "therm_setpoint_fp": "away" + }, + { + "room_id": "2042969726", + "therm_setpoint_fp": "away" + }, + { + "room_id": "3497055021", + "therm_setpoint_fp": "away" + }, + { + "room_id": "3435163850", + "therm_setpoint_fp": "away" + }, + { + "room_id": "873035982", + "therm_setpoint_fp": "away" + }, + { + "room_id": "1662974901", + "therm_setpoint_fp": "away" + }, + { + "room_id": "93888250", + "therm_setpoint_fp": "away" + }, + { + "room_id": "596817675", + "therm_setpoint_fp": "away" + }, + { + "room_id": "3795659199", + "therm_setpoint_fp": "away" + }, + { + "room_id": "927970817", + "therm_setpoint_fp": "away" + }, + { + "room_id": "1641945290", + "therm_setpoint_fp": "away" + }, + { + "room_id": "2102454491", + "therm_setpoint_fp": "away" + } + ], + "modules": [], + "rooms": [ + { + "id": "3707962039", + "therm_setpoint_fp": "away" + }, + { + "id": "2042969726", + "therm_setpoint_fp": "away" + }, + { + "id": "3497055021", + "therm_setpoint_fp": "away" + }, + { + "id": "3435163850", + "therm_setpoint_fp": "away" + }, + { + "id": "873035982", + "therm_setpoint_fp": "away" + }, + { + "id": "1662974901", + "therm_setpoint_fp": "away" + }, + { + "id": "93888250", + "therm_setpoint_fp": "away" + }, + { + "id": "596817675", + "therm_setpoint_fp": "away" + }, + { + "id": "3795659199", + "therm_setpoint_fp": "away" + }, + { + "id": "927970817", + "therm_setpoint_fp": "away" + }, + { + "id": "1641945290", + "therm_setpoint_fp": "away" + }, + { + "id": "2102454491", + "therm_setpoint_fp": "away" + } + ] + }, + { + "name": "Nuit", + "id": 1, + "type": 1, + "rooms_temp": [ + { + "room_id": "3707962039", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "2042969726", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "3497055021", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "3435163850", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "873035982", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "1662974901", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "93888250", + "therm_setpoint_fp": "away" + }, + { + "room_id": "596817675", + "therm_setpoint_fp": "away" + }, + { + "room_id": "3795659199", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "927970817", + "therm_setpoint_fp": "away" + }, + { + "room_id": "1641945290", + "therm_setpoint_fp": "away" + }, + { + "room_id": "2102454491", + "therm_setpoint_fp": "frost_guard" + } + ], + "modules": [], + "rooms": [ + { + "id": "3707962039", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "2042969726", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "3497055021", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "3435163850", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "873035982", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "1662974901", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "93888250", + "therm_setpoint_fp": "away" + }, + { + "id": "596817675", + "therm_setpoint_fp": "away" + }, + { + "id": "3795659199", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "927970817", + "therm_setpoint_fp": "away" + }, + { + "id": "1641945290", + "therm_setpoint_fp": "away" + }, + { + "id": "2102454491", + "therm_setpoint_fp": "frost_guard" + } + ] + }, + { + "name": "Off", + "id": 2, + "type": 4, + "rooms_temp": [ + { + "room_id": "3707962039", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "2042969726", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "3497055021", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "3435163850", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "873035982", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "1662974901", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "93888250", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "596817675", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3795659199", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "927970817", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "1641945290", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "2102454491", + "therm_setpoint_fp": "frost_guard" + } + ], + "modules": [], + "rooms": [ + { + "id": "3707962039", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "2042969726", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "3497055021", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "3435163850", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "873035982", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "1662974901", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "93888250", + "therm_setpoint_fp": "comfort" + }, + { + "id": "596817675", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3795659199", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "927970817", + "therm_setpoint_fp": "comfort" + }, + { + "id": "1641945290", + "therm_setpoint_fp": "comfort" + }, + { + "id": "2102454491", + "therm_setpoint_fp": "frost_guard" + } + ] + } + ], + "name": "Planning ete", + "default": false, + "away_temp": 12, + "hg_temp": 7, + "id": "625a76ceec2cce72075ac55a", + "type": "therm" + }, + { + "timetable": [ + { + "zone_id": 0, + "m_offset": 0 + } + ], + "zones": [ + { + "name": "Comfort", + "id": 0, + "type": 0, + "rooms_temp": [ + { + "room_id": "3707962039", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "2042969726", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3497055021", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3435163850", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "873035982", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "1662974901", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "93888250", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "596817675", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3795659199", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "927970817", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "1641945290", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "2102454491", + "therm_setpoint_fp": "comfort" + } + ], + "modules": [], + "rooms": [ + { + "id": "3707962039", + "therm_setpoint_fp": "comfort" + }, + { + "id": "2042969726", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3497055021", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3435163850", + "therm_setpoint_fp": "comfort" + }, + { + "id": "873035982", + "therm_setpoint_fp": "comfort" + }, + { + "id": "1662974901", + "therm_setpoint_fp": "comfort" + }, + { + "id": "93888250", + "therm_setpoint_fp": "comfort" + }, + { + "id": "596817675", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3795659199", + "therm_setpoint_fp": "comfort" + }, + { + "id": "927970817", + "therm_setpoint_fp": "comfort" + }, + { + "id": "1641945290", + "therm_setpoint_fp": "comfort" + }, + { + "id": "2102454491", + "therm_setpoint_fp": "comfort" + } + ] + }, + { + "name": "Night", + "id": 1, + "type": 1, + "rooms_temp": [ + { + "room_id": "3707962039", + "therm_setpoint_fp": "away" + }, + { + "room_id": "2042969726", + "therm_setpoint_fp": "away" + }, + { + "room_id": "3497055021", + "therm_setpoint_fp": "away" + }, + { + "room_id": "3435163850", + "therm_setpoint_fp": "away" + }, + { + "room_id": "873035982", + "therm_setpoint_fp": "away" + }, + { + "room_id": "1662974901", + "therm_setpoint_fp": "away" + }, + { + "room_id": "93888250", + "therm_setpoint_fp": "away" + }, + { + "room_id": "596817675", + "therm_setpoint_fp": "away" + }, + { + "room_id": "3795659199", + "therm_setpoint_fp": "away" + }, + { + "room_id": "927970817", + "therm_setpoint_fp": "away" + }, + { + "room_id": "1641945290", + "therm_setpoint_fp": "away" + }, + { + "room_id": "2102454491", + "therm_setpoint_fp": "away" + } + ], + "modules": [], + "rooms": [ + { + "id": "3707962039", + "therm_setpoint_fp": "away" + }, + { + "id": "2042969726", + "therm_setpoint_fp": "away" + }, + { + "id": "3497055021", + "therm_setpoint_fp": "away" + }, + { + "id": "3435163850", + "therm_setpoint_fp": "away" + }, + { + "id": "873035982", + "therm_setpoint_fp": "away" + }, + { + "id": "1662974901", + "therm_setpoint_fp": "away" + }, + { + "id": "93888250", + "therm_setpoint_fp": "away" + }, + { + "id": "596817675", + "therm_setpoint_fp": "away" + }, + { + "id": "3795659199", + "therm_setpoint_fp": "away" + }, + { + "id": "927970817", + "therm_setpoint_fp": "away" + }, + { + "id": "1641945290", + "therm_setpoint_fp": "away" + }, + { + "id": "2102454491", + "therm_setpoint_fp": "away" + } + ] + }, + { + "name": "Comfort+", + "id": 3, + "type": 8, + "rooms_temp": [ + { + "room_id": "3707962039", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "2042969726", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3497055021", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3435163850", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "873035982", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "1662974901", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "93888250", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "596817675", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3795659199", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "927970817", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "1641945290", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "2102454491", + "therm_setpoint_fp": "comfort" + } + ], + "modules": [], + "rooms": [ + { + "id": "3707962039", + "therm_setpoint_fp": "comfort" + }, + { + "id": "2042969726", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3497055021", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3435163850", + "therm_setpoint_fp": "comfort" + }, + { + "id": "873035982", + "therm_setpoint_fp": "comfort" + }, + { + "id": "1662974901", + "therm_setpoint_fp": "comfort" + }, + { + "id": "93888250", + "therm_setpoint_fp": "comfort" + }, + { + "id": "596817675", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3795659199", + "therm_setpoint_fp": "comfort" + }, + { + "id": "927970817", + "therm_setpoint_fp": "comfort" + }, + { + "id": "1641945290", + "therm_setpoint_fp": "comfort" + }, + { + "id": "2102454491", + "therm_setpoint_fp": "comfort" + } + ] + }, + { + "name": "Eco", + "id": 4, + "type": 5, + "rooms_temp": [ + { + "room_id": "3707962039", + "therm_setpoint_fp": "away" + }, + { + "room_id": "2042969726", + "therm_setpoint_fp": "away" + }, + { + "room_id": "3497055021", + "therm_setpoint_fp": "away" + }, + { + "room_id": "3435163850", + "therm_setpoint_fp": "away" + }, + { + "room_id": "873035982", + "therm_setpoint_fp": "away" + }, + { + "room_id": "1662974901", + "therm_setpoint_fp": "away" + }, + { + "room_id": "93888250", + "therm_setpoint_fp": "away" + }, + { + "room_id": "596817675", + "therm_setpoint_fp": "away" + }, + { + "room_id": "3795659199", + "therm_setpoint_fp": "away" + }, + { + "room_id": "927970817", + "therm_setpoint_fp": "away" + }, + { + "room_id": "1641945290", + "therm_setpoint_fp": "away" + }, + { + "room_id": "2102454491", + "therm_setpoint_fp": "away" + } + ], + "modules": [], + "rooms": [ + { + "id": "3707962039", + "therm_setpoint_fp": "away" + }, + { + "id": "2042969726", + "therm_setpoint_fp": "away" + }, + { + "id": "3497055021", + "therm_setpoint_fp": "away" + }, + { + "id": "3435163850", + "therm_setpoint_fp": "away" + }, + { + "id": "873035982", + "therm_setpoint_fp": "away" + }, + { + "id": "1662974901", + "therm_setpoint_fp": "away" + }, + { + "id": "93888250", + "therm_setpoint_fp": "away" + }, + { + "id": "596817675", + "therm_setpoint_fp": "away" + }, + { + "id": "3795659199", + "therm_setpoint_fp": "away" + }, + { + "id": "927970817", + "therm_setpoint_fp": "away" + }, + { + "id": "1641945290", + "therm_setpoint_fp": "away" + }, + { + "id": "2102454491", + "therm_setpoint_fp": "away" + } + ] + } + ], + "name": "Full", + "default": false, + "away_temp": 12, + "hg_temp": 7, + "id": "63a44ba0735ffc27410f2331", + "type": "therm" + }, + { + "timetable": [ + { + "zone_id": 4, + "m_offset": 0 + }, + { + "zone_id": 0, + "m_offset": 300 + }, + { + "zone_id": 4, + "m_offset": 420 + }, + { + "zone_id": 0, + "m_offset": 855 + }, + { + "zone_id": 4, + "m_offset": 1140 + }, + { + "zone_id": 0, + "m_offset": 1740 + }, + { + "zone_id": 4, + "m_offset": 1860 + }, + { + "zone_id": 0, + "m_offset": 2295 + }, + { + "zone_id": 4, + "m_offset": 2580 + }, + { + "zone_id": 0, + "m_offset": 3180 + }, + { + "zone_id": 4, + "m_offset": 3300 + }, + { + "zone_id": 0, + "m_offset": 3735 + }, + { + "zone_id": 4, + "m_offset": 4020 + }, + { + "zone_id": 0, + "m_offset": 4620 + }, + { + "zone_id": 4, + "m_offset": 4740 + }, + { + "zone_id": 0, + "m_offset": 5175 + }, + { + "zone_id": 4, + "m_offset": 5460 + }, + { + "zone_id": 0, + "m_offset": 6060 + }, + { + "zone_id": 4, + "m_offset": 6180 + }, + { + "zone_id": 0, + "m_offset": 6615 + }, + { + "zone_id": 4, + "m_offset": 6900 + }, + { + "zone_id": 0, + "m_offset": 7500 + }, + { + "zone_id": 4, + "m_offset": 7620 + }, + { + "zone_id": 0, + "m_offset": 8055 + }, + { + "zone_id": 4, + "m_offset": 8340 + }, + { + "zone_id": 0, + "m_offset": 8940 + }, + { + "zone_id": 4, + "m_offset": 9060 + }, + { + "zone_id": 0, + "m_offset": 9495 + }, + { + "zone_id": 4, + "m_offset": 9780 + } + ], + "zones": [ + { + "name": "Comfort", + "id": 0, + "type": 0, + "rooms_temp": [ + { + "room_id": "3707962039", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "2042969726", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3497055021", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3435163850", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "873035982", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "1662974901", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "93888250", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "596817675", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3795659199", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "927970817", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "1641945290", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "2102454491", + "therm_setpoint_fp": "comfort" + } + ], + "rooms": [ + { + "id": "3707962039", + "therm_setpoint_fp": "comfort" + }, + { + "id": "2042969726", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3497055021", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3435163850", + "therm_setpoint_fp": "comfort" + }, + { + "id": "873035982", + "therm_setpoint_fp": "comfort" + }, + { + "id": "1662974901", + "therm_setpoint_fp": "comfort" + }, + { + "id": "93888250", + "therm_setpoint_fp": "comfort" + }, + { + "id": "596817675", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3795659199", + "therm_setpoint_fp": "comfort" + }, + { + "id": "927970817", + "therm_setpoint_fp": "comfort" + }, + { + "id": "1641945290", + "therm_setpoint_fp": "comfort" + }, + { + "id": "2102454491", + "therm_setpoint_fp": "comfort" + } + ] + }, + { + "name": "Comfort+", + "id": 3, + "type": 8, + "rooms_temp": [ + { + "room_id": "3707962039", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "2042969726", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3497055021", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3435163850", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "873035982", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "1662974901", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "93888250", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "596817675", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3795659199", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "927970817", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "1641945290", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "2102454491", + "therm_setpoint_fp": "comfort" + } + ], + "rooms": [ + { + "id": "3707962039", + "therm_setpoint_fp": "comfort" + }, + { + "id": "2042969726", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3497055021", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3435163850", + "therm_setpoint_fp": "comfort" + }, + { + "id": "873035982", + "therm_setpoint_fp": "comfort" + }, + { + "id": "1662974901", + "therm_setpoint_fp": "comfort" + }, + { + "id": "93888250", + "therm_setpoint_fp": "comfort" + }, + { + "id": "596817675", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3795659199", + "therm_setpoint_fp": "comfort" + }, + { + "id": "927970817", + "therm_setpoint_fp": "comfort" + }, + { + "id": "1641945290", + "therm_setpoint_fp": "comfort" + }, + { + "id": "2102454491", + "therm_setpoint_fp": "comfort" + } + ] + }, + { + "name": "Éco", + "id": 4, + "type": 5, + "rooms_temp": [ + { + "room_id": "3707962039", + "therm_setpoint_fp": "away" + }, + { + "room_id": "2042969726", + "therm_setpoint_fp": "away" + }, + { + "room_id": "3497055021", + "therm_setpoint_fp": "away" + }, + { + "room_id": "3435163850", + "therm_setpoint_fp": "away" + }, + { + "room_id": "873035982", + "therm_setpoint_fp": "away" + }, + { + "room_id": "1662974901", + "therm_setpoint_fp": "away" + }, + { + "room_id": "93888250", + "therm_setpoint_fp": "away" + }, + { + "room_id": "596817675", + "therm_setpoint_fp": "away" + }, + { + "room_id": "3795659199", + "therm_setpoint_fp": "away" + }, + { + "room_id": "927970817", + "therm_setpoint_fp": "away" + }, + { + "room_id": "1641945290", + "therm_setpoint_fp": "away" + }, + { + "room_id": "2102454491", + "therm_setpoint_fp": "away" + } + ], + "modules": [], + "rooms": [ + { + "id": "3707962039", + "therm_setpoint_fp": "away" + }, + { + "id": "2042969726", + "therm_setpoint_fp": "away" + }, + { + "id": "3497055021", + "therm_setpoint_fp": "away" + }, + { + "id": "3435163850", + "therm_setpoint_fp": "away" + }, + { + "id": "873035982", + "therm_setpoint_fp": "away" + }, + { + "id": "1662974901", + "therm_setpoint_fp": "away" + }, + { + "id": "93888250", + "therm_setpoint_fp": "away" + }, + { + "id": "596817675", + "therm_setpoint_fp": "away" + }, + { + "id": "3795659199", + "therm_setpoint_fp": "away" + }, + { + "id": "927970817", + "therm_setpoint_fp": "away" + }, + { + "id": "1641945290", + "therm_setpoint_fp": "away" + }, + { + "id": "2102454491", + "therm_setpoint_fp": "away" + } + ] + }, + { + "name": "Nuit", + "id": 1, + "type": 1, + "rooms_temp": [ + { + "room_id": "3707962039", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "2042969726", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "3497055021", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "3435163850", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "873035982", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "1662974901", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "93888250", + "therm_setpoint_fp": "away" + }, + { + "room_id": "596817675", + "therm_setpoint_fp": "away" + }, + { + "room_id": "3795659199", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "927970817", + "therm_setpoint_fp": "away" + }, + { + "room_id": "1641945290", + "therm_setpoint_fp": "away" + }, + { + "room_id": "2102454491", + "therm_setpoint_fp": "frost_guard" + } + ], + "modules": [], + "rooms": [ + { + "id": "3707962039", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "2042969726", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "3497055021", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "3435163850", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "873035982", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "1662974901", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "93888250", + "therm_setpoint_fp": "away" + }, + { + "id": "596817675", + "therm_setpoint_fp": "away" + }, + { + "id": "3795659199", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "927970817", + "therm_setpoint_fp": "away" + }, + { + "id": "1641945290", + "therm_setpoint_fp": "away" + }, + { + "id": "2102454491", + "therm_setpoint_fp": "frost_guard" + } + ] + } + ], + "name": "Planning Hiver frileux", + "default": false, + "away_temp": 12, + "hg_temp": 7, + "id": "63c5b63b766611525b0b1e4d", + "type": "therm" + }, + { + "timetable": [ + { + "zone_id": 1, + "m_offset": 0 + }, + { + "zone_id": 0, + "m_offset": 300 + }, + { + "zone_id": 4, + "m_offset": 420 + }, + { + "zone_id": 0, + "m_offset": 1020 + }, + { + "zone_id": 1, + "m_offset": 1140 + }, + { + "zone_id": 0, + "m_offset": 1740 + }, + { + "zone_id": 4, + "m_offset": 1860 + }, + { + "zone_id": 0, + "m_offset": 2460 + }, + { + "zone_id": 1, + "m_offset": 2580 + }, + { + "zone_id": 0, + "m_offset": 3180 + }, + { + "zone_id": 4, + "m_offset": 3300 + }, + { + "zone_id": 0, + "m_offset": 3900 + }, + { + "zone_id": 1, + "m_offset": 4020 + }, + { + "zone_id": 0, + "m_offset": 4620 + }, + { + "zone_id": 4, + "m_offset": 4740 + }, + { + "zone_id": 0, + "m_offset": 5340 + }, + { + "zone_id": 1, + "m_offset": 5460 + }, + { + "zone_id": 0, + "m_offset": 6060 + }, + { + "zone_id": 4, + "m_offset": 6180 + }, + { + "zone_id": 0, + "m_offset": 6780 + }, + { + "zone_id": 1, + "m_offset": 6900 + }, + { + "zone_id": 0, + "m_offset": 7560 + }, + { + "zone_id": 4, + "m_offset": 7680 + }, + { + "zone_id": 0, + "m_offset": 8220 + }, + { + "zone_id": 1, + "m_offset": 8340 + }, + { + "zone_id": 0, + "m_offset": 9000 + }, + { + "zone_id": 4, + "m_offset": 9120 + }, + { + "zone_id": 0, + "m_offset": 9660 + }, + { + "zone_id": 1, + "m_offset": 9780 + } + ], + "zones": [ + { + "name": "Comfort+", + "id": 3, + "type": 8, + "rooms_temp": [ + { + "room_id": "3707962039", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "2042969726", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3497055021", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3435163850", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "873035982", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "1662974901", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "93888250", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "596817675", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3795659199", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "927970817", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "1641945290", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "2102454491", + "therm_setpoint_fp": "comfort" + } + ], + "rooms": [ + { + "id": "3707962039", + "therm_setpoint_fp": "comfort" + }, + { + "id": "2042969726", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3497055021", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3435163850", + "therm_setpoint_fp": "comfort" + }, + { + "id": "873035982", + "therm_setpoint_fp": "comfort" + }, + { + "id": "1662974901", + "therm_setpoint_fp": "comfort" + }, + { + "id": "93888250", + "therm_setpoint_fp": "comfort" + }, + { + "id": "596817675", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3795659199", + "therm_setpoint_fp": "comfort" + }, + { + "id": "927970817", + "therm_setpoint_fp": "comfort" + }, + { + "id": "1641945290", + "therm_setpoint_fp": "comfort" + }, + { + "id": "2102454491", + "therm_setpoint_fp": "comfort" + } + ] + }, + { + "name": "Nuit", + "id": 1, + "type": 1, + "rooms_temp": [ + { + "room_id": "3707962039", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "2042969726", + "therm_setpoint_fp": "away" + }, + { + "room_id": "3497055021", + "therm_setpoint_fp": "away" + }, + { + "room_id": "3435163850", + "therm_setpoint_fp": "away" + }, + { + "room_id": "873035982", + "therm_setpoint_fp": "away" + }, + { + "room_id": "1662974901", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "93888250", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "596817675", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "3795659199", + "therm_setpoint_fp": "away" + }, + { + "room_id": "927970817", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "1641945290", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "2102454491", + "therm_setpoint_fp": "frost_guard" + } + ], + "modules": [], + "rooms": [ + { + "id": "3707962039", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "2042969726", + "therm_setpoint_fp": "away" + }, + { + "id": "3497055021", + "therm_setpoint_fp": "away" + }, + { + "id": "3435163850", + "therm_setpoint_fp": "away" + }, + { + "id": "873035982", + "therm_setpoint_fp": "away" + }, + { + "id": "1662974901", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "93888250", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "596817675", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "3795659199", + "therm_setpoint_fp": "away" + }, + { + "id": "927970817", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "1641945290", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "2102454491", + "therm_setpoint_fp": "frost_guard" + } + ] + }, + { + "name": "Confort", + "id": 0, + "type": 0, + "rooms_temp": [ + { + "room_id": "3707962039", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "2042969726", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3497055021", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3435163850", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "873035982", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "1662974901", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "93888250", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "596817675", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "3795659199", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "927970817", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "1641945290", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "2102454491", + "therm_setpoint_fp": "frost_guard" + } + ], + "modules": [], + "rooms": [ + { + "id": "3707962039", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "2042969726", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3497055021", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3435163850", + "therm_setpoint_fp": "comfort" + }, + { + "id": "873035982", + "therm_setpoint_fp": "comfort" + }, + { + "id": "1662974901", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "93888250", + "therm_setpoint_fp": "comfort" + }, + { + "id": "596817675", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "3795659199", + "therm_setpoint_fp": "comfort" + }, + { + "id": "927970817", + "therm_setpoint_fp": "comfort" + }, + { + "id": "1641945290", + "therm_setpoint_fp": "comfort" + }, + { + "id": "2102454491", + "therm_setpoint_fp": "frost_guard" + } + ] + }, + { + "name": "Éco", + "id": 4, + "type": 5, + "rooms_temp": [ + { + "room_id": "3707962039", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "2042969726", + "therm_setpoint_fp": "away" + }, + { + "room_id": "3497055021", + "therm_setpoint_fp": "away" + }, + { + "room_id": "3435163850", + "therm_setpoint_fp": "away" + }, + { + "room_id": "873035982", + "therm_setpoint_fp": "away" + }, + { + "room_id": "1662974901", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "93888250", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "596817675", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "3795659199", + "therm_setpoint_fp": "away" + }, + { + "room_id": "927970817", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "1641945290", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "2102454491", + "therm_setpoint_fp": "frost_guard" + } + ], + "modules": [], + "rooms": [ + { + "id": "3707962039", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "2042969726", + "therm_setpoint_fp": "away" + }, + { + "id": "3497055021", + "therm_setpoint_fp": "away" + }, + { + "id": "3435163850", + "therm_setpoint_fp": "away" + }, + { + "id": "873035982", + "therm_setpoint_fp": "away" + }, + { + "id": "1662974901", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "93888250", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "596817675", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "3795659199", + "therm_setpoint_fp": "away" + }, + { + "id": "927970817", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "1641945290", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "2102454491", + "therm_setpoint_fp": "frost_guard" + } + ] + } + ], + "name": "Chambres et SDB", + "default": false, + "away_temp": 12, + "hg_temp": 7, + "id": "6413ac548decf9f28b0efc4e", + "type": "therm" + }, + { + "timetable": [ + { + "zone_id": 5, + "m_offset": 0 + }, + { + "zone_id": 2, + "m_offset": 360 + }, + { + "zone_id": 5, + "m_offset": 450 + }, + { + "zone_id": 2, + "m_offset": 1020 + }, + { + "zone_id": 5, + "m_offset": 1080 + }, + { + "zone_id": 2, + "m_offset": 1800 + }, + { + "zone_id": 5, + "m_offset": 1890 + }, + { + "zone_id": 2, + "m_offset": 2460 + }, + { + "zone_id": 5, + "m_offset": 2520 + }, + { + "zone_id": 2, + "m_offset": 3240 + }, + { + "zone_id": 5, + "m_offset": 3330 + }, + { + "zone_id": 2, + "m_offset": 3900 + }, + { + "zone_id": 5, + "m_offset": 3960 + }, + { + "zone_id": 2, + "m_offset": 4680 + }, + { + "zone_id": 5, + "m_offset": 4770 + }, + { + "zone_id": 2, + "m_offset": 5340 + }, + { + "zone_id": 5, + "m_offset": 5400 + }, + { + "zone_id": 2, + "m_offset": 6120 + }, + { + "zone_id": 5, + "m_offset": 6210 + }, + { + "zone_id": 2, + "m_offset": 6780 + }, + { + "zone_id": 5, + "m_offset": 6840 + }, + { + "zone_id": 2, + "m_offset": 7680 + }, + { + "zone_id": 5, + "m_offset": 7740 + }, + { + "zone_id": 2, + "m_offset": 8220 + }, + { + "zone_id": 5, + "m_offset": 8280 + }, + { + "zone_id": 2, + "m_offset": 9120 + }, + { + "zone_id": 5, + "m_offset": 9180 + }, + { + "zone_id": 2, + "m_offset": 9660 + }, + { + "zone_id": 5, + "m_offset": 9720 + } + ], + "zones": [ + { + "name": "Comfort", + "id": 0, + "type": 0, + "rooms_temp": [ + { + "room_id": "3707962039", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "2042969726", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3497055021", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3435163850", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "873035982", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "1662974901", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "93888250", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "596817675", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3795659199", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "927970817", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "1641945290", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "2102454491", + "therm_setpoint_fp": "comfort" + } + ], + "modules": [], + "rooms": [ + { + "id": "3707962039", + "therm_setpoint_fp": "comfort" + }, + { + "id": "2042969726", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3497055021", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3435163850", + "therm_setpoint_fp": "comfort" + }, + { + "id": "873035982", + "therm_setpoint_fp": "comfort" + }, + { + "id": "1662974901", + "therm_setpoint_fp": "comfort" + }, + { + "id": "93888250", + "therm_setpoint_fp": "comfort" + }, + { + "id": "596817675", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3795659199", + "therm_setpoint_fp": "comfort" + }, + { + "id": "927970817", + "therm_setpoint_fp": "comfort" + }, + { + "id": "1641945290", + "therm_setpoint_fp": "comfort" + }, + { + "id": "2102454491", + "therm_setpoint_fp": "comfort" + } + ] + }, + { + "name": "Night", + "id": 1, + "type": 1, + "rooms_temp": [ + { + "room_id": "3707962039", + "therm_setpoint_fp": "away" + }, + { + "room_id": "2042969726", + "therm_setpoint_fp": "away" + }, + { + "room_id": "3497055021", + "therm_setpoint_fp": "away" + }, + { + "room_id": "3435163850", + "therm_setpoint_fp": "away" + }, + { + "room_id": "873035982", + "therm_setpoint_fp": "away" + }, + { + "room_id": "1662974901", + "therm_setpoint_fp": "away" + }, + { + "room_id": "93888250", + "therm_setpoint_fp": "away" + }, + { + "room_id": "596817675", + "therm_setpoint_fp": "away" + }, + { + "room_id": "3795659199", + "therm_setpoint_fp": "away" + }, + { + "room_id": "927970817", + "therm_setpoint_fp": "away" + }, + { + "room_id": "1641945290", + "therm_setpoint_fp": "away" + }, + { + "room_id": "2102454491", + "therm_setpoint_fp": "away" + } + ], + "modules": [], + "rooms": [ + { + "id": "3707962039", + "therm_setpoint_fp": "away" + }, + { + "id": "2042969726", + "therm_setpoint_fp": "away" + }, + { + "id": "3497055021", + "therm_setpoint_fp": "away" + }, + { + "id": "3435163850", + "therm_setpoint_fp": "away" + }, + { + "id": "873035982", + "therm_setpoint_fp": "away" + }, + { + "id": "1662974901", + "therm_setpoint_fp": "away" + }, + { + "id": "93888250", + "therm_setpoint_fp": "away" + }, + { + "id": "596817675", + "therm_setpoint_fp": "away" + }, + { + "id": "3795659199", + "therm_setpoint_fp": "away" + }, + { + "id": "927970817", + "therm_setpoint_fp": "away" + }, + { + "id": "1641945290", + "therm_setpoint_fp": "away" + }, + { + "id": "2102454491", + "therm_setpoint_fp": "away" + } + ] + }, + { + "name": "Comfort+", + "id": 3, + "type": 8, + "rooms_temp": [ + { + "room_id": "3707962039", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "2042969726", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3497055021", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3435163850", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "873035982", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "1662974901", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "93888250", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "596817675", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3795659199", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "927970817", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "1641945290", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "2102454491", + "therm_setpoint_fp": "comfort" + } + ], + "modules": [], + "rooms": [ + { + "id": "3707962039", + "therm_setpoint_fp": "comfort" + }, + { + "id": "2042969726", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3497055021", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3435163850", + "therm_setpoint_fp": "comfort" + }, + { + "id": "873035982", + "therm_setpoint_fp": "comfort" + }, + { + "id": "1662974901", + "therm_setpoint_fp": "comfort" + }, + { + "id": "93888250", + "therm_setpoint_fp": "comfort" + }, + { + "id": "596817675", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3795659199", + "therm_setpoint_fp": "comfort" + }, + { + "id": "927970817", + "therm_setpoint_fp": "comfort" + }, + { + "id": "1641945290", + "therm_setpoint_fp": "comfort" + }, + { + "id": "2102454491", + "therm_setpoint_fp": "comfort" + } + ] + }, + { + "name": "Eco", + "id": 4, + "type": 5, + "rooms_temp": [ + { + "room_id": "3707962039", + "therm_setpoint_fp": "away" + }, + { + "room_id": "2042969726", + "therm_setpoint_fp": "away" + }, + { + "room_id": "3497055021", + "therm_setpoint_fp": "away" + }, + { + "room_id": "3435163850", + "therm_setpoint_fp": "away" + }, + { + "room_id": "873035982", + "therm_setpoint_fp": "away" + }, + { + "room_id": "1662974901", + "therm_setpoint_fp": "away" + }, + { + "room_id": "93888250", + "therm_setpoint_fp": "away" + }, + { + "room_id": "596817675", + "therm_setpoint_fp": "away" + }, + { + "room_id": "3795659199", + "therm_setpoint_fp": "away" + }, + { + "room_id": "927970817", + "therm_setpoint_fp": "away" + }, + { + "room_id": "1641945290", + "therm_setpoint_fp": "away" + }, + { + "room_id": "2102454491", + "therm_setpoint_fp": "away" + } + ], + "modules": [], + "rooms": [ + { + "id": "3707962039", + "therm_setpoint_fp": "away" + }, + { + "id": "2042969726", + "therm_setpoint_fp": "away" + }, + { + "id": "3497055021", + "therm_setpoint_fp": "away" + }, + { + "id": "3435163850", + "therm_setpoint_fp": "away" + }, + { + "id": "873035982", + "therm_setpoint_fp": "away" + }, + { + "id": "1662974901", + "therm_setpoint_fp": "away" + }, + { + "id": "93888250", + "therm_setpoint_fp": "away" + }, + { + "id": "596817675", + "therm_setpoint_fp": "away" + }, + { + "id": "3795659199", + "therm_setpoint_fp": "away" + }, + { + "id": "927970817", + "therm_setpoint_fp": "away" + }, + { + "id": "1641945290", + "therm_setpoint_fp": "away" + }, + { + "id": "2102454491", + "therm_setpoint_fp": "away" + } + ] + }, + { + "name": "SDB seulement", + "id": 2, + "type": 4, + "rooms_temp": [ + { + "room_id": "3707962039", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "2042969726", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "3497055021", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "3435163850", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "873035982", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "1662974901", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "93888250", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "596817675", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3795659199", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "927970817", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "1641945290", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "2102454491", + "therm_setpoint_fp": "frost_guard" + } + ], + "modules": [], + "rooms": [ + { + "id": "3707962039", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "2042969726", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "3497055021", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "3435163850", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "873035982", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "1662974901", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "93888250", + "therm_setpoint_fp": "comfort" + }, + { + "id": "596817675", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3795659199", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "927970817", + "therm_setpoint_fp": "comfort" + }, + { + "id": "1641945290", + "therm_setpoint_fp": "comfort" + }, + { + "id": "2102454491", + "therm_setpoint_fp": "frost_guard" + } + ] + }, + { + "name": "Tout Off", + "id": 5, + "type": 4, + "rooms_temp": [ + { + "room_id": "3707962039", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "2042969726", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "3497055021", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "3435163850", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "873035982", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "1662974901", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "93888250", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "596817675", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3795659199", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "927970817", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "1641945290", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "2102454491", + "therm_setpoint_fp": "frost_guard" + } + ], + "modules": [], + "rooms": [ + { + "id": "3707962039", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "2042969726", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "3497055021", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "3435163850", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "873035982", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "1662974901", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "93888250", + "therm_setpoint_fp": "comfort" + }, + { + "id": "596817675", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3795659199", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "927970817", + "therm_setpoint_fp": "comfort" + }, + { + "id": "1641945290", + "therm_setpoint_fp": "comfort" + }, + { + "id": "2102454491", + "therm_setpoint_fp": "frost_guard" + } + ] + }, + { + "name": "Chambres Confort and All Off", + "id": 6, + "type": 4, + "rooms_temp": [ + { + "room_id": "1662974901", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "2042969726", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3435163850", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3497055021", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3707962039", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "3795659199", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "596817675", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "873035982", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "93888250", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "927970817", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "1641945290", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "2102454491", + "therm_setpoint_fp": "frost_guard" + } + ], + "modules": [], + "rooms": [ + { + "id": "1662974901", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "2042969726", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3435163850", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3497055021", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3707962039", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "3795659199", + "therm_setpoint_fp": "comfort" + }, + { + "id": "596817675", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "873035982", + "therm_setpoint_fp": "comfort" + }, + { + "id": "93888250", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "927970817", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "1641945290", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "2102454491", + "therm_setpoint_fp": "frost_guard" + } + ] + }, + { + "name": "Chambres Eco and all Off", + "id": 7, + "type": 4, + "rooms_temp": [ + { + "room_id": "1662974901", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "2042969726", + "therm_setpoint_fp": "away" + }, + { + "room_id": "3435163850", + "therm_setpoint_fp": "away" + }, + { + "room_id": "3497055021", + "therm_setpoint_fp": "away" + }, + { + "room_id": "3707962039", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3795659199", + "therm_setpoint_fp": "away" + }, + { + "room_id": "596817675", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "873035982", + "therm_setpoint_fp": "away" + }, + { + "room_id": "93888250", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "927970817", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "1641945290", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "2102454491", + "therm_setpoint_fp": "comfort" + } + ], + "modules": [], + "rooms": [ + { + "id": "1662974901", + "therm_setpoint_fp": "comfort" + }, + { + "id": "2042969726", + "therm_setpoint_fp": "away" + }, + { + "id": "3435163850", + "therm_setpoint_fp": "away" + }, + { + "id": "3497055021", + "therm_setpoint_fp": "away" + }, + { + "id": "3707962039", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3795659199", + "therm_setpoint_fp": "away" + }, + { + "id": "596817675", + "therm_setpoint_fp": "comfort" + }, + { + "id": "873035982", + "therm_setpoint_fp": "away" + }, + { + "id": "93888250", + "therm_setpoint_fp": "comfort" + }, + { + "id": "927970817", + "therm_setpoint_fp": "comfort" + }, + { + "id": "1641945290", + "therm_setpoint_fp": "comfort" + }, + { + "id": "2102454491", + "therm_setpoint_fp": "comfort" + } + ] + }, + { + "name": "Chambres Eco SDB Confort All OFF", + "id": 8, + "type": 4, + "rooms_temp": [ + { + "room_id": "1662974901", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "2042969726", + "therm_setpoint_fp": "away" + }, + { + "room_id": "3435163850", + "therm_setpoint_fp": "away" + }, + { + "room_id": "3497055021", + "therm_setpoint_fp": "away" + }, + { + "room_id": "3707962039", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "3795659199", + "therm_setpoint_fp": "away" + }, + { + "room_id": "596817675", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "873035982", + "therm_setpoint_fp": "away" + }, + { + "room_id": "93888250", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "927970817", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "1641945290", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "2102454491", + "therm_setpoint_fp": "frost_guard" + } + ], + "modules": [], + "rooms": [ + { + "id": "1662974901", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "2042969726", + "therm_setpoint_fp": "away" + }, + { + "id": "3435163850", + "therm_setpoint_fp": "away" + }, + { + "id": "3497055021", + "therm_setpoint_fp": "away" + }, + { + "id": "3707962039", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "3795659199", + "therm_setpoint_fp": "away" + }, + { + "id": "596817675", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "873035982", + "therm_setpoint_fp": "away" + }, + { + "id": "93888250", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "927970817", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "1641945290", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "2102454491", + "therm_setpoint_fp": "frost_guard" + } + ] + }, + { + "name": "Chambres & SDB Confort All OFF", + "id": 9, + "type": 4, + "rooms_temp": [ + { + "room_id": "1662974901", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "2042969726", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3435163850", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3497055021", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3707962039", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "3795659199", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "596817675", + "therm_setpoint_fp": "frost_guard" + }, + { + "room_id": "873035982", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "93888250", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "927970817", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "1641945290", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "2102454491", + "therm_setpoint_fp": "frost_guard" + } + ], + "modules": [], + "rooms": [ + { + "id": "1662974901", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "2042969726", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3435163850", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3497055021", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3707962039", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "3795659199", + "therm_setpoint_fp": "comfort" + }, + { + "id": "596817675", + "therm_setpoint_fp": "frost_guard" + }, + { + "id": "873035982", + "therm_setpoint_fp": "comfort" + }, + { + "id": "93888250", + "therm_setpoint_fp": "comfort" + }, + { + "id": "927970817", + "therm_setpoint_fp": "comfort" + }, + { + "id": "1641945290", + "therm_setpoint_fp": "comfort" + }, + { + "id": "2102454491", + "therm_setpoint_fp": "frost_guard" + } + ] + } + ], + "name": "SDM seulement", + "default": false, + "away_temp": 12, + "hg_temp": 7, + "id": "65428fdc7349fc4e49034381", + "type": "therm" + }, + { + "timetable": [ + { + "zone_id": 1, + "m_offset": 300 + }, + { + "zone_id": 3, + "m_offset": 540 + }, + { + "zone_id": 2, + "m_offset": 960 + }, + { + "zone_id": 0, + "m_offset": 1020 + }, + { + "zone_id": 1, + "m_offset": 1740 + }, + { + "zone_id": 3, + "m_offset": 1980 + }, + { + "zone_id": 2, + "m_offset": 2400 + }, + { + "zone_id": 0, + "m_offset": 2460 + }, + { + "zone_id": 1, + "m_offset": 3180 + }, + { + "zone_id": 3, + "m_offset": 3420 + }, + { + "zone_id": 2, + "m_offset": 3840 + }, + { + "zone_id": 0, + "m_offset": 3900 + }, + { + "zone_id": 1, + "m_offset": 4620 + }, + { + "zone_id": 3, + "m_offset": 4860 + }, + { + "zone_id": 2, + "m_offset": 5280 + }, + { + "zone_id": 0, + "m_offset": 5340 + }, + { + "zone_id": 1, + "m_offset": 6060 + }, + { + "zone_id": 3, + "m_offset": 6300 + }, + { + "zone_id": 2, + "m_offset": 6720 + }, + { + "zone_id": 0, + "m_offset": 6780 + }, + { + "zone_id": 1, + "m_offset": 7500 + }, + { + "zone_id": 3, + "m_offset": 7740 + }, + { + "zone_id": 2, + "m_offset": 8160 + }, + { + "zone_id": 0, + "m_offset": 8220 + }, + { + "zone_id": 1, + "m_offset": 8940 + }, + { + "zone_id": 3, + "m_offset": 9180 + }, + { + "zone_id": 2, + "m_offset": 9600 + }, + { + "zone_id": 0, + "m_offset": 9660 + } + ], + "zones": [ + { + "id": 0, + "modules": [ + { + "id": "98:76:54:32:10:00:00:67", + "bridge": "aa:aa:aa:aa:aa:aa", + "on": false + } + ] + }, + { + "id": 1, + "modules": [ + { + "id": "98:76:54:32:10:00:00:67", + "bridge": "aa:aa:aa:aa:aa:aa", + "on": true + } + ] + }, + { + "id": 2, + "modules": [ + { + "id": "98:76:54:32:10:00:00:67", + "bridge": "aa:aa:aa:aa:aa:aa", + "on": true + } + ] + }, + { + "id": 3, + "modules": [ + { + "id": "98:76:54:32:10:00:00:67", + "bridge": "aa:aa:aa:aa:aa:aa", + "on": false + } + ] + } + ], + "name": "Planning d'actions", + "default": false, + "timetable_sunrise": [], + "timetable_sunset": [], + "id": "64fa4a1266404bbb130f4b71", + "type": "event", + "selected": true + }, + { + "timetable": [], + "zones": [], + "name": "Hiver", + "default": false, + "timetable_sunrise": [], + "timetable_sunset": [], + "id": "65c3f82f61e0a3ec5401640f", + "type": "event" + }, + { + "timetable": [ + { + "zone_id": 0, + "m_offset": 0 + }, + { + "zone_id": 1, + "m_offset": 130 + }, + { + "zone_id": 0, + "m_offset": 430 + }, + { + "zone_id": 1, + "m_offset": 850 + }, + { + "zone_id": 0, + "m_offset": 1030 + }, + { + "zone_id": 1, + "m_offset": 1570 + }, + { + "zone_id": 0, + "m_offset": 1870 + }, + { + "zone_id": 1, + "m_offset": 2290 + }, + { + "zone_id": 0, + "m_offset": 2470 + }, + { + "zone_id": 1, + "m_offset": 3010 + }, + { + "zone_id": 0, + "m_offset": 3310 + }, + { + "zone_id": 1, + "m_offset": 3730 + }, + { + "zone_id": 0, + "m_offset": 3910 + }, + { + "zone_id": 1, + "m_offset": 4450 + }, + { + "zone_id": 0, + "m_offset": 4750 + }, + { + "zone_id": 1, + "m_offset": 5170 + }, + { + "zone_id": 0, + "m_offset": 5350 + }, + { + "zone_id": 1, + "m_offset": 5890 + }, + { + "zone_id": 0, + "m_offset": 6190 + }, + { + "zone_id": 1, + "m_offset": 6610 + }, + { + "zone_id": 0, + "m_offset": 6790 + }, + { + "zone_id": 1, + "m_offset": 7330 + }, + { + "zone_id": 0, + "m_offset": 7630 + }, + { + "zone_id": 1, + "m_offset": 8050 + }, + { + "zone_id": 0, + "m_offset": 8230 + }, + { + "zone_id": 1, + "m_offset": 8770 + }, + { + "zone_id": 0, + "m_offset": 9070 + }, + { + "zone_id": 1, + "m_offset": 9490 + }, + { + "zone_id": 0, + "m_offset": 9670 + } + ], + "zones": [ + { + "id": 0, + "price": 0.27, + "price_type": "peak" + }, + { + "id": 1, + "price": 0.2068, + "price_type": "off_peak" + } + ], + "name": "electricity", + "default": false, + "tariff": "edf_tarif_bleu", + "tariff_option": "peak_and_off_peak", + "power_threshold": 36, + "contract_power_unit": "kW", + "id": "60ce3d057a24d640444c4f1c", + "type": "electricity", + "selected": true + } + ] + }, + { + "id": "eeeeeeeeeffffffffffaaaaa", + "name": "A SECOND HOME", + "altitude": 200, + "coordinates": [ + 34.345576, + 89.667112 + ], + "country": "FR", + "timezone": "Europe/Paris", + "rooms": [ + { + "id": "1468717414", + "name": "Cave", + "type": "garage", + "module_ids": [ + "12:34:56:78:90:00:00:09", + "12:34:56:78:90:00:00:20", + "12:34:56:78:90:00:00:33" + ] + }, + { + "id": "738709350", + "name": "Salon", + "type": "livingroom", + "module_ids": [ + "bb:bb:bb:bb:bb:bb", + "12:34:56:78:90:00:00:01", + "12:34:56:78:90:00:00:02", + "12:34:56:78:90:00:00:03", + "12:34:56:78:90:00:00:04", + "12:34:56:78:90:00:00:05", + "12:34:56:78:90:00:00:06", + "12:34:56:78:90:00:00:07", + "12:34:56:78:90:00:00:08", + "12:34:56:78:90:00:00:10", + "12:34:56:78:90:00:00:23", + "12:34:56:78:90:00:00:24", + "12:34:56:78:90:00:00:29", + "12:34:56:78:90:00:00:30" + ] + }, + { + "id": "3098890768", + "name": "Entrée", + "type": "lobby", + "module_ids": [ + "12:34:56:78:90:00:00:11" + ] + }, + { + "id": "70754041", + "name": "Salle de bains", + "type": "bathroom", + "module_ids": [ + "12:34:56:78:90:00:00:12", + "12:34:56:78:90:00:00:26" + ] + }, + { + "id": "1761556353", + "name": "Nouveaux toilettes", + "type": "toilets", + "module_ids": [ + "12:34:56:78:90:00:00:13" + ] + }, + { + "id": "968846272", + "name": "Anciens Toilettes", + "type": "toilets", + "module_ids": [ + "12:34:56:78:90:00:00:14" + ] + }, + { + "id": "4008422910", + "name": "Palier", + "type": "corridor", + "module_ids": [ + "12:34:56:78:90:00:00:15", + "12:34:56:78:90:00:00:18", + "12:34:56:78:90:00:00:31" + ] + }, + { + "id": "2640531479", + "name": "Chambre parents", + "type": "bedroom", + "module_ids": [ + "12:34:56:78:90:00:00:16", + "12:34:56:78:90:00:00:27" + ] + }, + { + "id": "2667868658", + "name": "Chambre Older", + "type": "bedroom", + "module_ids": [ + "12:34:56:78:90:00:00:17", + "12:34:56:78:90:00:00:22" + ] + }, + { + "id": "3783425301", + "name": "Chambre Enfants", + "type": "bedroom", + "module_ids": [ + "12:34:56:78:90:00:00:19", + "12:34:56:78:90:00:00:28" + ] + }, + { + "id": "68319658", + "name": "Chambre Old", + "type": "bedroom", + "module_ids": [ + "12:34:56:78:90:00:00:25" + ] + }, + { + "id": "1156588698", + "name": "Jardin", + "type": "outdoor", + "module_ids": [ + "12:34:56:78:90:00:00:21" + ] + }, + { + "id": "1229033409", + "name": "Grenier", + "type": "custom", + "module_ids": [ + "12:34:56:78:90:00:00:32", + "12:34:56:78:90:00:00:34" + ] + } + ], + "modules": [ + { + "id": "bb:bb:bb:bb:bb:bb", + "type": "NLG", + "name": "Legrand Gateway", + "setup_date": 1692558574, + "room_id": "738709350", + "modules_bridged": [ + "12:34:56:78:90:00:00:01", + "12:34:56:78:90:00:00:02", + "12:34:56:78:90:00:00:03", + "12:34:56:78:90:00:00:04", + "12:34:56:78:90:00:00:05", + "12:34:56:78:90:00:00:06", + "12:34:56:78:90:00:00:07", + "12:34:56:78:90:00:00:08", + "12:34:56:78:90:00:00:09", + "12:34:56:78:90:00:00:10", + "12:34:56:78:90:00:00:11", + "12:34:56:78:90:00:00:12", + "12:34:56:78:90:00:00:13", + "12:34:56:78:90:00:00:14", + "12:34:56:78:90:00:00:15", + "12:34:56:78:90:00:00:16", + "12:34:56:78:90:00:00:17", + "12:34:56:78:90:00:00:18", + "12:34:56:78:90:00:00:19", + "12:34:56:78:90:00:00:20", + "12:34:56:78:90:00:00:21", + "12:34:56:78:90:00:00:22", + "12:34:56:78:90:00:00:23", + "12:34:56:78:90:00:00:24", + "12:34:56:78:90:00:00:25", + "12:34:56:78:90:00:00:26", + "12:34:56:78:90:00:00:27", + "12:34:56:78:90:00:00:28", + "12:34:56:78:90:00:00:29", + "12:34:56:78:90:00:00:30", + "12:34:56:78:90:00:00:31", + "12:34:56:78:90:00:00:32", + "12:34:56:78:90:00:00:33", + "12:34:56:78:90:00:00:34" + ] + }, + { + "id": "12:34:56:78:90:00:00:01", + "type": "NLAO", + "name": "Commande On/Off (Sans-Fil, Sans Pile) 1", + "setup_date": 1692558628, + "room_id": "738709350", + "bridge": "bb:bb:bb:bb:bb:bb" + }, + { + "id": "12:34:56:78:90:00:00:02", + "type": "NLAO", + "name": "Commande On/Off (Sans-Fil, Sans Pile) 2", + "setup_date": 1692558628, + "room_id": "738709350", + "bridge": "bb:bb:bb:bb:bb:bb" + }, + { + "id": "12:34:56:78:90:00:00:03", + "type": "NLAO", + "name": "Commande On/Off (Sans-Fil, Sans Pile) 3", + "setup_date": 1692558628, + "room_id": "738709350", + "bridge": "bb:bb:bb:bb:bb:bb" + }, + { + "id": "12:34:56:78:90:00:00:04", + "type": "NLAO", + "name": "Commande On/Off (Sans-Fil, Sans Pile) 4", + "setup_date": 1692558628, + "room_id": "738709350", + "bridge": "bb:bb:bb:bb:bb:bb" + }, + { + "id": "12:34:56:78:90:00:00:05", + "type": "NLAO", + "name": "Commande On/Off (Sans-Fil, Sans Pile) 5", + "setup_date": 1692558628, + "room_id": "738709350", + "bridge": "bb:bb:bb:bb:bb:bb" + }, + { + "id": "12:34:56:78:90:00:00:06", + "type": "NLAO", + "name": "Commande On/Off (Sans-Fil, Sans Pile) 6", + "setup_date": 1692558628, + "room_id": "738709350", + "bridge": "bb:bb:bb:bb:bb:bb" + }, + { + "id": "12:34:56:78:90:00:00:07", + "type": "NLAO", + "name": "Commande On/Off (Sans-Fil, Sans Pile) 7", + "setup_date": 1692558628, + "room_id": "738709350", + "bridge": "bb:bb:bb:bb:bb:bb" + }, + { + "id": "12:34:56:78:90:00:00:08", + "type": "NLAO", + "name": "Commande On/Off (Sans-Fil, Sans Pile) 8", + "setup_date": 1692558628, + "room_id": "738709350", + "bridge": "bb:bb:bb:bb:bb:bb" + }, + { + "id": "12:34:56:78:90:00:00:09", + "type": "NLPO", + "name": "OL Cumulus", + "setup_date": 1692558628, + "room_id": "1468717414", + "bridge": "bb:bb:bb:bb:bb:bb", + "appliance_type": "water_heater" + }, + { + "id": "12:34:56:78:90:00:00:10", + "type": "NLPT", + "name": "OL Lumière Cuisine", + "setup_date": 1692558628, + "room_id": "738709350", + "bridge": "bb:bb:bb:bb:bb:bb" + }, + { + "id": "12:34:56:78:90:00:00:11", + "type": "NLPT", + "name": "OL Escalier", + "setup_date": 1692558628, + "room_id": "3098890768", + "bridge": "bb:bb:bb:bb:bb:bb" + }, + { + "id": "12:34:56:78:90:00:00:12", + "type": "NLPT", + "name": "OL Éclairage Salle de bain", + "setup_date": 1692558628, + "room_id": "70754041", + "bridge": "bb:bb:bb:bb:bb:bb" + }, + { + "id": "12:34:56:78:90:00:00:13", + "type": "NLPT", + "name": "OL Lumière nouveaux toilettes", + "setup_date": 1692558628, + "room_id": "1761556353", + "bridge": "bb:bb:bb:bb:bb:bb" + }, + { + "id": "12:34:56:78:90:00:00:14", + "type": "NLPT", + "name": "OL Lumière anciens toilettes", + "setup_date": 1692558628, + "room_id": "968846272", + "bridge": "bb:bb:bb:bb:bb:bb" + }, + { + "id": "12:34:56:78:90:00:00:15", + "type": "NLPT", + "name": "OL Lumière palier salle de bain", + "setup_date": 1692558628, + "room_id": "4008422910", + "bridge": "bb:bb:bb:bb:bb:bb" + }, + { + "id": "12:34:56:78:90:00:00:16", + "type": "NLPT", + "name": "OL Lumière chambre A", + "setup_date": 1692558628, + "room_id": "2640531479", + "bridge": "bb:bb:bb:bb:bb:bb" + }, + { + "id": "12:34:56:78:90:00:00:17", + "type": "NLPT", + "name": "OL Lumière Chambre Older", + "setup_date": 1692558628, + "room_id": "2667868658", + "bridge": "bb:bb:bb:bb:bb:bb" + }, + { + "id": "12:34:56:78:90:00:00:18", + "type": "NLPT", + "name": "OL Lumière Palier", + "setup_date": 1692558628, + "room_id": "4008422910", + "bridge": "bb:bb:bb:bb:bb:bb" + }, + { + "id": "12:34:56:78:90:00:00:19", + "type": "NLPT", + "name": "OL Lumière enfants", + "setup_date": 1692558628, + "room_id": "3783425301", + "bridge": "bb:bb:bb:bb:bb:bb" + }, + { + "id": "12:34:56:78:90:00:00:20", + "type": "NLM", + "name": "OL Lumière Cave", + "setup_date": 1692643741, + "room_id": "1468717414", + "bridge": "bb:bb:bb:bb:bb:bb" + }, + { + "id": "12:34:56:78:90:00:00:21", + "type": "NLM", + "name": "OL Lumière Jardin", + "setup_date": 1692643763, + "room_id": "1156588698", + "bridge": "bb:bb:bb:bb:bb:bb" + }, + { + "id": "12:34:56:78:90:00:00:22", + "type": "NLC", + "name": "OL Radiateur Chambre Older", + "setup_date": 1692644489, + "room_id": "2667868658", + "bridge": "bb:bb:bb:bb:bb:bb", + "appliance_type": "radiator" + }, + { + "id": "12:34:56:78:90:00:00:23", + "type": "NLC", + "name": "OL Radiateur Salon Fenêtre", + "setup_date": 1692644493, + "room_id": "738709350", + "bridge": "bb:bb:bb:bb:bb:bb", + "appliance_type": "radiator" + }, + { + "id": "12:34:56:78:90:00:00:24", + "type": "NLC", + "name": "OL Radiateur Salon Porte", + "setup_date": 1692644497, + "room_id": "738709350", + "bridge": "bb:bb:bb:bb:bb:bb", + "appliance_type": "radiator" + }, + { + "id": "12:34:56:78:90:00:00:25", + "type": "NLC", + "name": "OL Radiateur Chambre Old", + "setup_date": 1692644501, + "room_id": "68319658", + "bridge": "bb:bb:bb:bb:bb:bb", + "appliance_type": "radiator" + }, + { + "id": "12:34:56:78:90:00:00:26", + "type": "NLC", + "name": "OL Radiateur Serviette", + "setup_date": 1692644506, + "room_id": "70754041", + "bridge": "bb:bb:bb:bb:bb:bb", + "appliance_type": "radiator" + }, + { + "id": "12:34:56:78:90:00:00:27", + "type": "NLC", + "name": "OL Radiateur Chambre A", + "setup_date": 1692644509, + "room_id": "2640531479", + "bridge": "bb:bb:bb:bb:bb:bb", + "appliance_type": "radiator" + }, + { + "id": "12:34:56:78:90:00:00:28", + "type": "NLC", + "name": "OL Radiateur Chambre E", + "setup_date": 1693159267, + "room_id": "3783425301", + "bridge": "bb:bb:bb:bb:bb:bb", + "appliance_type": "radiator" + }, + { + "id": "12:34:56:78:90:00:00:29", + "type": "NLPT", + "name": "OL Salle à manger", + "setup_date": 1696921179, + "room_id": "738709350", + "bridge": "bb:bb:bb:bb:bb:bb" + }, + { + "id": "12:34:56:78:90:00:00:30", + "type": "NLPT", + "name": "OL Plan de travail", + "setup_date": 1696921199, + "room_id": "738709350", + "bridge": "bb:bb:bb:bb:bb:bb" + }, + { + "id": "12:34:56:78:90:00:00:31", + "type": "NLAO", + "name": "Commande On/Off (Sans-Fil, Sans Pile) 9", + "setup_date": 1696922170, + "room_id": "4008422910", + "bridge": "bb:bb:bb:bb:bb:bb" + }, + { + "id": "12:34:56:78:90:00:00:32", + "type": "NLPT", + "name": "OL Lumière Grenier", + "setup_date": 1696949709, + "room_id": "1229033409", + "bridge": "bb:bb:bb:bb:bb:bb" + }, + { + "id": "12:34:56:78:90:00:00:33", + "type": "NLAO", + "name": "Commande Cave", + "setup_date": 1696949890, + "room_id": "1468717414", + "bridge": "bb:bb:bb:bb:bb:bb" + }, + { + "id": "12:34:56:78:90:00:00:34", + "type": "NLAO", + "name": "Commande Grenier", + "setup_date": 1696951661, + "room_id": "1229033409", + "bridge": "bb:bb:bb:bb:bb:bb" + } + ], + "temperature_control_mode": "heating", + "therm_mode": "hg", + "therm_setpoint_default_duration": 180, + "schedules": [ + { + "timetable": [ + { + "zone_id": 1, + "m_offset": 0 + }, + { + "zone_id": 0, + "m_offset": 420 + }, + { + "zone_id": 4, + "m_offset": 480 + }, + { + "zone_id": 0, + "m_offset": 1140 + }, + { + "zone_id": 1, + "m_offset": 1320 + }, + { + "zone_id": 0, + "m_offset": 1860 + }, + { + "zone_id": 4, + "m_offset": 1920 + }, + { + "zone_id": 0, + "m_offset": 2580 + }, + { + "zone_id": 1, + "m_offset": 2760 + }, + { + "zone_id": 0, + "m_offset": 3300 + }, + { + "zone_id": 4, + "m_offset": 3360 + }, + { + "zone_id": 0, + "m_offset": 4020 + }, + { + "zone_id": 1, + "m_offset": 4200 + }, + { + "zone_id": 0, + "m_offset": 4740 + }, + { + "zone_id": 4, + "m_offset": 4800 + }, + { + "zone_id": 0, + "m_offset": 5460 + }, + { + "zone_id": 1, + "m_offset": 5640 + }, + { + "zone_id": 0, + "m_offset": 6180 + }, + { + "zone_id": 4, + "m_offset": 6240 + }, + { + "zone_id": 0, + "m_offset": 6900 + }, + { + "zone_id": 1, + "m_offset": 7080 + }, + { + "zone_id": 0, + "m_offset": 7620 + }, + { + "zone_id": 4, + "m_offset": 7800 + }, + { + "zone_id": 0, + "m_offset": 8220 + }, + { + "zone_id": 1, + "m_offset": 8520 + }, + { + "zone_id": 0, + "m_offset": 9060 + }, + { + "zone_id": 4, + "m_offset": 9240 + }, + { + "zone_id": 0, + "m_offset": 9660 + }, + { + "zone_id": 1, + "m_offset": 9960 + } + ], + "zones": [ + { + "name": "Comfort", + "id": 0, + "type": 0, + "rooms_temp": [ + { + "room_id": "738709350", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "70754041", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "2640531479", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "2667868658", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3783425301", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "68319658", + "therm_setpoint_fp": "comfort" + } + ], + "modules": [], + "rooms": [ + { + "id": "738709350", + "therm_setpoint_fp": "comfort" + }, + { + "id": "70754041", + "therm_setpoint_fp": "comfort" + }, + { + "id": "2640531479", + "therm_setpoint_fp": "comfort" + }, + { + "id": "2667868658", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3783425301", + "therm_setpoint_fp": "comfort" + }, + { + "id": "68319658", + "therm_setpoint_fp": "comfort" + } + ] + }, + { + "name": "Night", + "id": 1, + "type": 1, + "rooms_temp": [ + { + "room_id": "738709350", + "therm_setpoint_fp": "away" + }, + { + "room_id": "70754041", + "therm_setpoint_fp": "away" + }, + { + "room_id": "2640531479", + "therm_setpoint_fp": "away" + }, + { + "room_id": "2667868658", + "therm_setpoint_fp": "away" + }, + { + "room_id": "3783425301", + "therm_setpoint_fp": "away" + }, + { + "room_id": "68319658", + "therm_setpoint_fp": "away" + } + ], + "modules": [], + "rooms": [ + { + "id": "738709350", + "therm_setpoint_fp": "away" + }, + { + "id": "70754041", + "therm_setpoint_fp": "away" + }, + { + "id": "2640531479", + "therm_setpoint_fp": "away" + }, + { + "id": "2667868658", + "therm_setpoint_fp": "away" + }, + { + "id": "3783425301", + "therm_setpoint_fp": "away" + }, + { + "id": "68319658", + "therm_setpoint_fp": "away" + } + ] + }, + { + "name": "Comfort+", + "id": 3, + "type": 8, + "rooms_temp": [ + { + "room_id": "738709350", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "70754041", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "2640531479", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "2667868658", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "3783425301", + "therm_setpoint_fp": "comfort" + }, + { + "room_id": "68319658", + "therm_setpoint_fp": "comfort" + } + ], + "modules": [], + "rooms": [ + { + "id": "738709350", + "therm_setpoint_fp": "comfort" + }, + { + "id": "70754041", + "therm_setpoint_fp": "comfort" + }, + { + "id": "2640531479", + "therm_setpoint_fp": "comfort" + }, + { + "id": "2667868658", + "therm_setpoint_fp": "comfort" + }, + { + "id": "3783425301", + "therm_setpoint_fp": "comfort" + }, + { + "id": "68319658", + "therm_setpoint_fp": "comfort" + } + ] + }, + { + "name": "Eco", + "id": 4, + "type": 5, + "rooms_temp": [ + { + "room_id": "738709350", + "therm_setpoint_fp": "away" + }, + { + "room_id": "70754041", + "therm_setpoint_fp": "away" + }, + { + "room_id": "2640531479", + "therm_setpoint_fp": "away" + }, + { + "room_id": "2667868658", + "therm_setpoint_fp": "away" + }, + { + "room_id": "3783425301", + "therm_setpoint_fp": "away" + }, + { + "room_id": "68319658", + "therm_setpoint_fp": "away" + } + ], + "modules": [], + "rooms": [ + { + "id": "738709350", + "therm_setpoint_fp": "away" + }, + { + "id": "70754041", + "therm_setpoint_fp": "away" + }, + { + "id": "2640531479", + "therm_setpoint_fp": "away" + }, + { + "id": "2667868658", + "therm_setpoint_fp": "away" + }, + { + "id": "3783425301", + "therm_setpoint_fp": "away" + }, + { + "id": "68319658", + "therm_setpoint_fp": "away" + } + ] + } + ], + "name": "Mon planning de chauffe", + "default": false, + "away_temp": 12, + "hg_temp": 7, + "id": "64e3b6102853d9405304b27a", + "type": "therm", + "selected": true + }, + { + "timetable": [], + "zones": [], + "name": "Planning d'actions", + "default": false, + "timetable_sunrise": [], + "timetable_sunset": [], + "id": "64ee68e2c8419b37790c5050", + "type": "event", + "selected": true + }, + { + "timetable": [ + { + "zone_id": 0, + "m_offset": 0 + } + ], + "zones": [ + { + "id": 0, + "price": 0.2516, + "price_type": "basic" + } + ], + "name": "electricity", + "default": false, + "tariff": "edf_tarif_bleu", + "tariff_option": "basic", + "power_threshold": 9, + "contract_power_unit": "kVA", + "id": "64e28390bc6d555f2d05c684", + "type": "electricity", + "selected": true + } + ] + } + ], + "user": { + "email": "john.doe@doe.com", + "language": "fr-FR", + "locale": "fr-FR", + "feel_like_algorithm": 0, + "unit_pressure": 0, + "unit_system": 0, + "unit_wind": 0, + "id": "111111111112222222333333" + } + }, + "status": "ok", + "time_exec": 0.07427000999450684, + "time_server": 1709762264 +} \ No newline at end of file diff --git a/src/pyatmo/__init__.py b/src/pyatmo/__init__.py index 4ef12ea8..ca3a8a89 100644 --- a/src/pyatmo/__init__.py +++ b/src/pyatmo/__init__.py @@ -3,7 +3,15 @@ from pyatmo import const, modules from pyatmo.account import AsyncAccount from pyatmo.auth import AbstractAsyncAuth -from pyatmo.exceptions import ApiError, InvalidHome, InvalidRoom, NoDevice, NoSchedule +from pyatmo.exceptions import ( + ApiError, + ApiErrorThrottling, + ApiHomeReachabilityError, + InvalidHome, + InvalidRoom, + NoDevice, + NoSchedule, +) from pyatmo.home import Home from pyatmo.modules import Module from pyatmo.modules.device_types import DeviceType @@ -12,6 +20,8 @@ __all__ = [ "AbstractAsyncAuth", "ApiError", + "ApiErrorThrottling", + "ApiHomeReachabilityError", "AsyncAccount", "InvalidHome", "InvalidRoom", diff --git a/src/pyatmo/account.py b/src/pyatmo/account.py index 69dad18a..e4a2a158 100644 --- a/src/pyatmo/account.py +++ b/src/pyatmo/account.py @@ -16,11 +16,13 @@ GETSTATIONDATA_ENDPOINT, HOME, SETSTATE_ENDPOINT, + MeasureInterval, RawData, ) +from pyatmo.exceptions import ApiHomeReachabilityError from pyatmo.helpers import extract_raw_data from pyatmo.home import Home -from pyatmo.modules.module import MeasureInterval, Module +from pyatmo.modules.module import Module if TYPE_CHECKING: from pyatmo.auth import AbstractAsyncAuth @@ -36,6 +38,7 @@ def __init__(self, auth: AbstractAsyncAuth, favorite_stations: bool = True) -> N self.auth: AbstractAsyncAuth = auth self.user: str | None = None + self.all_account_homes_id: dict[str, str] = {} self.homes: dict[str, Home] = {} self.raw_data: RawData = {} self.favorite_stations: bool = favorite_stations @@ -49,16 +52,28 @@ def __repr__(self) -> str: f"{self.__class__.__name__}(user={self.user}, home_ids={self.homes.keys()}" ) - def process_topology(self) -> None: + def process_topology(self, disabled_homes_ids: list[str] | None = None) -> None: """Process topology information from /homesdata.""" for home in self.raw_data["homes"]: - if (home_id := home["id"]) in self.homes: + + home_id = home.get("id", "Unknown") + home_name = home.get("name", "Unknown") + self.all_account_homes_id[home_id] = home_name + + if disabled_homes_ids and home_id in disabled_homes_ids: + if home_id in self.homes: + del self.homes[home_id] + continue + + if home_id in self.homes: self.homes[home_id].update_topology(home) else: self.homes[home_id] = Home(self.auth, raw_data=home) - async def async_update_topology(self) -> None: + async def async_update_topology( + self, disabled_homes_ids: list[str] | None = None + ) -> int: """Retrieve topology data from /homesdata.""" resp = await self.auth.async_post_api_request( @@ -68,19 +83,39 @@ async def async_update_topology(self) -> None: self.user = self.raw_data.get("user", {}).get("email") - self.process_topology() + self.process_topology(disabled_homes_ids=disabled_homes_ids) - async def async_update_status(self, home_id: str) -> None: - """Retrieve status data from /homestatus.""" - resp = await self.auth.async_post_api_request( - endpoint=GETHOMESTATUS_ENDPOINT, - params={"home_id": home_id}, - ) - raw_data = extract_raw_data(await resp.json(), HOME) - await self.homes[home_id].update(raw_data) + return 1 + + async def async_update_status(self, home_id: str | None = None) -> int: + """Retrieve status data from /homestatus. Returns the number of performed API calls.""" - async def async_update_events(self, home_id: str) -> None: - """Retrieve events from /getevents.""" + if home_id is None: + homes = self.homes + else: + homes = [home_id] + num_calls = 0 + all_homes_ok = True + for h_id in homes: + resp = await self.auth.async_post_api_request( + endpoint=GETHOMESTATUS_ENDPOINT, + params={"home_id": h_id}, + ) + raw_data = extract_raw_data(await resp.json(), HOME) + is_correct_update = await self.homes[h_id].update(raw_data) + if not is_correct_update: + all_homes_ok = False + num_calls += 1 + + if all_homes_ok is False: + raise ApiHomeReachabilityError( + "No Home update could be performed, all modules unreachable and not updated", + ) + + return num_calls + + async def async_update_events(self, home_id: str) -> int: + """Retrieve events from /getevents. Returns the number of performed API calls.""" resp = await self.auth.async_post_api_request( endpoint=GETEVENTS_ENDPOINT, params={"home_id": home_id}, @@ -88,33 +123,43 @@ async def async_update_events(self, home_id: str) -> None: raw_data = extract_raw_data(await resp.json(), HOME) await self.homes[home_id].update(raw_data) - async def async_update_weather_stations(self) -> None: - """Retrieve status data from /getstationsdata.""" + return 1 + + async def async_update_weather_stations(self) -> int: + """Retrieve status data from /getstationsdata. Returns the number of performed API calls.""" params = {"get_favorites": ("true" if self.favorite_stations else "false")} await self._async_update_data( GETSTATIONDATA_ENDPOINT, params=params, ) + return 1 - async def async_update_air_care(self) -> None: - """Retrieve status data from /gethomecoachsdata.""" + async def async_update_air_care(self) -> int: + """Retrieve status data from /gethomecoachsdata. Returns the number of performed API calls.""" await self._async_update_data(GETHOMECOACHDATA_ENDPOINT) + return 1 + async def async_update_measures( self, home_id: str, module_id: str, start_time: int | None = None, + end_time: int | None = None, interval: MeasureInterval = MeasureInterval.HOUR, days: int = 7, - ) -> None: - """Retrieve measures data from /getmeasure.""" + ) -> int: + """Retrieve measures data from /getmeasure. Returns the number of performed API calls.""" - await getattr(self.homes[home_id].modules[module_id], "async_update_measures")( + num_calls = await getattr( + self.homes[home_id].modules[module_id], "async_update_measures" + )( start_time=start_time, + end_time=end_time, interval=interval, days=days, ) + return num_calls def register_public_weather_area( self, @@ -139,8 +184,8 @@ def register_public_weather_area( ) return area_id - async def async_update_public_weather(self, area_id: str) -> None: - """Retrieve status data from /getpublicdata.""" + async def async_update_public_weather(self, area_id: str) -> int: + """Retrieve status data from /getpublicdata. Returns the number of performed API calls.""" params = { "lat_ne": self.public_weather_areas[area_id].location.lat_ne, "lon_ne": self.public_weather_areas[area_id].location.lon_ne, @@ -157,6 +202,8 @@ async def async_update_public_weather(self, area_id: str) -> None: area_id=area_id, ) + return 1 + async def _async_update_data( self, endpoint: str, @@ -169,8 +216,8 @@ async def _async_update_data( raw_data = extract_raw_data(await resp.json(), tag) await self.update_devices(raw_data, area_id) - async def async_set_state(self, home_id: str, data: dict[str, Any]) -> None: - """Modify device state by passing JSON specific to the device.""" + async def async_set_state(self, home_id: str, data: dict[str, Any]) -> int: + """Modify device state by passing JSON specific to the device. Returns the number of performed API calls.""" LOG.debug("Setting state: %s", data) post_params = { @@ -186,6 +233,7 @@ async def async_set_state(self, home_id: str, data: dict[str, Any]) -> None: params=post_params, ) LOG.debug("Response: %s", resp) + return 1 async def update_devices( self, diff --git a/src/pyatmo/auth.py b/src/pyatmo/auth.py index e94ed410..b4d3ffe8 100644 --- a/src/pyatmo/auth.py +++ b/src/pyatmo/auth.py @@ -17,7 +17,7 @@ WEBHOOK_URL_ADD_ENDPOINT, WEBHOOK_URL_DROP_ENDPOINT, ) -from pyatmo.exceptions import ApiError +from pyatmo.exceptions import ApiError, ApiErrorThrottling LOG = logging.getLogger(__name__) @@ -89,6 +89,42 @@ async def async_post_api_request( timeout=timeout, ) + async def async_get_api_request( + self, + endpoint: str, + base_url: str | None = None, + params: dict[str, Any] | None = None, + timeout: int = 5, + ) -> ClientResponse: + """Wrap async post requests.""" + + return await self.async_get_request( + url=(base_url or self.base_url) + endpoint, + params=params, + timeout=timeout, + ) + + async def async_get_request( + self, + url: str, + params: dict[str, Any] | None = None, + timeout: int = 5, + ) -> ClientResponse: + """Wrap async post requests.""" + + access_token = await self.get_access_token() + headers = {AUTHORIZATION_HEADER: f"Bearer {access_token}"} + + req_args = self.prepare_request_get_arguments(params) + + async with self.websession.get( + url, + **req_args, + headers=headers, + timeout=timeout, + ) as resp: + return await self.process_response(resp, url) + async def async_post_request( self, url: str, @@ -131,6 +167,10 @@ def prepare_request_arguments(self, params): return req_args + def prepare_request_get_arguments(self, params): + """Prepare get request arguments.""" + return params + async def process_response(self, resp, url): """Process response.""" resp_status = resp.status @@ -146,14 +186,21 @@ async def handle_error_response(self, resp, resp_status, url): """Handle error response.""" try: resp_json = await resp.json() - raise ApiError( - f"{resp_status} - " - f"{ERRORS.get(resp_status, '')} - " - f"{resp_json['error']['message']} " - f"({resp_json['error']['code']}) " - f"when accessing '{url}'", + + message = ( + f"{resp_status} - {ERRORS.get(resp_status, '')} - {resp_json['error']['message']} " + f"({resp_json['error']['code']}) when accessing '{url}'" ) + if resp_status == 403 and resp_json["error"]["code"] == 26: + raise ApiErrorThrottling( + message, + ) + else: + raise ApiError( + message, + ) + except (JSONDecodeError, ContentTypeError) as exc: raise ApiError( f"{resp_status} - " diff --git a/src/pyatmo/const.py b/src/pyatmo/const.py index f182dfc2..bd87c22f 100644 --- a/src/pyatmo/const.py +++ b/src/pyatmo/const.py @@ -2,6 +2,7 @@ from __future__ import annotations +from enum import Enum from typing import Any ERRORS: dict[int, str] = { @@ -45,6 +46,7 @@ GETHOMECOACHDATA_ENDPOINT = "api/gethomecoachsdata" GETMEASURE_ENDPOINT = "api/getmeasure" +GETHOMEMEASURE_ENDPOINT = "api/gethomemeasure" GETSTATIONDATA_ENDPOINT = "api/getstationsdata" GETPUBLIC_DATA_ENDPOINT = "api/getpublicdata" @@ -88,7 +90,6 @@ SCHEDULES = "schedules" EVENTS = "events" - STATION_TEMPERATURE_TYPE = "temperature" STATION_PRESSURE_TYPE = "pressure" STATION_HUMIDITY_TYPE = "humidity" @@ -102,3 +103,49 @@ ACCESSORY_WIND_TIME_TYPE = "wind_timeutc" ACCESSORY_GUST_STRENGTH_TYPE = "gust_strength" ACCESSORY_GUST_ANGLE_TYPE = "gust_angle" + +SCHEDULE_TYPE_THERM = "therm" +SCHEDULE_TYPE_EVENT = "event" +SCHEDULE_TYPE_ELECTRICITY = "electricity" +SCHEDULE_TYPE_COOLING = "cooling" + +ENERGY_ELEC_PEAK_IDX = 0 +ENERGY_ELEC_OFF_IDX = 1 + + +class MeasureType(Enum): + """Measure type.""" + + BOILERON = "boileron" + BOILEROFF = "boileroff" + SUM_BOILER_ON = "sum_boiler_on" + SUM_BOILER_OFF = "sum_boiler_off" + SUM_ENERGY_ELEC = "sum_energy_buy_from_grid" + SUM_ENERGY_ELEC_BASIC = "sum_energy_buy_from_grid$0" + SUM_ENERGY_ELEC_PEAK = "sum_energy_buy_from_grid$1" + SUM_ENERGY_ELEC_OFF_PEAK = "sum_energy_buy_from_grid$2" + SUM_ENERGY_PRICE = "sum_energy_buy_from_grid_price" + SUM_ENERGY_PRICE_BASIC = "sum_energy_buy_from_grid_price$0" + SUM_ENERGY_PRICE_PEAK = "sum_energy_buy_from_grid_price$1" + SUM_ENERGY_PRICE_OFF_PEAK = "sum_energy_buy_from_grid_price$2" + + +class MeasureInterval(Enum): + """Measure interval.""" + + HALF_HOUR = "30min" + HOUR = "1hour" + THREE_HOURS = "3hours" + DAY = "1day" + WEEK = "1week" + MONTH = "1month" + + +MEASURE_INTERVAL_TO_SECONDS = { + MeasureInterval.HALF_HOUR: 1800, + MeasureInterval.HOUR: 3600, + MeasureInterval.THREE_HOURS: 10800, + MeasureInterval.DAY: 86400, + MeasureInterval.WEEK: 604800, + MeasureInterval.MONTH: 2592000, +} diff --git a/src/pyatmo/exceptions.py b/src/pyatmo/exceptions.py index 4ed5f120..31cc8c69 100644 --- a/src/pyatmo/exceptions.py +++ b/src/pyatmo/exceptions.py @@ -37,6 +37,18 @@ class ApiError(Exception): pass +class ApiErrorThrottling(ApiError): + """Raised when an API error is encountered.""" + + pass + + +class ApiHomeReachabilityError(ApiError): + """Raised when an API error is encountered.""" + + pass + + class InvalidState(Exception): """Raised when an invalid state is encountered.""" diff --git a/src/pyatmo/helpers.py b/src/pyatmo/helpers.py index 72c5a822..9db2adbd 100644 --- a/src/pyatmo/helpers.py +++ b/src/pyatmo/helpers.py @@ -33,7 +33,6 @@ def fix_id(raw_data: RawData) -> dict[str, Any]: def extract_raw_data(resp: Any, tag: str) -> dict[str, Any]: """Extract raw data from server response.""" - raw_data = {} if tag == "body": return {"public": resp["body"], "errors": []} diff --git a/src/pyatmo/home.py b/src/pyatmo/home.py index 0c593969..580ccd0a 100644 --- a/src/pyatmo/home.py +++ b/src/pyatmo/home.py @@ -9,7 +9,11 @@ from pyatmo import modules from pyatmo.const import ( + ENERGY_ELEC_OFF_IDX, + ENERGY_ELEC_PEAK_IDX, EVENTS, + SCHEDULE_TYPE_ELECTRICITY, + SCHEDULE_TYPE_THERM, SCHEDULES, SETPERSONSAWAY_ENDPOINT, SETPERSONSHOME_ENDPOINT, @@ -17,6 +21,7 @@ SETTHERMMODE_ENDPOINT, SWITCHHOMESCHEDULE_ENDPOINT, SYNCHOMESCHEDULE_ENDPOINT, + MeasureType, RawData, ) from pyatmo.event import Event @@ -24,7 +29,7 @@ from pyatmo.modules import Module from pyatmo.person import Person from pyatmo.room import Room -from pyatmo.schedule import Schedule +from pyatmo.schedule import Schedule, ThermSchedule, schedule_factory if TYPE_CHECKING: from pyatmo.auth import AbstractAsyncAuth @@ -40,9 +45,12 @@ class Home: name: str rooms: dict[str, Room] modules: dict[str, Module] - schedules: dict[str, Schedule] + schedules: dict[str, Schedule] # for compatibility should diseappear + all_schedules: dict[dict[str, str, Schedule]] | {} persons: dict[str, Person] events: dict[str, Event] + energy_endpoints: list[str] + energy_schedule: list[int] def __init__(self, auth: AbstractAsyncAuth, raw_data: RawData) -> None: """Initialize a Netatmo home instance.""" @@ -62,15 +70,76 @@ def __init__(self, auth: AbstractAsyncAuth, raw_data: RawData) -> None: ) for room in raw_data.get("rooms", []) } - self.schedules = { - s["id"]: Schedule(home=self, raw_data=s) - for s in raw_data.get(SCHEDULES, []) - } + self._handle_schedules(raw_data.get(SCHEDULES, [])) self.persons = { s["id"]: Person(home=self, raw_data=s) for s in raw_data.get("persons", []) } self.events = {} + def _handle_schedules(self, raw_data): + + schedules = {} + + self.schedules = {} + + for s in raw_data: + # strange but Energy plan are stored in schedules, we should handle this one differently + sched, schedule_type = schedule_factory(home=self, raw_data=s) + if schedule_type not in schedules: + schedules[schedule_type] = {} + schedules[schedule_type][s["id"]] = sched + self.schedules[s["id"]] = sched + + self.all_schedules = schedules + + nrj_schedule = next( + iter(schedules.get(SCHEDULE_TYPE_ELECTRICITY, {}).values()), None + ) + + self.energy_schedule_vals = [] + self.energy_endpoints = [MeasureType.SUM_ENERGY_ELEC_BASIC.value] + if nrj_schedule is not None: + + # Tariff option (basic = always the same price, peak_and_off_peak = peak & off peak hours) + type_tariff = nrj_schedule.tariff_option + zones = nrj_schedule.zones + + if type_tariff == "peak_and_off_peak" and len(zones) >= 2: + + self.energy_endpoints = [None, None] + + self.energy_endpoints[ENERGY_ELEC_PEAK_IDX] = ( + MeasureType.SUM_ENERGY_ELEC_PEAK.value + ) + self.energy_endpoints[ENERGY_ELEC_OFF_IDX] = ( + MeasureType.SUM_ENERGY_ELEC_OFF_PEAK.value + ) + + if zones[0].price_type == "peak": + peak_id = zones[0].entity_id + else: + peak_id = zones[1].entity_id + + timetable = nrj_schedule.timetable + + # timetable are daily for electricity type, and sorted from begining to end + for t in timetable: + + time = ( + t.m_offset * 60 + ) # m_offset is in minute from the begininng of the day + if len(self.energy_schedule_vals) == 0: + time = 0 + + pos_to_add = ENERGY_ELEC_OFF_IDX + if t.zone_id == peak_id: + pos_to_add = ENERGY_ELEC_PEAK_IDX + + self.energy_schedule_vals.append((time, pos_to_add)) + + else: + self.energy_endpoints = [MeasureType.SUM_ENERGY_ELEC_BASIC.value] + def get_module(self, module: dict) -> Module: """Return module.""" @@ -120,32 +189,39 @@ def update_topology(self, raw_data: RawData) -> None: for room in self.rooms.keys() - {m["id"] for m in raw_rooms}: self.rooms.pop(room) - self.schedules = { - s["id"]: Schedule(home=self, raw_data=s) - for s in raw_data.get(SCHEDULES, []) - } + self._handle_schedules(raw_data.get(SCHEDULES, [])) - async def update(self, raw_data: RawData) -> None: + async def update(self, raw_data: RawData) -> bool: """Update home with the latest data.""" - + num_errors = 0 for module in raw_data.get("errors", []): + num_errors += 1 await self.modules[module["id"]].update({}) data = raw_data["home"] + has_an_update = False for module in data.get("modules", []): + has_an_update = True if module["id"] not in self.modules: self.update_topology({"modules": [module]}) await self.modules[module["id"]].update(module) for room in data.get("rooms", []): + has_an_update = True self.rooms[room["id"]].update(room) self.events = { s["id"]: Event(home_id=self.entity_id, raw_data=s) for s in data.get(EVENTS, []) } + if len(self.events) > 0: + has_an_update = True + + has_one_module_reachable = False for module in self.modules.values(): + if module.reachable: + has_one_module_reachable = True if hasattr(module, "events"): setattr( module, @@ -157,18 +233,39 @@ async def update(self, raw_data: RawData) -> None: ], ) - def get_selected_schedule(self) -> Schedule | None: + if ( + num_errors > 0 + and has_one_module_reachable is False + and has_an_update is False + ): + return False + + return True + + def get_selected_schedule(self, schedule_type: str = None) -> Schedule | None: """Return selected schedule for given home.""" + if schedule_type is None: + schedule_type = SCHEDULE_TYPE_THERM + + schedules = self.all_schedules.get(schedule_type, {}) return next( - (schedule for schedule in self.schedules.values() if schedule.selected), + (schedule for schedule in schedules.values() if schedule.selected), None, ) + def get_selected_temperature_schedule(self) -> ThermSchedule | None: + """Return selected temperature schedule for given home.""" + + return self.get_selected_schedule(schedule_type=SCHEDULE_TYPE_THERM) + def is_valid_schedule(self, schedule_id: str) -> bool: """Check if valid schedule.""" + for schedules in self.all_schedules.values(): + if schedule_id in schedules: + return True - return schedule_id in self.schedules + return False def has_otm(self) -> bool: """Check if any room has an OTM device.""" @@ -178,14 +275,14 @@ def has_otm(self) -> bool: def get_hg_temp(self) -> float | None: """Return frost guard temperature value for given home.""" - if (schedule := self.get_selected_schedule()) is None: + if (schedule := self.get_selected_temperature_schedule()) is None: return None return schedule.hg_temp def get_away_temp(self) -> float | None: """Return configured away temperature value for given home.""" - if (schedule := self.get_selected_schedule()) is None: + if (schedule := self.get_selected_temperature_schedule()) is None: return None return schedule.away_temp @@ -277,7 +374,7 @@ async def async_set_schedule_temperatures( ) -> None: """Set the scheduled room temperature for the given schedule ID.""" - selected_schedule = self.get_selected_schedule() + selected_schedule = self.get_selected_temperature_schedule() if selected_schedule is None: raise NoSchedule("Could not determine selected schedule.") diff --git a/src/pyatmo/modules/base_class.py b/src/pyatmo/modules/base_class.py index dbc88779..5d245a27 100644 --- a/src/pyatmo/modules/base_class.py +++ b/src/pyatmo/modules/base_class.py @@ -3,9 +3,11 @@ from __future__ import annotations from abc import ABC +import bisect from collections.abc import Iterable from dataclasses import dataclass import logging +from operator import itemgetter from typing import TYPE_CHECKING, Any from pyatmo.const import RawData @@ -15,8 +17,9 @@ from pyatmo.event import EventTypes from pyatmo.home import Home -LOG = logging.getLogger(__name__) +from time import time +LOG = logging.getLogger(__name__) NETATMO_ATTRIBUTES_MAP = { "entity_id": lambda x, y: x.get("id", y), @@ -50,6 +53,13 @@ class EntityBase: entity_id: str home: Home bridge: str | None + history_features: set[str] + history_features_values: dict[str, [int, int]] | {} + name: str | None + + +# 2 days of dynamic historical data stored +MAX_HISTORY_TIME_S = 24 * 2 * 3600 class NetatmoBase(EntityBase, ABC): @@ -60,6 +70,8 @@ def __init__(self, raw_data: RawData) -> None: self.entity_id = raw_data["id"] self.name = raw_data.get("name", f"Unknown {self.entity_id}") + self.history_features_values = {} + self.history_features = set() def update_topology(self, raw_data: RawData) -> None: """Update topology.""" @@ -81,6 +93,50 @@ def _update_attributes(self, raw_data: RawData) -> None: for key, val in self.__dict__.items() } + now = int(time()) + for hist_feature in self.history_features: + if hist_feature in self.__dict__: + hist_f = self.history_features_values.get(hist_feature, None) + if hist_f is None: + hist_f = [] + self.history_features_values[hist_feature] = hist_f + val = getattr(self, hist_feature) + if val is None: + continue + if not hist_f or hist_f[-1][0] <= now: + hist_f.append((now, val, self.entity_id)) + else: + i = bisect.bisect_left(hist_f, now, key=itemgetter(0)) + + if i < len(hist_f): + if hist_f[i][0] == now: + hist_f[i] = (now, val, self.entity_id) + i = None + + if i is not None: + hist_f.insert(i, (now, val, self.entity_id)) + + # keep timing history to a maximum representative time + while len(hist_f) > 0 and now - hist_f[0][0] > MAX_HISTORY_TIME_S: + hist_f.pop(0) + + def get_history_data(self, feature: str, from_ts: int, to_ts: int | None = None): + """Retrieve historical data.""" + + hist_f = self.history_features_values.get(feature, []) + + if not hist_f: + return [] + + in_s = bisect.bisect_left(hist_f, from_ts, key=itemgetter(0)) + + if to_ts is None: + out_s = len(hist_f) + else: + out_s = bisect.bisect_right(hist_f, to_ts, key=itemgetter(0)) + + return hist_f[in_s:out_s] + @dataclass class Location: diff --git a/src/pyatmo/modules/bticino.py b/src/pyatmo/modules/bticino.py index e4c681bc..7e38af69 100644 --- a/src/pyatmo/modules/bticino.py +++ b/src/pyatmo/modules/bticino.py @@ -4,7 +4,7 @@ import logging -from pyatmo.modules.module import Dimmer, Module, Shutter, Switch +from pyatmo.modules.module import Dimmer, Module, OffloadMixin, Shutter, Switch LOG = logging.getLogger(__name__) @@ -61,7 +61,7 @@ class BNTR(Module): """BTicino radiator thermostat.""" -class BNIL(Switch): +class BNIL(Switch, OffloadMixin): """BTicino itelligent light.""" diff --git a/src/pyatmo/modules/device_types.py b/src/pyatmo/modules/device_types.py index 6b2a27a3..e5fbe849 100644 --- a/src/pyatmo/modules/device_types.py +++ b/src/pyatmo/modules/device_types.py @@ -7,6 +7,7 @@ LOG = logging.getLogger(__name__) + # pylint: disable=W0613 @@ -205,7 +206,6 @@ class DeviceCategory(str, Enum): DeviceType.NLLF: DeviceCategory.fan, } - DEVICE_DESCRIPTION_MAP: dict[DeviceType, tuple[str, str]] = { # Netatmo Climate/Energy DeviceType.NAPlug: ("Netatmo", "Smart Thermostat Gateway"), diff --git a/src/pyatmo/modules/legrand.py b/src/pyatmo/modules/legrand.py index 0fe68e4f..657c83cf 100644 --- a/src/pyatmo/modules/legrand.py +++ b/src/pyatmo/modules/legrand.py @@ -7,11 +7,11 @@ from pyatmo.modules.module import ( BatteryMixin, ContactorMixin, + DimmableMixin, Dimmer, - EnergyMixin, + EnergyHistoryMixin, Fan, FirmwareMixin, - HistoryMixin, Module, OffloadMixin, PowerMixin, @@ -24,6 +24,7 @@ LOG = logging.getLogger(__name__) + # pylint: disable=R0901 @@ -31,15 +32,15 @@ class NLG(FirmwareMixin, OffloadMixin, WifiMixin, Module): """Legrand gateway.""" -class NLT(FirmwareMixin, BatteryMixin, Module): - """Legrand global remote control.""" +class NLT(DimmableMixin, FirmwareMixin, BatteryMixin, SwitchMixin, Module): + """Legrand global remote control...but also wireless switch, like NLD.""" -class NLP(Switch, HistoryMixin, PowerMixin, OffloadMixin, Module): +class NLP(Switch, OffloadMixin): """Legrand plug.""" -class NLPM(Switch): +class NLPM(Switch, OffloadMixin): """Legrand mobile plug.""" @@ -47,7 +48,7 @@ class NLPO(ContactorMixin, OffloadMixin, Switch): """Legrand contactor.""" -class NLPT(Switch): +class NLPT(Switch, OffloadMixin): """Legrand latching relay/teleruptor.""" @@ -75,11 +76,11 @@ class NLIS(Switch): """Legrand double switch.""" -class NLD(Dimmer): - """Legrand Double On/Off dimmer remote.""" +class NLD(DimmableMixin, FirmwareMixin, BatteryMixin, SwitchMixin, Module): + """Legrand Double On/Off dimmer remote. Wireless 2 button switch light.""" -class NLL(FirmwareMixin, EnergyMixin, WifiMixin, SwitchMixin, Module): +class NLL(Switch, WifiMixin): """Legrand / BTicino italian light switch with neutral.""" @@ -95,19 +96,19 @@ class NLLM(FirmwareMixin, RfMixin, ShutterMixin, Module): """Legrand / BTicino shutters.""" -class NLPC(FirmwareMixin, HistoryMixin, PowerMixin, EnergyMixin, Module): +class NLPC(FirmwareMixin, EnergyHistoryMixin, PowerMixin, Module): """Legrand / BTicino connected energy meter.""" -class NLE(FirmwareMixin, HistoryMixin, PowerMixin, EnergyMixin, Module): +class NLE(FirmwareMixin, EnergyHistoryMixin, PowerMixin, Module): """Legrand / BTicino connected ecometer.""" -class NLPS(FirmwareMixin, PowerMixin, EnergyMixin, Module): +class NLPS(FirmwareMixin, EnergyHistoryMixin, PowerMixin, Module): """Legrand / BTicino smart load shedder.""" -class NLC(FirmwareMixin, SwitchMixin, HistoryMixin, PowerMixin, OffloadMixin, Module): +class NLC(Switch, OffloadMixin): """Legrand / BTicino cable outlet.""" @@ -115,7 +116,7 @@ class NLDD(FirmwareMixin, Module): """Legrand NLDD dimmer remote control.""" -class NLUP(FirmwareMixin, PowerMixin, SwitchMixin, Module): +class NLUP(Switch): """Legrand NLUP Power outlet.""" @@ -135,7 +136,7 @@ class NLUO(Dimmer): """Legrand NLUO device stub.""" -class NLLF(Fan): +class NLLF(Fan, PowerMixin, EnergyHistoryMixin): """Legrand NLLF fan/ventilation device.""" @@ -159,7 +160,7 @@ class NLTS(Module): """NLTS motion sensor.""" -class NLPD(FirmwareMixin, SwitchMixin, EnergyMixin, PowerMixin, Module): +class NLPD(Switch, OffloadMixin): """NLPD dry contact.""" diff --git a/src/pyatmo/modules/module.py b/src/pyatmo/modules/module.py index b918c2b4..9800d7f4 100644 --- a/src/pyatmo/modules/module.py +++ b/src/pyatmo/modules/module.py @@ -2,14 +2,20 @@ from __future__ import annotations -from datetime import datetime, timezone -from enum import Enum +import copy +from datetime import datetime, timedelta, timezone import logging from typing import TYPE_CHECKING, Any from aiohttp import ClientConnectorError -from pyatmo.const import GETMEASURE_ENDPOINT, RawData +from pyatmo.const import ( + ENERGY_ELEC_PEAK_IDX, + GETMEASURE_ENDPOINT, + MEASURE_INTERVAL_TO_SECONDS, + MeasureInterval, + RawData, +) from pyatmo.exceptions import ApiError from pyatmo.modules.base_class import EntityBase, NetatmoBase, Place from pyatmo.modules.device_types import DEVICE_CATEGORY_MAP, DeviceCategory, DeviceType @@ -18,6 +24,10 @@ from pyatmo.event import Event from pyatmo.home import Home +import bisect +from operator import itemgetter +from time import time + LOG = logging.getLogger(__name__) ModuleT = dict[str, Any] @@ -41,6 +51,8 @@ "device_category", "device_type", "features", + "history_features", + "history_features_values", } @@ -289,16 +301,6 @@ def __init__(self, home: Home, module: ModuleT): self.appliance_type: str | None = None -class EnergyMixin(EntityBase): - """Mixin for energy data.""" - - def __init__(self, home: Home, module: ModuleT): - """Initialize energy mixin.""" - - super().__init__(home, module) # type: ignore # mypy issue 4335 - self.sum_energy_elec: int | None = None - - class PowerMixin(EntityBase): """Mixin for power data.""" @@ -307,6 +309,7 @@ def __init__(self, home: Home, module: ModuleT): super().__init__(home, module) # type: ignore # mypy issue 4335 self.power: int | None = None + self.history_features.add("power") class EventMixin(EntityBase): @@ -452,7 +455,6 @@ def __init__(self, home: Home, module: ModuleT): self.local_url: str | None = None self.is_local: bool | None = None self.alim_status: int | None = None - self.device_type: DeviceType async def async_get_live_snapshot(self) -> bytes | None: """Fetch live camera image.""" @@ -470,7 +472,7 @@ async def async_get_live_snapshot(self) -> bytes | None: async def async_update_camera_urls(self) -> None: """Update and validate the camera urls.""" - if self.device_type == "NDB": + if isinstance(self, Module) and self.device_type == "NDB": self.is_local = None if self.vpn_url and self.is_local: @@ -584,35 +586,16 @@ async def async_monitoring_off(self) -> bool: return await self.async_set_monitoring_state("off") -class MeasureInterval(Enum): - """Measure interval.""" - - HALF_HOUR = "30min" - HOUR = "1hour" - THREE_HOURS = "3hours" - DAY = "1day" - WEEK = "1week" - MONTH = "1month" - +def _get_proper_in_schedule_index(energy_schedule_vals, srt_beg): + idx = bisect.bisect_left(energy_schedule_vals, srt_beg, key=itemgetter(0)) + if idx >= len(energy_schedule_vals): + idx = len(energy_schedule_vals) - 1 + elif energy_schedule_vals[idx][0] > srt_beg: # if strict equal idx is the good one + idx = max(0, idx - 1) + return idx -class MeasureType(Enum): - """Measure type.""" - BOILERON = "boileron" - BOILEROFF = "boileroff" - SUM_BOILER_ON = "sum_boiler_on" - SUM_BOILER_OFF = "sum_boiler_off" - SUM_ENERGY_ELEC = "sum_energy_elec" - SUM_ENERGY_ELEC_BASIC = "sum_energy_elec$0" - SUM_ENERGY_ELEC_PEAK = "sum_energy_elec$1" - SUM_ENERGY_ELEC_OFF_PEAK = "sum_energy_elec$2" - SUM_ENERGY_PRICE = "sum_energy_price" - SUM_ENERGY_PRICE_BASIC = "sum_energy_price$0" - SUM_ENERGY_PRICE_PEAK = "sum_energy_price$1" - SUM_ENERGY_PRICE_OFF_PEAK = "sum_energy_price$2" - - -class HistoryMixin(EntityBase): +class EnergyHistoryMixin(EntityBase): """Mixin for history data.""" def __init__(self, home: Home, module: ModuleT): @@ -621,55 +604,478 @@ def __init__(self, home: Home, module: ModuleT): super().__init__(home, module) # type: ignore # mypy issue 4335 self.historical_data: list[dict[str, Any]] | None = None self.start_time: int | None = None + self.end_time: int | None = None self.interval: MeasureInterval | None = None + self.sum_energy_elec: int | None = None + self.sum_energy_elec_peak: int | None = None + self.sum_energy_elec_off_peak: int | None = None + self._last_energy_from_API_end_for_power_adjustment_calculus: int | None = None + self.in_reset: bool | False = False + + def reset_measures(self): + """Reset energy measures.""" + self.in_reset = True + self.historical_data = [] + self._last_energy_from_API_end_for_power_adjustment_calculus = None + self.sum_energy_elec = 0 + self.sum_energy_elec_peak = 0 + self.sum_energy_elec_off_peak = 0 + + def compute_rieman_sum(self, power_data, conservative: bool = False): + """Compute energy from power with a rieman sum.""" + + delta_energy = 0 + if len(power_data) > 1: + + # compute a rieman sum, as best as possible , trapezoidal, taking pessimistic asumption + # as we don't want to artifically go up the previous one + # (except in rare exceptions like reset, 0 , etc) + + for i in range(len(power_data) - 1): + + dt_h = float(power_data[i + 1][0] - power_data[i][0]) / 3600.0 + + if conservative: + d_p_w = 0 + else: + d_p_w = abs(float(power_data[i + 1][1] - power_data[i][1])) + + d_nrj_wh = dt_h * ( + min(power_data[i + 1][1], power_data[i][1]) + 0.5 * d_p_w + ) + + delta_energy += d_nrj_wh + + return delta_energy + + def get_sum_energy_elec_power_adapted( + self, to_ts: int | float | None = None, conservative: bool = False + ): + """Compute proper energy value with adaptation from power.""" + v = self.sum_energy_elec + + if v is None: + return None, 0 + + delta_energy = 0 + + if self.in_reset is False: + + if to_ts is None: + to_ts = time() + + from_ts = self._last_energy_from_API_end_for_power_adjustment_calculus + + if ( + from_ts is not None + and from_ts < to_ts + and isinstance(self, PowerMixin) + and isinstance(self, NetatmoBase) + ): + power_data = self.get_history_data( + "power", from_ts=from_ts, to_ts=to_ts + ) + if isinstance( + self, EnergyHistoryMixin + ): # well to please the linter.... + delta_energy = self.compute_rieman_sum(power_data, conservative) + + return v, delta_energy + + def _log_energy_error(self, start_time, end_time, msg=None, body=None): + if body is None: + body = "NO BODY" + LOG.debug( + "ENERGY collection error %s %s %s %s %s %s %s", + msg, + self.name, + datetime.fromtimestamp(start_time), + datetime.fromtimestamp(end_time), + start_time, + end_time, + body, + ) + + def update_measures_num_calls(self): + """Get number of possible endpoint calls.""" + + if not self.home.energy_endpoints: + return 1 + else: + return len(self.home.energy_endpoints) async def async_update_measures( self, start_time: int | None = None, + end_time: int | None = None, interval: MeasureInterval = MeasureInterval.HOUR, days: int = 7, - ) -> None: + ) -> int | None: """Update historical data.""" - end_time = int(datetime.now().timestamp()) + if end_time is None: + end_time = int(datetime.now().timestamp()) + if start_time is None: - start_time = end_time - days * 24 * 60 * 60 - - data_point = MeasureType.SUM_ENERGY_ELEC_BASIC.value - params = { - "device_id": self.bridge, - "module_id": self.entity_id, - "scale": interval.value, - "type": data_point, - "date_begin": start_time, - "date_end": end_time, - } + end = datetime.fromtimestamp(end_time) + start_time = end - timedelta(days=days) + start_time = int(start_time.timestamp()) - resp = await self.home.auth.async_post_api_request( - endpoint=GETMEASURE_ENDPOINT, - params=params, + prev_start_time = self.start_time + prev_end_time = self.end_time + + self.start_time = start_time + self.end_time = end_time + + # the legrand/netatmo handling of start and endtime is very peculiar + # for 30mn/1h/3h intervals : in fact the starts is asked_start + intervals/2 ! + # => so shift of 15mn, 30mn and 1h30 + # for 1day : start is ALWAYS 12am (half day) of the first day of the range + # for 1week : it will be half week ALWAYS, ie on a thursday at 12am (half day) + # in fact in the case for all intervals the reported dates are "the middle" of the ranges + + delta_range = MEASURE_INTERVAL_TO_SECONDS.get(interval, 0) // 2 + + data_points, num_calls, raw_datas, peak_off_peak_mode = ( + await self._energy_API_calls(start_time, end_time, interval) ) - raw_data = await resp.json() - data = raw_data["body"][0] - interval_sec = int(data["step_time"]) - interval_min = interval_sec // 60 + energy_schedule_vals = [] + + if peak_off_peak_mode: + energy_schedule_vals = await self._compute_proper_energy_schedule_offsets( + start_time, end_time, 2 * delta_range, raw_datas, data_points + ) + + hist_good_vals = await self._get_aligned_energy_values_and_mode( + start_time, + end_time, + delta_range, + energy_schedule_vals, + peak_off_peak_mode, + raw_datas, + data_points, + ) self.historical_data = [] - self.start_time = int(data["beg_time"]) - start_time = self.start_time - for value in data["value"]: - end_time = start_time + interval_sec + prev_sum_energy_elec = self.sum_energy_elec + self.sum_energy_elec = 0 + self.sum_energy_elec_peak = 0 + self.sum_energy_elec_off_peak = 0 + self._last_energy_from_API_end_for_power_adjustment_calculus = start_time # no data at all: we know nothing for the end: best guess, it is the start + self.in_reset = False + + if len(hist_good_vals) == 0: + # nothing has been updated or changed it can nearly be seen as an error, but the api is answering correctly + # so we probably have to reset to 0 anyway as it means there were no exisitng + # historical data for this time range + + LOG.debug( + "NO VALUES energy update %s from: %s to %s, prev_sum=%s", + self.name, + datetime.fromtimestamp(start_time), + datetime.fromtimestamp(end_time), + prev_sum_energy_elec if prev_sum_energy_elec is not None else "NOTHING", + ) + else: + + await self._prepare_exported_historical_data( + start_time, + end_time, + delta_range, + hist_good_vals, + prev_end_time, + prev_start_time, + prev_sum_energy_elec, + peak_off_peak_mode, + ) + + return num_calls + + async def _prepare_exported_historical_data( + self, + start_time, + end_time, + delta_range, + hist_good_vals, + prev_end_time, + prev_start_time, + prev_sum_energy_elec, + peak_off_peak_mode, + ): + computed_start = 0 + computed_end = 0 + computed_end_for_calculus = 0 + for cur_start_time, val, cur_peak_or_off_peak_mode in hist_good_vals: + + self.sum_energy_elec += val + + if peak_off_peak_mode: + mode = "off_peak" + if cur_peak_or_off_peak_mode == ENERGY_ELEC_PEAK_IDX: + self.sum_energy_elec_peak += val + mode = "peak" + else: + self.sum_energy_elec_off_peak += val + else: + mode = "standard" + + c_start = cur_start_time + c_end = cur_start_time + 2 * delta_range + + if computed_start == 0: + computed_start = c_start + computed_end = c_end + computed_end_for_calculus = c_end # - delta_range #not sure, revert ... it seems the energy value effectively stops at those mid values + + start_time_string = f"{datetime.fromtimestamp(c_start + 1, tz=timezone.utc).isoformat().split('+')[0]}Z" + end_time_string = f"{datetime.fromtimestamp(c_end, tz=timezone.utc).isoformat().split('+')[0]}Z" self.historical_data.append( { - "duration": interval_min, - "startTime": f"{datetime.fromtimestamp(start_time + 1, tz=timezone.utc).isoformat().split('+')[0]}Z", - "endTime": f"{datetime.fromtimestamp(end_time, tz=timezone.utc).isoformat().split('+')[0]}Z", - "Wh": value[0], + "duration": (2 * delta_range) // 60, + "startTime": start_time_string, + "endTime": end_time_string, + "Wh": val, + "energyMode": mode, + "startTimeUnix": c_start, + "endTimeUnix": c_end, }, ) + if ( + prev_sum_energy_elec is not None + and prev_sum_energy_elec > self.sum_energy_elec + ): + msg = ( + "ENERGY GOING DOWN %s from: %s to %s " + "computed_start: %s, computed_end: %s, " + "sum=%f prev_sum=%f prev_start: %s, prev_end %s" + ) + LOG.debug( + msg, + self.name, + datetime.fromtimestamp(start_time), + datetime.fromtimestamp(end_time), + datetime.fromtimestamp(computed_start), + datetime.fromtimestamp(computed_end), + self.sum_energy_elec, + prev_sum_energy_elec, + datetime.fromtimestamp(prev_start_time), + datetime.fromtimestamp(prev_end_time), + ) + else: + msg = ( + "Success in energy update %s from: %s to %s " + "computed_start: %s, computed_end: %s , sum=%s prev_sum=%s" + ) + LOG.debug( + msg, + self.name, + datetime.fromtimestamp(start_time), + datetime.fromtimestamp(end_time), + datetime.fromtimestamp(computed_start), + datetime.fromtimestamp(computed_end), + self.sum_energy_elec, + prev_sum_energy_elec if prev_sum_energy_elec is not None else "NOTHING", + ) + self._last_energy_from_API_end_for_power_adjustment_calculus = ( + computed_end_for_calculus + ) + + async def _get_aligned_energy_values_and_mode( + self, + start_time, + end_time, + delta_range, + energy_schedule_vals, + peak_off_peak_mode, + raw_datas, + data_points, + ): + hist_good_vals = [] + for cur_peak_or_off_peak_mode, values_lots in enumerate(raw_datas): + for values_lot in values_lots: + try: + start_lot_time = int(values_lot["beg_time"]) + except Exception: + self._log_energy_error( + start_time, + end_time, + msg=f"beg_time missing {data_points[cur_peak_or_off_peak_mode]}", + body=raw_datas[cur_peak_or_off_peak_mode], + ) + raise ApiError( + f"Energy badly formed resp beg_time missing: {raw_datas[cur_peak_or_off_peak_mode]} - " + f"module: {self.name} - " + f"when accessing '{data_points[cur_peak_or_off_peak_mode]}'" + ) from None + + interval_sec = values_lot.get("step_time") + if interval_sec is None: + if len(values_lot.get("value", [])) > 1: + self._log_energy_error( + start_time, + end_time, + msg=f"step_time missing {data_points[cur_peak_or_off_peak_mode]}", + body=raw_datas[cur_peak_or_off_peak_mode], + ) + interval_sec = 2 * delta_range + else: + interval_sec = int(interval_sec) + + # align the start on the begining of the segment + cur_start_time = start_lot_time - interval_sec // 2 + for val_arr in values_lot.get("value", []): + val = val_arr[0] + + if peak_off_peak_mode: + + d_srt = datetime.fromtimestamp(cur_start_time) + # offset from start of the day + day_origin = int( + datetime(d_srt.year, d_srt.month, d_srt.day).timestamp() + ) + srt_beg = cur_start_time - day_origin + srt_mid = srt_beg + interval_sec // 2 + + # now check if srt_beg is in a schedule span of the right type + idx_limit = _get_proper_in_schedule_index( + energy_schedule_vals, srt_mid + ) + + if ( + self.home.energy_schedule_vals[idx_limit][1] + != cur_peak_or_off_peak_mode + ): + + # we are NOT in a proper schedule time for this time span ... + # jump to the next one... meaning it is the next day! + if idx_limit == len(energy_schedule_vals) - 1: + # should never append with the performed day extension above + self._log_energy_error( + start_time, + end_time, + msg=f"bad idx missing {data_points[cur_peak_or_off_peak_mode]}", + body=raw_datas[cur_peak_or_off_peak_mode], + ) + + raise ApiError( + f"Energy badly formed bad schedule idx in vals: {raw_datas[cur_peak_or_off_peak_mode]} - " + f"module: {self.name} - " + f"when accessing '{data_points[cur_peak_or_off_peak_mode]}'" + ) + else: + # by construction of the energy schedule the next one should be of opposite mode + if ( + energy_schedule_vals[idx_limit + 1][1] + != cur_peak_or_off_peak_mode + ): + self._log_energy_error( + start_time, + end_time, + msg=f"bad schedule {data_points[cur_peak_or_off_peak_mode]}", + body=raw_datas[cur_peak_or_off_peak_mode], + ) + raise ApiError( + f"Energy badly formed bad schedule: {raw_datas[cur_peak_or_off_peak_mode]} - " + f"module: {self.name} - " + f"when accessing '{data_points[cur_peak_or_off_peak_mode]}'" + ) + + start_time_to_get_closer = energy_schedule_vals[ + idx_limit + 1 + ][0] + diff_t = start_time_to_get_closer - srt_mid + cur_start_time = ( + day_origin + + srt_beg + + (diff_t // interval_sec + 1) * interval_sec + ) + + hist_good_vals.append( + (cur_start_time, int(val), cur_peak_or_off_peak_mode) + ) + cur_start_time = cur_start_time + interval_sec + + hist_good_vals = sorted(hist_good_vals, key=itemgetter(0)) + return hist_good_vals + + async def _compute_proper_energy_schedule_offsets( + self, start_time, end_time, interval_sec, raw_datas, data_points + ): + max_interval_sec = interval_sec + for cur_peak_or_off_peak_mode, values_lots in enumerate(raw_datas): + for values_lot in values_lots: + local_step_time = values_lot.get("step_time") + + if local_step_time is None: + if len(values_lot.get("value", [])) > 1: + self._log_energy_error( + start_time, + end_time, + msg=f"step_time missing {data_points[cur_peak_or_off_peak_mode]}", + body=raw_datas[cur_peak_or_off_peak_mode], + ) + else: + local_step_time = int(local_step_time) + max_interval_sec = max(max_interval_sec, local_step_time) + biggest_day_interval = max_interval_sec // (3600 * 24) + 1 + energy_schedule_vals = copy.copy(self.home.energy_schedule_vals) + if energy_schedule_vals[-1][0] < max_interval_sec + (3600 * 24): + if energy_schedule_vals[0][1] == energy_schedule_vals[-1][1]: + # it means the last one continue in the first one the next day + energy_schedule_vals_next = energy_schedule_vals[1:] + else: + energy_schedule_vals_next = copy.copy(self.home.energy_schedule_vals) + + for d in range(0, biggest_day_interval): + next_day_extend = [ + (offset + ((d + 1) * 24 * 3600), mode) + for offset, mode in energy_schedule_vals_next + ] + energy_schedule_vals.extend(next_day_extend) + return energy_schedule_vals + + async def _energy_API_calls(self, start_time, end_time, interval): + num_calls = 0 + data_points = self.home.energy_endpoints + raw_datas = [] + for data_point in data_points: + + params = { + "device_id": self.bridge, + "module_id": self.entity_id, + "scale": interval.value, + "type": data_point, + "date_begin": start_time, + "date_end": end_time, + } + + resp = await self.home.auth.async_post_api_request( + endpoint=GETMEASURE_ENDPOINT, + params=params, + ) + + rw_dt_f = await resp.json() + rw_dt = rw_dt_f.get("body") + + if rw_dt is None: + self._log_energy_error( + start_time, end_time, msg=f"direct from {data_point}", body=rw_dt_f + ) + raise ApiError( + f"Energy badly formed resp: {rw_dt_f} - " + f"module: {self.name} - " + f"when accessing '{data_point}'" + ) + + num_calls += 1 + raw_datas.append(rw_dt) + + peak_off_peak_mode = False + if len(raw_datas) > 1 and len(self.home.energy_schedule_vals) > 0: + peak_off_peak_mode = True - start_time = end_time + return data_points, num_calls, raw_datas, peak_off_peak_mode class Module(NetatmoBase): @@ -743,7 +1149,7 @@ async def update(self, raw_data: RawData) -> None: await self.async_update_camera_urls() -class Switch(FirmwareMixin, PowerMixin, SwitchMixin, Module): +class Switch(FirmwareMixin, EnergyHistoryMixin, PowerMixin, SwitchMixin, Module): """Class to represent a Netatmo switch.""" ... diff --git a/src/pyatmo/modules/netatmo.py b/src/pyatmo/modules/netatmo.py index 047c4eb1..d40f9350 100644 --- a/src/pyatmo/modules/netatmo.py +++ b/src/pyatmo/modules/netatmo.py @@ -43,6 +43,7 @@ LOG = logging.getLogger(__name__) + # pylint: disable=R0901 diff --git a/src/pyatmo/schedule.py b/src/pyatmo/schedule.py index d603801e..98f413fe 100644 --- a/src/pyatmo/schedule.py +++ b/src/pyatmo/schedule.py @@ -6,7 +6,13 @@ import logging from typing import TYPE_CHECKING -from pyatmo.const import RawData +from pyatmo.const import ( + SCHEDULE_TYPE_COOLING, + SCHEDULE_TYPE_ELECTRICITY, + SCHEDULE_TYPE_EVENT, + SCHEDULE_TYPE_THERM, + RawData, +) from pyatmo.modules.base_class import NetatmoBase from pyatmo.room import Room @@ -21,23 +27,104 @@ class Schedule(NetatmoBase): """Class to represent a Netatmo schedule.""" selected: bool - away_temp: float | None - hg_temp: float | None + default: bool + type: str timetable: list[TimetableEntry] + zones: list[Zone] def __init__(self, home: Home, raw_data: RawData) -> None: """Initialize a Netatmo schedule instance.""" super().__init__(raw_data) self.home = home + self.type = raw_data.get("type", "therm") self.selected = raw_data.get("selected", False) - self.hg_temp = raw_data.get("hg_temp") - self.away_temp = raw_data.get("away_temp") + self.default = raw_data.get("default", False) self.timetable = [ TimetableEntry(home, r) for r in raw_data.get("timetable", []) ] self.zones = [Zone(home, r) for r in raw_data.get("zones", [])] +@dataclass +class ScheduleWithRealZones(Schedule): + """Class to represent a Netatmo schedule.""" + + zones: list[Zone] + + def __init__(self, home: Home, raw_data: RawData) -> None: + """Initialize a Netatmo schedule instance.""" + super().__init__(home, raw_data) + self.zones = [Zone(home, r) for r in raw_data.get("zones", [])] + + +@dataclass +class ThermSchedule(ScheduleWithRealZones): + """Class to represent a Netatmo Temperature schedule.""" + + away_temp: float | None + hg_temp: float | None + + def __init__(self, home: Home, raw_data: RawData) -> None: + """Initialize ThermSchedule.""" + super().__init__(home, raw_data) + self.hg_temp = raw_data.get("hg_temp") + self.away_temp = raw_data.get("away_temp") + + +@dataclass +class CoolingSchedule(ThermSchedule): + """Class to represent a Netatmo Cooling schedule.""" + + cooling_away_temp: float | None + hg_temp: float | None + + def __init__(self, home: Home, raw_data: RawData) -> None: + """Initialize CoolingSchedule.""" + super().__init__(home, raw_data) + self.cooling_away_temp = self.away_temp = raw_data.get( + "cooling_away_temp", self.away_temp + ) + + +@dataclass +class ElectricitySchedule(Schedule): + """Class to represent a Netatmo Energy Plan schedule.""" + + tariff: str + tariff_option: str + power_threshold: int | 6 + contract_power_unit: str # kVA or KW + zones: list[ZoneElectricity] + + def __init__(self, home: Home, raw_data: RawData) -> None: + """Initialize ElectricitySchedule.""" + super().__init__(home, raw_data) + self.tariff = raw_data.get("tariff", "custom") + # Tariff option (basic = always the same price, peak_and_off_peak = peak & offpeak hours) + self.tariff_option = raw_data.get("tariff_option", "basic") + self.power_threshold = raw_data.get("power_threshold", 6) + self.contract_power_unit = raw_data.get("power_threshold", "kVA") + self.zones = [ZoneElectricity(home, r) for r in raw_data.get("zones", [])] + + +@dataclass +class EventSchedule(Schedule): + """Class to represent a Netatmo Energy Plan schedule.""" + + timetable_sunrise: list[TimetableEventEntry] + timetable_sunset: list[TimetableEventEntry] + + def __init__(self, home: Home, raw_data: RawData) -> None: + """Initialize EventSchedule.""" + super().__init__(home, raw_data) + self.timetable_sunrise = [ + TimetableEventEntry(home, r) for r in raw_data.get("timetable_sunrise", []) + ] + self.timetable_sunset = [ + TimetableEventEntry(home, r) for r in raw_data.get("timetable_sunset", []) + ] + + @dataclass class TimetableEntry: """Class to represent a Netatmo schedule's timetable entry.""" @@ -50,6 +137,41 @@ def __init__(self, home: Home, raw_data: RawData) -> None: self.home = home self.zone_id = raw_data.get("zone_id", 0) self.m_offset = raw_data.get("m_offset", 0) + self.twilight_offset = raw_data.get("twilight_offset", 0) + + +@dataclass +class TimetableEventEntry: + """Class to represent a Netatmo schedule's timetable entry.""" + + zone_id: int | None + day: int | 1 + twilight_offset: int | 0 + + def __init__(self, home: Home, raw_data: RawData) -> None: + """Initialize a Netatmo schedule's timetable entry instance.""" + self.home = home + self.zone_id = raw_data.get("zone_id", 0) + self.day = raw_data.get("day", 1) + self.twilight_offset = raw_data.get("twilight_offset", 0) + + +class ModuleSchedule(NetatmoBase): + """Class to represent a Netatmo schedule.""" + + on: bool | None + target_position: int | None + fan_speed: int | None + brightness: int | None + + def __init__(self, home: Home, raw_data: RawData) -> None: + """Initialize a Netatmo schedule's zone instance.""" + super().__init__(raw_data) + self.home = home + self.on = raw_data.get("on", None) + self.target_position = raw_data.get("target_position", None) + self.fan_speed = raw_data.get("fan_speed", None) + self.brightness = raw_data.get("brightness", None) @dataclass @@ -58,6 +180,7 @@ class Zone(NetatmoBase): type: int rooms: list[Room] + modules: list[ModuleSchedule] def __init__(self, home: Home, raw_data: RawData) -> None: """Initialize a Netatmo schedule's zone instance.""" @@ -65,9 +188,38 @@ def __init__(self, home: Home, raw_data: RawData) -> None: self.home = home self.type = raw_data.get("type", 0) - def room_factory(home: Home, room_raw_data: RawData): - room = Room(home, room_raw_data, {}) + def room_factory(room_home: Home, room_raw_data: RawData): + room = Room(room_home, room_raw_data, {}) room.update(room_raw_data) return room self.rooms = [room_factory(home, r) for r in raw_data.get("rooms", [])] + self.modules = [ModuleSchedule(home, m) for m in raw_data.get("modules", [])] + + +@dataclass +class ZoneElectricity(NetatmoBase): + """Class to represent a Netatmo schedule's zone.""" + + price: float + price_type: str + + def __init__(self, home: Home, raw_data: RawData) -> None: + """Initialize a Netatmo schedule's zone instance.""" + super().__init__(raw_data) + self.home = home + self.price = raw_data.get("price", 0.0) + self.price_type = raw_data.get("price_type", "off_peak") + + +def schedule_factory(home: Home, raw_data: RawData) -> (Schedule, str): + """Create proper schedules.""" + + schedule_type = raw_data.get("type", "custom") + cls = { + SCHEDULE_TYPE_THERM: ThermSchedule, + SCHEDULE_TYPE_EVENT: EventSchedule, + SCHEDULE_TYPE_ELECTRICITY: ElectricitySchedule, + SCHEDULE_TYPE_COOLING: CoolingSchedule, + }.get(schedule_type, Schedule) + return cls(home, raw_data), schedule_type diff --git a/tests/common.py b/tests/common.py index f030959b..3bff5404 100644 --- a/tests/common.py +++ b/tests/common.py @@ -61,11 +61,22 @@ async def fake_post_request(*args, **kwargs): elif endpoint == "getmeasure": module_id = kwargs.get("params", {}).get("module_id") + type = kwargs.get("params", {}).get("type") payload = json.loads( - load_fixture(f"{endpoint}_{module_id.replace(':', '_')}.json"), + load_fixture(f"{endpoint}_{type}_{module_id.replace(':', '_')}.json"), ) else: - payload = json.loads(load_fixture(f"{endpoint}.json")) + postfix = kwargs.get("POSTFIX", None) + if postfix is not None: + payload = json.loads(load_fixture(f"{endpoint}_{postfix}.json")) + else: + payload = json.loads(load_fixture(f"{endpoint}.json")) return MockResponse(payload, 200) + + +async def fake_post_request_multi(*args, **kwargs): + kwargs["POSTFIX"] = "multi" + r = await fake_post_request(*args, **kwargs) + return r diff --git a/tests/conftest.py b/tests/conftest.py index 5c596d57..bd9bfdea 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -7,7 +7,7 @@ import pyatmo import pytest -from .common import fake_post_request +from .common import fake_post_request, fake_post_request_multi @contextmanager @@ -44,3 +44,29 @@ async def async_home(async_account): home_id = "91763b24c43d3e344f424e8b" await async_account.async_update_status(home_id) yield async_account.homes[home_id] + + +@pytest.fixture(scope="function") +async def async_account_multi(async_auth): + """AsyncAccount fixture.""" + account = pyatmo.AsyncAccount(async_auth) + + with patch( + "pyatmo.auth.AbstractAsyncAuth.async_post_api_request", + fake_post_request_multi, + ), patch( + "pyatmo.auth.AbstractAsyncAuth.async_post_request", + fake_post_request_multi, + ): + await account.async_update_topology( + disabled_homes_ids=["eeeeeeeeeffffffffffaaaaa"] + ) + yield account + + +@pytest.fixture(scope="function") +async def async_home_multi(async_account_multi): + """AsyncClimate fixture for home_id 91763b24c43d3e344f424e8b.""" + home_id = "aaaaaaaaaaabbbbbbbbbbccc" + await async_account_multi.async_update_status(home_id) + yield async_account_multi.homes[home_id] diff --git a/tests/test_energy.py b/tests/test_energy.py index 06ec2408..35dd9797 100644 --- a/tests/test_energy.py +++ b/tests/test_energy.py @@ -1,7 +1,16 @@ """Define tests for energy module.""" -from pyatmo import DeviceType +import datetime as dt +import json +from unittest.mock import AsyncMock, patch + +from pyatmo import ApiHomeReachabilityError, DeviceType +from pyatmo.const import MeasureInterval +from pyatmo.modules.module import EnergyHistoryMixin import pytest +import time_machine + +from tests.common import MockResponse # pylint: disable=F6401 @@ -14,3 +23,163 @@ async def test_async_energy_NLPC(async_home): # pylint: disable=invalid-name module = async_home.modules[module_id] assert module.device_type == DeviceType.NLPC assert module.power == 476 + + +@time_machine.travel(dt.datetime(2022, 2, 12, 7, 59, 49)) +@pytest.mark.asyncio +async def test_historical_data_retrieval(async_account): + """Test retrieval of historical measurements.""" + home_id = "91763b24c43d3e344f424e8b" + await async_account.async_update_events(home_id=home_id) + home = async_account.homes[home_id] + + module_id = "12:34:56:00:00:a1:4c:da" + assert module_id in home.modules + module = home.modules[module_id] + assert module.device_type == DeviceType.NLPC + + await async_account.async_update_measures(home_id=home_id, module_id=module_id) + # changed teh reference here as start and stop data was not calculated in the spirit of the netatmo api where their time data is in the fact representing the "middle" of the range and not the begining + assert module.historical_data[0] == { + "Wh": 197, + "duration": 60, + "endTime": "2022-02-05T08:59:49Z", + "endTimeUnix": 1644051589, + "energyMode": "standard", + "startTime": "2022-02-05T07:59:50Z", + "startTimeUnix": 1644047989, + } + assert module.historical_data[-1] == { + "Wh": 259, + "duration": 60, + "endTime": "2022-02-12T07:59:49Z", + "endTimeUnix": 1644652789, + "energyMode": "standard", + "startTime": "2022-02-12T06:59:50Z", + "startTimeUnix": 1644649189, + } + assert len(module.historical_data) == 168 + + +async def test_historical_data_retrieval_multi(async_account_multi): + """Test retrieval of historical measurements.""" + home_id = "aaaaaaaaaaabbbbbbbbbbccc" + + home = async_account_multi.homes[home_id] + + module_id = "98:76:54:32:10:00:00:73" + assert module_id in home.modules + module = home.modules[module_id] + assert module.device_type == DeviceType.NLC + + strt = int(dt.datetime.fromisoformat("2024-03-03 00:10:00").timestamp()) + end_time = int(dt.datetime.fromisoformat("2024-03-05 23:59:59").timestamp()) + + await async_account_multi.async_update_measures( + home_id=home_id, + module_id=module_id, + interval=MeasureInterval.HALF_HOUR, + start_time=strt, + end_time=end_time, + ) + assert isinstance(module, EnergyHistoryMixin) + + assert module.historical_data[0] == { + "Wh": 0, + "duration": 30, + "endTime": "2024-03-02T23:40:00Z", + "endTimeUnix": 1709422800, + "energyMode": "peak", + "startTime": "2024-03-02T23:10:01Z", + "startTimeUnix": 1709421000, + } + assert module.historical_data[-1] == { + "Wh": 0, + "duration": 30, + "endTime": "2024-03-05T23:10:00Z", + "endTimeUnix": 1709680200, + "energyMode": "peak", + "startTime": "2024-03-05T22:40:01Z", + "startTimeUnix": 1709678400, + } + assert len(module.historical_data) == 134 + + assert ( + module.sum_energy_elec + == module.sum_energy_elec_peak + module.sum_energy_elec_off_peak + ) + assert module.sum_energy_elec_off_peak == 11219 + assert module.sum_energy_elec_peak == 31282 + + +async def test_historical_data_retrieval_multi_2(async_account_multi): + """Test retrieval of historical measurements.""" + home_id = "aaaaaaaaaaabbbbbbbbbbccc" + + home = async_account_multi.homes[home_id] + + module_id = "98:76:54:32:10:00:00:49" + assert module_id in home.modules + module = home.modules[module_id] + assert module.device_type == DeviceType.NLC + + strt = int(dt.datetime.fromisoformat("2024-03-15 00:29:51").timestamp()) + end = int(dt.datetime.fromisoformat("2024-03-15 13:45:24").timestamp()) + + await async_account_multi.async_update_measures( + home_id=home_id, + module_id=module_id, + interval=MeasureInterval.HALF_HOUR, + start_time=strt, + end_time=end, + ) + + assert module.historical_data[0] == { + "Wh": 0, + "duration": 30, + "endTime": "2024-03-14T23:59:51Z", + "endTimeUnix": 1710460791, + "energyMode": "peak", + "startTime": "2024-03-14T23:29:52Z", + "startTimeUnix": 1710458991, + } + assert module.historical_data[-1] == { + "Wh": 0, + "duration": 30, + "endTime": "2024-03-15T12:59:51Z", + "endTimeUnix": 1710507591, + "energyMode": "peak", + "startTime": "2024-03-15T12:29:52Z", + "startTimeUnix": 1710505791, + } + assert len(module.historical_data) == 26 + + assert ( + module.sum_energy_elec + == module.sum_energy_elec_peak + module.sum_energy_elec_off_peak + ) + assert module.sum_energy_elec_off_peak == 780 + assert module.sum_energy_elec_peak == 890 + + +async def test_disconnected_main_bridge(async_account_multi): + """Test retrieval of historical measurements.""" + home_id = "aaaaaaaaaaabbbbbbbbbbccc" + + with open( + "fixtures/home_multi_status_error_disconnected.json", + encoding="utf-8", + ) as json_file: + home_status_fixture = json.load(json_file) + mock_home_status_resp = MockResponse(home_status_fixture, 200) + + with patch( + "pyatmo.auth.AbstractAsyncAuth.async_post_api_request", + AsyncMock(return_value=mock_home_status_resp), + ) as mock_request: + try: + await async_account_multi.async_update_status(home_id) + except ApiHomeReachabilityError: + pass # expected error + else: + assert False diff --git a/tests/test_home.py b/tests/test_home.py index 67bf922c..b4772a5b 100644 --- a/tests/test_home.py +++ b/tests/test_home.py @@ -7,7 +7,7 @@ import pyatmo from pyatmo import DeviceType, NoDevice import pytest -import time_machine + from tests.common import MockResponse @@ -145,35 +145,6 @@ async def test_home_event_update(async_account): assert events[1].event_type == "connection" -@time_machine.travel(dt.datetime(2022, 2, 12, 7, 59, 49)) -@pytest.mark.asyncio -async def test_historical_data_retrieval(async_account): - """Test retrieval of historical measurements.""" - home_id = "91763b24c43d3e344f424e8b" - await async_account.async_update_events(home_id=home_id) - home = async_account.homes[home_id] - - module_id = "12:34:56:00:00:a1:4c:da" - assert module_id in home.modules - module = home.modules[module_id] - assert module.device_type == DeviceType.NLPC - - await async_account.async_update_measures(home_id=home_id, module_id=module_id) - assert module.historical_data[0] == { - "Wh": 197, - "duration": 60, - "startTime": "2022-02-05T08:29:50Z", - "endTime": "2022-02-05T09:29:49Z", - } - assert module.historical_data[-1] == { - "Wh": 259, - "duration": 60, - "startTime": "2022-02-12T07:29:50Z", - "endTime": "2022-02-12T08:29:49Z", - } - assert len(module.historical_data) == 168 - - def test_device_types_missing(): """Test handling of missing device types.""" diff --git a/tests/testing_main_template.py b/tests/testing_main_template.py new file mode 100644 index 00000000..e3fe3183 --- /dev/null +++ b/tests/testing_main_template.py @@ -0,0 +1,50 @@ +import pyatmo +import aiohttp +from pyatmo.auth import AbstractAsyncAuth +from aiohttp import ClientError, ClientResponse, ClientSession, ContentTypeError +import asyncio + +from pyatmo.const import MeasureInterval + + +MY_TOKEN_FROM_NETATMO = "MY_TOKEN" + + +class MyAuth(AbstractAsyncAuth): + + async def async_get_access_token(self): + + return MY_TOKEN_FROM_NETATMO + + +async def main(): + session = ClientSession() + async_auth = MyAuth(session) + account = pyatmo.AsyncAccount(async_auth) + + t = asyncio.create_task(account.async_update_topology()) + home_id = "MY_HOME_ID" + module_id = "MY_MODULE_ID" + + await asyncio.gather(t) + + await account.async_update_status(home_id=home_id) + + strt = 1709766000 + 10 * 60 # 1709421000+15*60 + end = 1709852400 + 10 * 60 + await account.async_update_measures( + home_id=home_id, + module_id=module_id, + interval=MeasureInterval.HALF_HOUR, + start_time=strt, + end_time=end, + ) + + print(account) + + +if __name__ == "__main__": + + topology = asyncio.run(main()) + + print(topology)