diff --git a/.github/workflows/publish-to-pypi.yml b/.github/workflows/publish-to-pypi.yml index dc1c567d..6f035024 100644 --- a/.github/workflows/publish-to-pypi.yml +++ b/.github/workflows/publish-to-pypi.yml @@ -3,7 +3,7 @@ name: Publish 📦 to PyPI on: push: tags: - - 'v*' + - "v*" jobs: build-n-publish: @@ -14,7 +14,7 @@ jobs: with: fetch-depth: 0 - name: Set up Python 3.10 - uses: actions/setup-python@v5.0.0 + uses: actions/setup-python@v5.1.1 with: python-version: 3.10.8 - name: Install dependencies @@ -24,11 +24,11 @@ jobs: - name: Build a binary wheel and a source tarball run: >- python -m build . -# - name: Publish 📦 to Test PyPI -# uses: pypa/gh-action-pypi-publish@master -# with: -# password: ${{ secrets.PYPI_TEST_TOKEN }} -# repository_url: https://test.pypi.org/legacy/ + # - name: Publish 📦 to Test PyPI + # uses: pypa/gh-action-pypi-publish@master + # with: + # password: ${{ secrets.PYPI_TEST_TOKEN }} + # repository-url: https://test.pypi.org/legacy/ - name: Publish 📦 to PyPI uses: pypa/gh-action-pypi-publish@release/v1 with: diff --git a/.github/workflows/publish-to-test-pypi.yml b/.github/workflows/publish-to-test-pypi.yml index 7aa636d6..1f9be7fe 100644 --- a/.github/workflows/publish-to-test-pypi.yml +++ b/.github/workflows/publish-to-test-pypi.yml @@ -5,7 +5,6 @@ on: branches: - development - jobs: build-n-publish: name: Build and publish 📦 to TestPyPI @@ -16,7 +15,7 @@ jobs: ref: development fetch-depth: 0 - name: Set up Python 3.10 - uses: actions/setup-python@v5.0.0 + uses: actions/setup-python@v5.1.1 with: python-version: 3.10.8 - name: Install dependencies @@ -30,4 +29,4 @@ jobs: uses: pypa/gh-action-pypi-publish@release/v1 with: password: ${{ secrets.PYPI_TEST_TOKEN }} - repository_url: https://test.pypi.org/legacy/ + repository-url: https://test.pypi.org/legacy/ diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index 54659359..8bf80923 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -23,7 +23,7 @@ jobs: with: fetch-depth: 0 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5.0.0 + uses: actions/setup-python@v5.1.1 with: python-version: ${{ matrix.python-version }} - name: Install dependencies @@ -44,7 +44,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5.0.0 + uses: actions/setup-python@v5.1.1 with: python-version: ${{ matrix.python-version }} - name: Install dependencies @@ -65,7 +65,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5.0.0 + uses: actions/setup-python@v5.1.1 with: python-version: ${{ matrix.python-version }} - name: Run tests with tox diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3a65137d..165c750b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,14 +5,14 @@ exclude: ^(fixtures/) repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.0.292 + rev: v0.4.9 hooks: - id: ruff args: - --fix - repo: https://github.com/asottile/pyupgrade - rev: v3.15.0 + rev: v3.16.0 hooks: - id: pyupgrade args: [--py310-plus] @@ -31,13 +31,13 @@ repos: - id: yesqa - repo: https://github.com/psf/black - rev: 23.9.1 + rev: 24.4.2 hooks: - id: black language_version: python3 - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.5.1 + rev: v1.10.0 hooks: - id: mypy name: mypy @@ -46,7 +46,7 @@ repos: - types-requests - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 # Use the ref you want to point at + rev: v4.6.0 # Use the ref you want to point at hooks: - id: check-ast - id: no-commit-to-branch diff --git a/CHANGELOG.md b/CHANGELOG.md index 83a78fc7..313f13a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- +- ### Changed @@ -25,11 +25,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed -- +- ### Security -- + +## [8.1.0] + +### Added + +- Expose camera person status +- Add NLE support +- Add proper energy support +- Add cooler support +- Add BNS support + +## [8.0.3] + +### Added + +- Add NLLF centralized ventilation controller + +### Fixed + +- Add BNSC switch capability ## [8.0.3] diff --git a/fixtures/getmeasure_12_34_56_00_00_a1_4c_da.json b/fixtures/getmeasure_12_34_56_00_00_a1_4c_da.json deleted file mode 100644 index 9eb499ab..00000000 --- a/fixtures/getmeasure_12_34_56_00_00_a1_4c_da.json +++ /dev/null @@ -1,517 +0,0 @@ -{ - "body": [ - { - "beg_time": 1644049789, - "step_time": 3600, - "value": [ - [ - 197 - ], - [ - 262 - ], - [ - 168 - ], - [ - 219 - ], - [ - 224 - ], - [ - 299 - ], - [ - 343 - ], - [ - 443 - ], - [ - 317 - ], - [ - 229 - ], - [ - 147 - ], - [ - 178 - ], - [ - 340 - ], - [ - 536 - ], - [ - 471 - ], - [ - 397 - ], - [ - 290 - ], - [ - 302 - ], - [ - 309 - ], - [ - 219 - ], - [ - 154 - ], - [ - 163 - ], - [ - 146 - ], - [ - 189 - ], - [ - 256 - ], - [ - 162 - ], - [ - 1288 - ], - [ - 256 - ], - [ - 709 - ], - [ - 310 - ], - [ - 379 - ], - [ - 296 - ], - [ - 230 - ], - [ - 505 - ], - [ - 362 - ], - [ - 611 - ], - [ - 597 - ], - [ - 505 - ], - [ - 431 - ], - [ - 1538 - ], - [ - 265 - ], - [ - 187 - ], - [ - 162 - ], - [ - 150 - ], - [ - 155 - ], - [ - 147 - ], - [ - 211 - ], - [ - 211 - ], - [ - 272 - ], - [ - 271 - ], - [ - 331 - ], - [ - 180 - ], - [ - 184 - ], - [ - 182 - ], - [ - 232 - ], - [ - 288 - ], - [ - 266 - ], - [ - 256 - ], - [ - 249 - ], - [ - 372 - ], - [ - 379 - ], - [ - 585 - ], - [ - 387 - ], - [ - 277 - ], - [ - 223 - ], - [ - 202 - ], - [ - 163 - ], - [ - 143 - ], - [ - 158 - ], - [ - 145 - ], - [ - 232 - ], - [ - 231 - ], - [ - 160 - ], - [ - 261 - ], - [ - 376 - ], - [ - 216 - ], - [ - 202 - ], - [ - 295 - ], - [ - 310 - ], - [ - 235 - ], - [ - 188 - ], - [ - 269 - ], - [ - 250 - ], - [ - 334 - ], - [ - 434 - ], - [ - 353 - ], - [ - 279 - ], - [ - 266 - ], - [ - 226 - ], - [ - 179 - ], - [ - 149 - ], - [ - 146 - ], - [ - 143 - ], - [ - 136 - ], - [ - 173 - ], - [ - 221 - ], - [ - 190 - ], - [ - 177 - ], - [ - 290 - ], - [ - 352 - ], - [ - 252 - ], - [ - 284 - ], - [ - 173 - ], - [ - 165 - ], - [ - 144 - ], - [ - 175 - ], - [ - 268 - ], - [ - 363 - ], - [ - 544 - ], - [ - 515 - ], - [ - 525 - ], - [ - 431 - ], - [ - 225 - ], - [ - 183 - ], - [ - 178 - ], - [ - 155 - ], - [ - 170 - ], - [ - 156 - ], - [ - 169 - ], - [ - 226 - ], - [ - 255 - ], - [ - 273 - ], - [ - 466 - ], - [ - 406 - ], - [ - 333 - ], - [ - 194 - ], - [ - 234 - ], - [ - 271 - ], - [ - 238 - ], - [ - 221 - ], - [ - 205 - ], - [ - 258 - ], - [ - 430 - ], - [ - 446 - ], - [ - 390 - ], - [ - 306 - ], - [ - 223 - ], - [ - 165 - ], - [ - 154 - ], - [ - 147 - ], - [ - 155 - ], - [ - 140 - ], - [ - 153 - ], - [ - 228 - ], - [ - 237 - ], - [ - 201 - ], - [ - 183 - ], - [ - 194 - ], - [ - 135 - ], - [ - 206 - ], - [ - 215 - ], - [ - 147 - ], - [ - 159 - ], - [ - 185 - ], - [ - 168 - ], - [ - 257 - ], - [ - 262 - ], - [ - 141 - ], - [ - 151 - ], - [ - 157 - ], - [ - 133 - ], - [ - 147 - ], - [ - 135 - ], - [ - 139 - ], - [ - 136 - ], - [ - 127 - ], - [ - 169 - ], - [ - 259 - ] - ] - } - ], - "status": "ok", - "time_exec": 0.5533828735351562, - "time_server": 1647935044 -} diff --git a/fixtures/getmeasure_sum_energy_buy_from_grid,sum_energy_buy_from_grid$0,sum_energy_buy_from_grid$1,sum_energy_buy_from_grid$2_12_34_56_00_00_a1_4c_da.json b/fixtures/getmeasure_sum_energy_buy_from_grid,sum_energy_buy_from_grid$0,sum_energy_buy_from_grid$1,sum_energy_buy_from_grid$2_12_34_56_00_00_a1_4c_da.json new file mode 100644 index 00000000..1c1d1694 --- /dev/null +++ b/fixtures/getmeasure_sum_energy_buy_from_grid,sum_energy_buy_from_grid$0,sum_energy_buy_from_grid$1,sum_energy_buy_from_grid$2_12_34_56_00_00_a1_4c_da.json @@ -0,0 +1,517 @@ +{ + "body": [ + { + "beg_time": 1644049789, + "step_time": 3600, + "value": [ + [ null, + 197 + ,null, null], + [null, + 262 + ,null, null], + [null, + 168 + ,null, null], + [null, + 219 + ,null, null], + [null, + 224 + ,null, null], + [null, + 299 + ,null, null], + [null, + 343 + ,null, null], + [null, + 443 + ,null, null], + [null, + 317 + ,null, null], + [null, + 229 + ,null, null], + [null, + 147 + ,null, null], + [null, + 178 + ,null, null], + [null, + 340 + ,null, null], + [null, + 536 + ,null, null], + [null, + 471 + ,null, null], + [null, + 397 + ,null, null], + [null, + 290 + ,null, null], + [null, + 302 + ,null, null], + [null, + 309 + ,null, null], + [null, + 219 + ,null, null], + [null, + 154 + ,null, null], + [null, + 163 + ,null, null], + [null, + 146 + ,null, null], + [null, + 189 + ,null, null], + [null, + 256 + ,null, null], + [null, + 162 + ,null, null], + [null, + 1288 + ,null, null], + [null, + 256 + ,null, null], + [null, + 709 + ,null, null], + [null, + 310 + ,null, null], + [null, + 379 + ,null, null], + [null, + 296 + ,null, null], + [null, + 230 + ,null, null], + [null, + 505 + ,null, null], + [null, + 362 + ,null, null], + [null, + 611 + ,null, null], + [null, + 597 + ,null, null], + [null, + 505 + ,null, null], + [null, + 431 + ,null, null], + [null, + 1538 + ,null, null], + [null, + 265 + ,null, null], + [null, + 187 + ,null, null], + [null, + 162 + ,null, null], + [null, + 150 + ,null, null], + [null, + 155 + ,null, null], + [null, + 147 + ,null, null], + [null, + 211 + ,null, null], + [null, + 211 + ,null, null], + [null, + 272 + ,null, null], + [null, + 271 + ,null, null], + [null, + 331 + ,null, null], + [null, + 180 + ,null, null], + [null, + 184 + ,null, null], + [null, + 182 + ,null, null], + [null, + 232 + ,null, null], + [null, + 288 + ,null, null], + [null, + 266 + ,null, null], + [null, + 256 + ,null, null], + [null, + 249 + ,null, null], + [null, + 372 + ,null, null], + [null, + 379 + ,null, null], + [null, + 585 + ,null, null], + [null, + 387 + ,null, null], + [null, + 277 + ,null, null], + [null, + 223 + ,null, null], + [null, + 202 + ,null, null], + [null, + 163 + ,null, null], + [null, + 143 + ,null, null], + [null, + 158 + ,null, null], + [null, + 145 + ,null, null], + [null, + 232 + ,null, null], + [null, + 231 + ,null, null], + [null, + 160 + ,null, null], + [null, + 261 + ,null, null], + [null, + 376 + ,null, null], + [null, + 216 + ,null, null], + [null, + 202 + ,null, null], + [null, + 295 + ,null, null], + [null, + 310 + ,null, null], + [null, + 235 + ,null, null], + [null, + 188 + ,null, null], + [null, + 269 + ,null, null], + [null, + 250 + ,null, null], + [null, + 334 + ,null, null], + [null, + 434 + ,null, null], + [null, + 353 + ,null, null], + [null, + 279 + ,null, null], + [null, + 266 + ,null, null], + [null, + 226 + ,null, null], + [null, + 179 + ,null, null], + [null, + 149 + ,null, null], + [null, + 146 + ,null, null], + [null, + 143 + ,null, null], + [null, + 136 + ,null, null], + [null, + 173 + ,null, null], + [null, + 221 + ,null, null], + [null, + 190 + ,null, null], + [null, + 177 + ,null, null], + [null, + 290 + ,null, null], + [null, + 352 + ,null, null], + [null, + 252 + ,null, null], + [null, + 284 + ,null, null], + [null, + 173 + ,null, null], + [null, + 165 + ,null, null], + [null, + 144 + ,null, null], + [null, + 175 + ,null, null], + [null, + 268 + ,null, null], + [null, + 363 + ,null, null], + [null, + 544 + ,null, null], + [null, + 515 + ,null, null], + [null, + 525 + ,null, null], + [null, + 431 + ,null, null], + [null, + 225 + ,null, null], + [null, + 183 + ,null, null], + [null, + 178 + ,null, null], + [null, + 155 + ,null, null], + [null, + 170 + ,null, null], + [null, + 156 + ,null, null], + [null, + 169 + ,null, null], + [null, + 226 + ,null, null], + [null, + 255 + ,null, null], + [null, + 273 + ,null, null], + [null, + 466 + ,null, null], + [null, + 406 + ,null, null], + [null, + 333 + ,null, null], + [null, + 194 + ,null, null], + [null, + 234 + ,null, null], + [null, + 271 + ,null, null], + [null, + 238 + ,null, null], + [null, + 221 + ,null, null], + [null, + 205 + ,null, null], + [null, + 258 + ,null, null], + [null, + 430 + ,null, null], + [null, + 446 + ,null, null], + [null, + 390 + ,null, null], + [null, + 306 + ,null, null], + [null, + 223 + ,null, null], + [null, + 165 + ,null, null], + [null, + 154 + ,null, null], + [null, + 147 + ,null, null], + [null, + 155 + ,null, null], + [null, + 140 + ,null, null], + [null, + 153 + ,null, null], + [null, + 228 + ,null, null], + [null, + 237 + ,null, null], + [null, + 201 + ,null, null], + [null, + 183 + ,null, null], + [null, + 194 + ,null, null], + [null, + 135 + ,null, null], + [null, + 206 + ,null, null], + [null, + 215 + ,null, null], + [null, + 147 + ,null, null], + [null, + 159 + ,null, null], + [null, + 185 + ,null, null], + [null, + 168 + ,null, null], + [null, + 257 + ,null, null], + [null, + 262 + ,null, null], + [null, + 141 + ,null, null], + [null, + 151 + ,null, null], + [null, + 157 + ,null, null], + [null, + 133 + ,null, null], + [null, + 147 + ,null, null], + [null, + 135 + ,null, null], + [null, + 139 + ,null, null], + [null, + 136 + ,null, null], + [null, + 127 + ,null, null], + [null, + 169 + ,null, null], + [null, + 259, null, null + ] + ] + } + ], + "status": "ok", + "time_exec": 0.5533828735351562, + "time_server": 1647935044 +} diff --git a/fixtures/getmeasure_sum_energy_buy_from_grid,sum_energy_buy_from_grid$0,sum_energy_buy_from_grid$1,sum_energy_buy_from_grid$2_98_76_54_32_10_00_00_69.json b/fixtures/getmeasure_sum_energy_buy_from_grid,sum_energy_buy_from_grid$0,sum_energy_buy_from_grid$1,sum_energy_buy_from_grid$2_98_76_54_32_10_00_00_69.json new file mode 100644 index 00000000..3e11402e --- /dev/null +++ b/fixtures/getmeasure_sum_energy_buy_from_grid,sum_energy_buy_from_grid$0,sum_energy_buy_from_grid$1,sum_energy_buy_from_grid$2_98_76_54_32_10_00_00_69.json @@ -0,0 +1,247 @@ +{ + "body": [ + { + "beg_time": 1721772900, + "step_time": 1800, + "value": [ + [ + null, + 20, + null, + null + ], + [ + null, + 19, + null, + null + ], + [ + null, + 16, + null, + null + ], + [ + null, + 11, + null, + null + ], + [ + null, + 18, + null, + null + ], + [ + null, + 14, + null, + null + ], + [ + null, + 14, + null, + null + ], + [ + null, + 64, + null, + null + ], + [ + null, + 100, + null, + null + ], + [ + null, + 58, + null, + null + ], + [ + null, + 39, + null, + null + ], + [ + null, + 24, + null, + null + ], + [ + null, + 19, + null, + null + ], + [ + null, + 19, + null, + null + ], + [ + null, + 712, + null, + null + ], + [ + null, + 724, + null, + null + ], + [ + null, + 711, + null, + null + ], + [ + null, + 498, + 212, + null + ], + [ + null, + null, + 717, + null + ], + [ + null, + null, + 714, + null + ], + [ + null, + null, + 714, + null + ], + [ + null, + null, + 711, + null + ], + [ + null, + null, + 706, + null + ], + [ + null, + null, + 704, + null + ], + [ + null, + null, + 706, + null + ], + [ + null, + null, + 709, + null + ], + [ + null, + null, + 714, + null + ], + [ + null, + null, + 712, + null + ], + [ + null, + null, + 238, + 477 + ], + [ + null, + null, + null, + 714 + ], + [ + null, + null, + null, + 722 + ], + [ + null, + null, + null, + 713 + ], + [ + null, + null, + null, + 709 + ], + [ + null, + null, + null, + 714 + ], + [ + null, + null, + 477, + 241 + ], + [ + null, + null, + 710, + null + ], + [ + null, + null, + 710, + null + ], + [ + null, + null, + 707, + null + ], + [ + null, + null, + 16, + null + ] + ] + } + ], + "status": "ok", + "time_exec": 0.025647878646850586, + "time_server": 1721852913 +} \ 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.json b/fixtures/homesdata.json index 7e7e753e..e5623ad4 100644 --- a/fixtures/homesdata.json +++ b/fixtures/homesdata.json @@ -805,6 +805,8 @@ } ], "therm_setpoint_default_duration": 120, + "temperature_control_mode": "cooling", + "cooling_mode": "schedule", "persons": [ { "id": "91827374-7e04-5298-83ad-a0cb8372dff1", diff --git a/fixtures/homesdata_multi.json b/fixtures/homesdata_multi.json new file mode 100644 index 00000000..17a53e74 --- /dev/null +++ b/fixtures/homesdata_multi.json @@ -0,0 +1,6441 @@ +{ + "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": "NLPC", + "name": "Compteur Filtration Local Technique", + "setup_date": 1711797163, + "room_id": "2754296835", + "bridge": "00:04:74:22:52:38" + }, + { + "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/setup.cfg b/setup.cfg index 66bae277..37249ba0 100644 --- a/setup.cfg +++ b/setup.cfg @@ -25,7 +25,7 @@ install_requires = aiohttp>=3.7.4,<4.0.0 oauthlib~=3.1 requests~=2.24 - requests-oauthlib~=1.3 + requests-oauthlib>=1.3,<3.0 python_requires = >=3.10 include_package_data = True package_dir = =src diff --git a/src/pyatmo/__init__.py b/src/pyatmo/__init__.py index 2bba15ea..ca3a8a89 100644 --- a/src/pyatmo/__init__.py +++ b/src/pyatmo/__init__.py @@ -1,8 +1,17 @@ """Expose submodules.""" + 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 @@ -11,6 +20,8 @@ __all__ = [ "AbstractAsyncAuth", "ApiError", + "ApiErrorThrottling", + "ApiHomeReachabilityError", "AsyncAccount", "InvalidHome", "InvalidRoom", diff --git a/src/pyatmo/account.py b/src/pyatmo/account.py index 2986fcbb..ff871573 100644 --- a/src/pyatmo/account.py +++ b/src/pyatmo/account.py @@ -1,4 +1,5 @@ """Support for a Netatmo account.""" + from __future__ import annotations import logging @@ -35,6 +36,7 @@ def __init__(self, auth: AbstractAsyncAuth, favorite_stations: bool = True) -> N self.auth: AbstractAsyncAuth = auth self.user: str | None = None + self.all_homes_id: dict[str, str] = {} self.homes: dict[str, Home] = {} self.raw_data: RawData = {} self.favorite_stations: bool = favorite_stations @@ -48,16 +50,30 @@ 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.""" + if disabled_homes_ids is None: + disabled_homes_ids = [] + 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_homes_id[home_id] = home_name + + if 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 + ) -> None: """Retrieve topology data from /homesdata.""" resp = await self.auth.async_post_api_request( @@ -67,7 +83,7 @@ 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.""" @@ -76,7 +92,7 @@ async def async_update_status(self, home_id: str) -> None: params={"home_id": home_id}, ) raw_data = extract_raw_data(await resp.json(), HOME) - await self.homes[home_id].update(raw_data) + await self.homes[home_id].update(raw_data, do_raise_for_reachability_error=True) async def async_update_events(self, home_id: str) -> None: """Retrieve events from /getevents.""" @@ -104,6 +120,7 @@ async def async_update_measures( home_id: str, module_id: str, start_time: int | None = None, + end_time: int | None = None, interval: MeasureInterval = MeasureInterval.HOUR, days: int = 7, ) -> None: @@ -111,6 +128,7 @@ async def async_update_measures( await getattr(self.homes[home_id].modules[module_id], "async_update_measures")( start_time=start_time, + end_time=end_time, interval=interval, days=days, ) @@ -218,7 +236,7 @@ async def update_devices( {HOME: {"modules": [normalize_weather_attributes(device_data)]}}, ) else: - LOG.debug("No home %s found.", home_id) + LOG.debug("No home %s (%s) found.", home_id, home_id) for module_data in device_data.get("modules", []): module_data["home_id"] = home_id diff --git a/src/pyatmo/auth.py b/src/pyatmo/auth.py index 970e52ba..e401124d 100644 --- a/src/pyatmo/auth.py +++ b/src/pyatmo/auth.py @@ -1,4 +1,5 @@ """Support for Netatmo authentication.""" + from __future__ import annotations from abc import ABC, abstractmethod @@ -16,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__) @@ -145,7 +146,8 @@ async def handle_error_response(self, resp, resp_status, url): """Handle error response.""" try: resp_json = await resp.json() - raise ApiError( + + message = ( f"{resp_status} - " f"{ERRORS.get(resp_status, '')} - " f"{resp_json['error']['message']} " @@ -153,6 +155,15 @@ async def handle_error_response(self, resp, resp_status, url): f"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 fccbd6a6..0b141743 100644 --- a/src/pyatmo/const.py +++ b/src/pyatmo/const.py @@ -1,4 +1,5 @@ """Common constants.""" + from __future__ import annotations from typing import Any @@ -87,7 +88,6 @@ SCHEDULES = "schedules" EVENTS = "events" - STATION_TEMPERATURE_TYPE = "temperature" STATION_PRESSURE_TYPE = "pressure" STATION_HUMIDITY_TYPE = "humidity" @@ -101,3 +101,8 @@ ACCESSORY_WIND_TIME_TYPE = "wind_timeutc" ACCESSORY_GUST_STRENGTH_TYPE = "gust_strength" ACCESSORY_GUST_ANGLE_TYPE = "gust_angle" + +# 2 days of dynamic historical data stored +MAX_HISTORY_TIME_FRAME = 24 * 2 * 3600 + +UNKNOWN = "unknown" diff --git a/src/pyatmo/event.py b/src/pyatmo/event.py index 7c9c201b..c2fa97ed 100644 --- a/src/pyatmo/event.py +++ b/src/pyatmo/event.py @@ -1,4 +1,5 @@ """Module to represent a Netatmo event.""" + from __future__ import annotations from dataclasses import dataclass 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 1b538cdb..9833d5b0 100644 --- a/src/pyatmo/helpers.py +++ b/src/pyatmo/helpers.py @@ -1,4 +1,5 @@ """Collection of helper functions.""" + from __future__ import annotations import logging @@ -38,7 +39,7 @@ def extract_raw_data(resp: Any, tag: str) -> dict[str, Any]: return {"public": resp["body"], "errors": []} if resp is None or "body" not in resp or tag not in resp["body"]: - LOG.debug("Server response: %s", resp) + LOG.debug("Server response (tag: %s): %s", tag, resp) raise NoDevice("No device found, errors in response") if tag == "homes": @@ -48,7 +49,7 @@ def extract_raw_data(resp: Any, tag: str) -> dict[str, Any]: } if not (raw_data := fix_id(resp["body"].get(tag))): - LOG.debug("Server response: %s", resp) + LOG.debug("Server response (tag: %s): %s", tag, resp) raise NoDevice("No device data available") return {tag: raw_data, "errors": resp["body"].get("errors", [])} diff --git a/src/pyatmo/home.py b/src/pyatmo/home.py index 3828ef75..4b46f960 100644 --- a/src/pyatmo/home.py +++ b/src/pyatmo/home.py @@ -1,4 +1,5 @@ """Module to represent a Netatmo home.""" + from __future__ import annotations import logging @@ -19,7 +20,12 @@ RawData, ) from pyatmo.event import Event -from pyatmo.exceptions import InvalidSchedule, InvalidState, NoSchedule +from pyatmo.exceptions import ( + ApiHomeReachabilityError, + InvalidSchedule, + InvalidState, + NoSchedule, +) from pyatmo.modules import Module from pyatmo.person import Person from pyatmo.room import Room @@ -43,6 +49,11 @@ class Home: persons: dict[str, Person] events: dict[str, Event] + temperature_control_mode: str | None = None + therm_mode: str | None = None + therm_setpoint_default_duration: int | None = None + cooling_mode: str | None = None + def __init__(self, auth: AbstractAsyncAuth, raw_data: RawData) -> None: """Initialize a Netatmo home instance.""" @@ -70,6 +81,13 @@ def __init__(self, auth: AbstractAsyncAuth, raw_data: RawData) -> None: } self.events = {} + self.temperature_control_mode = raw_data.get("temperature_control_mode") + self.therm_mode = raw_data.get("therm_mode") + self.therm_setpoint_default_duration = raw_data.get( + "therm_setpoint_default_duration", + ) + self.cooling_mode = raw_data.get("cooling_mode") + def get_module(self, module: dict) -> Module: """Return module.""" @@ -91,12 +109,17 @@ def update_topology(self, raw_data: RawData) -> None: self.name = raw_data.get("name", "Unknown") raw_modules = raw_data.get("modules", []) + + self.temperature_control_mode = raw_data.get("temperature_control_mode") + self.therm_mode = raw_data.get("therm_mode") + self.therm_setpoint_default_duration = raw_data.get( + "therm_setpoint_default_duration", + ) + self.cooling_mode = raw_data.get("cooling_mode") + for module in raw_modules: if (module_id := module["id"]) not in self.modules: - self.modules[module_id] = getattr(modules, module["type"])( - home=self, - module=module, - ) + self.modules[module_id] = self.get_module(module) else: self.modules[module_id].update_topology(module) @@ -124,27 +147,45 @@ def update_topology(self, raw_data: RawData) -> None: for s in raw_data.get(SCHEDULES, []) } - async def update(self, raw_data: RawData) -> None: + async def update( + self, + raw_data: RawData, + do_raise_for_reachability_error=False, + ) -> None: """Update home with the latest data.""" - + has_error = False for module in raw_data.get("errors", []): + has_error = True 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) + for person_status in data.get("persons", []): + if person := self.persons.get(person_status["id"]): + person.update(person_status) + 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, @@ -156,6 +197,16 @@ async def update(self, raw_data: RawData) -> None: ], ) + if ( + do_raise_for_reachability_error + and has_error + and has_one_module_reachable is False + and has_an_update is False + ): + raise ApiHomeReachabilityError( + "No Home update could be performed, all modules unreachable and not updated", + ) + def get_selected_schedule(self) -> Schedule | None: """Return selected schedule for given home.""" @@ -174,6 +225,11 @@ def has_otm(self) -> bool: return any("OTM" in room.device_types for room in self.rooms.values()) + def has_bns(self) -> bool: + """Check if any room has a BNS device.""" + + return any("BNS" in room.device_types for room in self.rooms.values()) + def get_hg_temp(self) -> float | None: """Return frost guard temperature value for given home.""" diff --git a/src/pyatmo/modules/__init__.py b/src/pyatmo/modules/__init__.py index d9364ddb..dc1c127d 100644 --- a/src/pyatmo/modules/__init__.py +++ b/src/pyatmo/modules/__init__.py @@ -1,4 +1,5 @@ """Expose submodules.""" + from .base_class import Place from .bticino import ( BNAB, diff --git a/src/pyatmo/modules/base_class.py b/src/pyatmo/modules/base_class.py index 41701547..5541a23c 100644 --- a/src/pyatmo/modules/base_class.py +++ b/src/pyatmo/modules/base_class.py @@ -1,19 +1,24 @@ """Base class for Netatmo entities.""" + 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 +from pyatmo.const import MAX_HISTORY_TIME_FRAME, RawData from pyatmo.modules.device_types import DeviceType if TYPE_CHECKING: from pyatmo.event import EventTypes from pyatmo.home import Home +from time import time + LOG = logging.getLogger(__name__) @@ -26,6 +31,7 @@ "monitoring": lambda x, _: x.get("monitoring", False) == "on", "battery_level": lambda x, y: x.get("battery_vp", x.get("battery_level")), "place": lambda x, _: Place(x.get("place")), + "target_position__step": lambda x, _: x.get("target_position:step"), } @@ -49,6 +55,9 @@ class EntityBase: entity_id: str home: Home bridge: str | None + history_features: set[str] + history_features_values: dict[str, [int, int]] | {} + name: str | None class NetatmoBase(EntityBase, ABC): @@ -59,6 +68,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.""" @@ -80,6 +91,54 @@ 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__: + val = getattr(self, hist_feature) + if val is None: + continue + + self.add_history_data(hist_feature, val, now) + + def add_history_data(self, feature: str, value, time: int) -> None: + """Add historical data at the given time.""" + + # get the feature values rolling buffer + hist_f = self.history_features_values.setdefault(feature, []) + if not hist_f or hist_f[-1][0] <= time: + hist_f.append((time, value, self.entity_id)) + else: + i = bisect.bisect_left(hist_f, time, key=itemgetter(0)) + + if i < len(hist_f): + if hist_f[i][0] == time: + hist_f[i] = (time, value, self.entity_id) + i = None + + if i is not None: + hist_f.insert(i, (time, value, self.entity_id)) + + # keep timing history to a maximum representative time + while len(hist_f) > 0 and hist_f[-1][0] - hist_f[0][0] > MAX_HISTORY_TIME_FRAME: + 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 ebfd32d0..abf8d1c8 100644 --- a/src/pyatmo/modules/bticino.py +++ b/src/pyatmo/modules/bticino.py @@ -1,9 +1,17 @@ """Module to represent BTicino modules.""" + from __future__ import annotations import logging -from pyatmo.modules.module import Dimmer, Module, Shutter, Switch +from pyatmo.modules.module import ( + DimmableMixin, + Module, + Shutter, + ShutterMixin, + Switch, + SwitchMixin, +) LOG = logging.getLogger(__name__) @@ -36,7 +44,7 @@ class BNMS(Shutter): """BTicino motorized shade.""" -class BNAS(Shutter): +class BNAS(ShutterMixin, Module): """BTicino automatic shutter.""" @@ -60,9 +68,9 @@ class BNTR(Module): """BTicino radiator thermostat.""" -class BNIL(Switch): - """BTicino itelligent light.""" +class BNIL(SwitchMixin, Module): + """BTicino intelligent light.""" -class BNLD(Dimmer): +class BNLD(DimmableMixin, SwitchMixin, Module): """BTicino dimmer light.""" diff --git a/src/pyatmo/modules/device_types.py b/src/pyatmo/modules/device_types.py index 41ac94ff..6b2a27a3 100644 --- a/src/pyatmo/modules/device_types.py +++ b/src/pyatmo/modules/device_types.py @@ -1,4 +1,5 @@ """Definitions of Netatmo devices types.""" + from __future__ import annotations from enum import Enum diff --git a/src/pyatmo/modules/idiamant.py b/src/pyatmo/modules/idiamant.py index 2753f0cf..a968205e 100644 --- a/src/pyatmo/modules/idiamant.py +++ b/src/pyatmo/modules/idiamant.py @@ -1,4 +1,5 @@ """Module to represent iDiamant modules.""" + from __future__ import annotations import logging diff --git a/src/pyatmo/modules/legrand.py b/src/pyatmo/modules/legrand.py index ebf954c2..058a5df8 100644 --- a/src/pyatmo/modules/legrand.py +++ b/src/pyatmo/modules/legrand.py @@ -1,4 +1,5 @@ """Module to represent Legrand modules.""" + from __future__ import annotations import logging @@ -6,11 +7,12 @@ from pyatmo.modules.module import ( BatteryMixin, ContactorMixin, + DimmableMixin, Dimmer, - EnergyMixin, + EnergyHistoryLegacyMixin, + EnergyHistoryMixin, Fan, FirmwareMixin, - HistoryMixin, Module, OffloadMixin, PowerMixin, @@ -30,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.""" @@ -46,7 +48,7 @@ class NLPO(ContactorMixin, OffloadMixin, Switch): """Legrand contactor.""" -class NLPT(Switch): +class NLPT(Switch, OffloadMixin): """Legrand latching relay/teleruptor.""" @@ -74,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.""" @@ -94,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): - """Legrand / BTicino connected ecometer.""" +class NLE(FirmwareMixin, EnergyHistoryLegacyMixin, Module): + """Legrand / BTicino connected ecometer. no power supported for the NLE (in the home status API).""" -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.""" @@ -114,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.""" @@ -134,7 +136,7 @@ class NLUO(Dimmer): """Legrand NLUO device stub.""" -class NLLF(Fan): +class NLLF(Fan, PowerMixin, EnergyHistoryMixin): """Legrand NLLF fan/ventilation device.""" @@ -158,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 b4f1d81a..2f5a0296 100644 --- a/src/pyatmo/modules/module.py +++ b/src/pyatmo/modules/module.py @@ -1,7 +1,8 @@ """Module to represent a Netatmo module.""" + from __future__ import annotations -from datetime import datetime, timezone +from datetime import datetime, timedelta, timezone from enum import Enum import logging from typing import TYPE_CHECKING, Any @@ -17,6 +18,9 @@ from pyatmo.event import Event from pyatmo.home import Home +from operator import itemgetter +from time import time + LOG = logging.getLogger(__name__) ModuleT = dict[str, Any] @@ -40,6 +44,8 @@ "device_category", "device_type", "features", + "history_features", + "history_features_values", } @@ -222,6 +228,16 @@ def __init__(self, home: Home, module: ModuleT): self.boiler_status: bool | None = None +class CoolerMixin(EntityBase): + """Mixin for cooler data.""" + + def __init__(self, home: Home, module: ModuleT): + """Initialize cooler mixin.""" + + super().__init__(home, module) # type: ignore # mypy issue 4335 + self.cooler_status: bool | None = None + + class BatteryMixin(EntityBase): """Mixin for battery data.""" @@ -288,16 +304,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.""" @@ -306,6 +312,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): @@ -402,21 +409,32 @@ async def async_set_fan_speed(self, speed: int) -> bool: class ShutterMixin(EntityBase): """Mixin for shutter data.""" + __open_position = 100 + __close_position = 0 + __stop_position = -1 + __preferred_position = -2 + def __init__(self, home: Home, module: ModuleT): """Initialize shutter mixin.""" super().__init__(home, module) # type: ignore # mypy issue 4335 self.current_position: int | None = None self.target_position: int | None = None + self.target_position__step: int | None = None async def async_set_target_position(self, target_position: int) -> bool: """Set shutter to target position.""" + # in case of a too low value, we default to stop and not the preferred position + # We check against __preferred_position that is the lower known value + if target_position < self.__preferred_position: + target_position = self.__stop_position + json_roller_shutter = { "modules": [ { "id": self.entity_id, - "target_position": max(min(100, target_position), -1), + "target_position": min(self.__open_position, target_position), "bridge": self.bridge, }, ], @@ -426,17 +444,22 @@ async def async_set_target_position(self, target_position: int) -> bool: async def async_open(self) -> bool: """Open shutter.""" - return await self.async_set_target_position(100) + return await self.async_set_target_position(self.__open_position) async def async_close(self) -> bool: """Close shutter.""" - return await self.async_set_target_position(0) + return await self.async_set_target_position(self.__close_position) async def async_stop(self) -> bool: """Stop shutter.""" - return await self.async_set_target_position(-1) + return await self.async_set_target_position(self.__stop_position) + + async def async_move_to_preferred_position(self) -> bool: + """Move shutter to preferred position.""" + + return await self.async_set_target_position(self.__preferred_position) class CameraMixin(EntityBase): @@ -601,18 +624,64 @@ class MeasureType(Enum): 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" + 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" + SUM_ENERGY_ELEC_LEGACY = "sum_energy_elec" + SUM_ENERGY_ELEC_BASIC_LEGACY = "sum_energy_elec$0" + SUM_ENERGY_ELEC_PEAK_LEGACY = "sum_energy_elec$1" + SUM_ENERGY_ELEC_OFF_PEAK_LEGACY = "sum_energy_elec$2" + + +MEASURE_INTERVAL_TO_SECONDS = { + MeasureInterval.HALF_HOUR: 1800, + MeasureInterval.HOUR: 3600, + MeasureInterval.THREE_HOURS: 10800, + MeasureInterval.DAY: 86400, + MeasureInterval.WEEK: 604800, + MeasureInterval.MONTH: 2592000, +} +ENERGY_FILTERS = f"{MeasureType.SUM_ENERGY_ELEC.value},{MeasureType.SUM_ENERGY_ELEC_BASIC.value},{MeasureType.SUM_ENERGY_ELEC_PEAK.value},{MeasureType.SUM_ENERGY_ELEC_OFF_PEAK.value}" +ENERGY_FILTERS_LEGACY = f"{MeasureType.SUM_ENERGY_ELEC_LEGACY.value},{MeasureType.SUM_ENERGY_ELEC_BASIC_LEGACY.value},{MeasureType.SUM_ENERGY_ELEC_PEAK_LEGACY.value},{MeasureType.SUM_ENERGY_ELEC_OFF_PEAK_LEGACY.value}" +ENERGY_FILTERS_MODES = ["generic", "basic", "peak", "off_peak"] -class HistoryMixin(EntityBase): - """Mixin for history data.""" + +def compute_riemann_sum( + power_data: list[tuple[int, float]], conservative: bool = False +): + """Compute energy from power with a rieman sum.""" + + delta_energy = 0 + if power_data and 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 + + +class EnergyHistoryMixin(EntityBase): + """Mixin for Energy history data.""" def __init__(self, home: Home, module: ModuleT): """Initialize history mixin.""" @@ -620,26 +689,300 @@ 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._anchor_for_power_adjustment: int | None = None + self.in_reset: bool | False = False + + def reset_measures(self, start_power_time, in_reset=True): + """Reset energy measures.""" + self.in_reset = in_reset + self.historical_data = [] + if start_power_time is None: + self._anchor_for_power_adjustment = start_power_time + else: + self._anchor_for_power_adjustment = int(start_power_time.timestamp()) + self.sum_energy_elec = 0 + self.sum_energy_elec_peak = 0 + self.sum_energy_elec_off_peak = 0 + + 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 not self.in_reset: + if to_ts is None: + to_ts = int(time()) + + from_ts = self._anchor_for_power_adjustment + + 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 = compute_riemann_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, + ) async def async_update_measures( self, start_time: int | None = None, + end_time: int | None = None, interval: MeasureInterval = MeasureInterval.HOUR, days: int = 7, ) -> 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 + end = datetime.fromtimestamp(end_time) + start_time = end - timedelta(days=days) + start_time = int(start_time.timestamp()) + + 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 + + filters, raw_data = await self._energy_API_calls(start_time, end_time, interval) + + hist_good_vals = await self._get_aligned_energy_values_and_mode( + start_time, end_time, delta_range, raw_data + ) + + self.historical_data = [] + 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 + + # no data at all: we know nothing for the end: best guess, it is the start + self._anchor_for_power_adjustment = start_time + + 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, + ) + + 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, + ): + computed_start = 0 + computed_end = 0 + computed_end_for_calculus = 0 + for cur_start_time, val, vals in hist_good_vals: + self.sum_energy_elec += val + + modes = [] + val_modes = [] + + for i, v in enumerate(vals): + if v is not None: + modes.append(ENERGY_FILTERS_MODES[i]) + val_modes.append(v) + if ENERGY_FILTERS_MODES[i] == "off_peak": + self.sum_energy_elec_off_peak += v + elif ENERGY_FILTERS_MODES[i] == "peak": + self.sum_energy_elec_peak += v + + 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 + + # - delta_range not sure, revert ... it seems the energy value effectively stops at those mid values + computed_end_for_calculus = c_end # - delta_range + + 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": (2 * delta_range) // 60, + "startTime": start_time_string, + "endTime": end_time_string, + "Wh": val, + "energyMode": modes, + "WhPerModes": val_modes, + "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._anchor_for_power_adjustment = computed_end_for_calculus + + async def _get_aligned_energy_values_and_mode( + self, start_time, end_time, delta_range, raw_data + ): + hist_good_vals = [] + values_lots = raw_data + + 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="beg_time missing", + body=values_lots, + ) + raise ApiError( + f"Energy badly formed resp beg_time missing: {values_lots} - " + f"module: {self.name}" + ) 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="step_time missing", + body=values_lots, + ) + 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", []): + vals = [] + val = 0 + for v in val_arr: + if v is not None: + v = int(v) + val += v + vals.append(v) + else: + vals.append(None) + + hist_good_vals.append((cur_start_time, val, vals)) + cur_start_time = cur_start_time + interval_sec + + hist_good_vals = sorted(hist_good_vals, key=itemgetter(0)) + return hist_good_vals + + def _get_energy_filers(self): + return ENERGY_FILTERS + + async def _energy_API_calls(self, start_time, end_time, interval): + filters = self._get_energy_filers() - data_point = MeasureType.SUM_ENERGY_ELEC_BASIC.value params = { "device_id": self.bridge, "module_id": self.entity_id, "scale": interval.value, - "type": data_point, + "type": filters, "date_begin": start_time, "date_end": end_time, } @@ -648,27 +991,30 @@ async def async_update_measures( endpoint=GETMEASURE_ENDPOINT, params=params, ) - raw_data = await resp.json() - data = raw_data["body"][0] - interval_sec = int(data["step_time"]) - interval_min = interval_sec // 60 + rw_dt_f = await resp.json() + rw_dt = rw_dt_f.get("body") - 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 - 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], - }, + if rw_dt is None: + self._log_energy_error( + start_time, end_time, msg=f"direct from {filters}", body=rw_dt_f ) + raise ApiError( + f"Energy badly formed resp: {rw_dt_f} - " + f"module: {self.name} - " + f"when accessing '{filters}'" + ) + + raw_data = rw_dt - start_time = end_time + return filters, raw_data + + +class EnergyHistoryLegacyMixin(EnergyHistoryMixin): + """Mixin for Energy history data, Using legacy APis (used for NLE).""" + + def _get_energy_filers(self): + return ENERGY_FILTERS_LEGACY class Module(NetatmoBase): @@ -703,6 +1049,15 @@ async def update(self, raw_data: RawData) -> None: self.update_topology(raw_data) self.update_features() + # If we have an NLE as a bridge all its bridged modules will have to be reachable + if self.device_type == DeviceType.NLE: + # if there is a bridge it means it is a leaf + if self.bridge: + self.reachable = True + elif self.modules: + # this NLE is a bridge itself : make it not available + self.reachable = False + if not self.reachable and self.modules: # Update bridged modules and associated rooms for module_id in self.modules: @@ -742,7 +1097,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 30edc205..047c4eb1 100644 --- a/src/pyatmo/modules/netatmo.py +++ b/src/pyatmo/modules/netatmo.py @@ -1,4 +1,5 @@ """Module to represent Netatmo modules.""" + from __future__ import annotations from dataclasses import dataclass diff --git a/src/pyatmo/modules/smarther.py b/src/pyatmo/modules/smarther.py index b7a3df8d..871365ee 100644 --- a/src/pyatmo/modules/smarther.py +++ b/src/pyatmo/modules/smarther.py @@ -1,12 +1,19 @@ """Module to represent Smarther modules.""" + from __future__ import annotations import logging -from pyatmo.modules.module import BoilerMixin, FirmwareMixin, Module, WifiMixin +from pyatmo.modules.module import ( + BoilerMixin, + CoolerMixin, + FirmwareMixin, + Module, + WifiMixin, +) LOG = logging.getLogger(__name__) -class BNS(FirmwareMixin, BoilerMixin, WifiMixin, Module): +class BNS(FirmwareMixin, BoilerMixin, CoolerMixin, WifiMixin, Module): """Smarther thermostat.""" diff --git a/src/pyatmo/modules/somfy.py b/src/pyatmo/modules/somfy.py index 05c0f34d..67c69f98 100644 --- a/src/pyatmo/modules/somfy.py +++ b/src/pyatmo/modules/somfy.py @@ -1,4 +1,5 @@ """Module to represent somfy modules.""" + from __future__ import annotations import logging diff --git a/src/pyatmo/person.py b/src/pyatmo/person.py index bb4cbf12..9ab89b2c 100644 --- a/src/pyatmo/person.py +++ b/src/pyatmo/person.py @@ -1,4 +1,5 @@ """Module to represent a Netatmo person.""" + from __future__ import annotations from dataclasses import dataclass @@ -20,6 +21,8 @@ class Person(NetatmoBase): pseudo: str | None url: str | None + out_of_sight: bool | None = None + last_seen: int | None = None def __init__(self, home: Home, raw_data: RawData) -> None: """Initialize a Netatmo person instance.""" @@ -28,3 +31,8 @@ def __init__(self, home: Home, raw_data: RawData) -> None: self.home = home self.pseudo = raw_data.get("pseudo") self.url = raw_data.get("url") + + def update(self, raw_data: RawData) -> None: + """Update person data.""" + self.out_of_sight = raw_data.get("out_of_sight") + self.last_seen = raw_data.get("last_seen") diff --git a/src/pyatmo/room.py b/src/pyatmo/room.py index 831d6e1e..a07d0077 100644 --- a/src/pyatmo/room.py +++ b/src/pyatmo/room.py @@ -1,11 +1,19 @@ """Module to represent a Netatmo room.""" + from __future__ import annotations from dataclasses import dataclass import logging from typing import TYPE_CHECKING, Any -from pyatmo.const import FROSTGUARD, HOME, MANUAL, SETROOMTHERMPOINT_ENDPOINT, RawData +from pyatmo.const import ( + FROSTGUARD, + HOME, + MANUAL, + SETROOMTHERMPOINT_ENDPOINT, + UNKNOWN, + RawData, +) from pyatmo.modules.base_class import NetatmoBase from pyatmo.modules.device_types import DeviceType @@ -28,15 +36,25 @@ class Room(NetatmoBase): climate_type: DeviceType | None = None - heating_power_request: int | None = None humidity: int | None = None + therm_measured_temperature: float | None = None + reachable: bool | None = None + + heating_power_request: int | None = None therm_setpoint_temperature: float | None = None therm_setpoint_mode: str | None = None - therm_measured_temperature: float | None = None therm_setpoint_start_time: int | None = None therm_setpoint_end_time: int | None = None + anticipating: bool | None = None + open_window: bool | None = None + + cooling_setpoint_temperature: float | None = None + cooling_setpoint_start_time: int | None = None + cooling_setpoint_end_time: int | None = None + cooling_setpoint_mode: str | None = None + def __init__( self, home: Home, @@ -59,7 +77,7 @@ def __init__( def update_topology(self, raw_data: RawData) -> None: """Update room topology.""" - self.name = raw_data["name"] + self.name = raw_data.get("name", UNKNOWN) self.modules = { m_id: m for m_id, m in self.home.modules.items() @@ -84,19 +102,37 @@ def evaluate_device_type(self) -> None: elif "BNS" in self.device_types: self.climate_type = DeviceType.BNS self.features.add("humidity") + elif "BNTH" in self.device_types: + self.climate_type = DeviceType.BNTH def update(self, raw_data: RawData) -> None: """Update room data.""" - self.heating_power_request = raw_data.get("heating_power_request") self.humidity = raw_data.get("humidity") - self.reachable = raw_data.get("reachable") + if self.climate_type == DeviceType.BNTH: + # BNTH is wired, so the room is always reachable + self.reachable = True + else: + self.reachable = raw_data.get("reachable") + self.therm_measured_temperature = raw_data.get("therm_measured_temperature") + + self.reachable = raw_data.get("reachable") + + self.heating_power_request = raw_data.get("heating_power_request") self.therm_setpoint_mode = raw_data.get("therm_setpoint_mode") self.therm_setpoint_temperature = raw_data.get("therm_setpoint_temperature") self.therm_setpoint_start_time = raw_data.get("therm_setpoint_start_time") self.therm_setpoint_end_time = raw_data.get("therm_setpoint_end_time") + self.anticipating = raw_data.get("anticipating") + self.open_window = raw_data.get("open_window") + + self.cooling_setpoint_temperature = raw_data.get("cooling_setpoint_temperature") + self.cooling_setpoint_start_time = raw_data.get("cooling_setpoint_start_time") + self.cooling_setpoint_end_time = raw_data.get("cooling_setpoint_end_time") + self.cooling_setpoint_mode = raw_data.get("cooling_setpoint_mode") + async def async_therm_manual( self, temp: float | None = None, @@ -127,7 +163,9 @@ async def async_therm_set( mode = MODE_MAP.get(mode, mode) if "NATherm1" in self.device_types or ( - "NRV" in self.device_types and not self.home.has_otm() + "NRV" in self.device_types + and not self.home.has_otm() + and not self.home.has_bns() ): await self._async_set_thermpoint(mode, temp, end_time) diff --git a/src/pyatmo/schedule.py b/src/pyatmo/schedule.py index 3b90336b..d603801e 100644 --- a/src/pyatmo/schedule.py +++ b/src/pyatmo/schedule.py @@ -1,4 +1,5 @@ """Module to represent a Netatmo schedule.""" + from __future__ import annotations from dataclasses import dataclass diff --git a/tests/common.py b/tests/common.py index a210be34..3bff5404 100644 --- a/tests/common.py +++ b/tests/common.py @@ -1,4 +1,5 @@ """Define shared test helpers.""" + # pylint: disable=redefined-outer-name, protected-access, unused-argument from __future__ import annotations @@ -60,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 0b0c7703..bd9bfdea 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,5 @@ """Define shared fixtures.""" + # pylint: disable=redefined-outer-name, protected-access from contextlib import contextmanager from unittest.mock import AsyncMock, patch @@ -6,7 +7,7 @@ import pyatmo import pytest -from .common import fake_post_request +from .common import fake_post_request, fake_post_request_multi @contextmanager @@ -43,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_camera.py b/tests/test_camera.py index d9d3018e..d0d19100 100644 --- a/tests/test_camera.py +++ b/tests/test_camera.py @@ -1,4 +1,5 @@ """Define tests for camera module.""" + import json from unittest.mock import AsyncMock, patch @@ -22,7 +23,10 @@ async def test_async_camera_NACamera(async_home): # pylint: disable=invalid-nam assert module.local_url == "http://192.168.0.123/678460a0d47e5618699fb31169e2b47d" person_id = "91827374-7e04-5298-83ad-a0cb8372dff1" assert person_id in module.home.persons - assert module.home.persons[person_id].pseudo == "John Doe" + person = module.home.persons[person_id] + assert person.pseudo == "John Doe" + assert person.out_of_sight + assert person.last_seen == 1557071156 @pytest.mark.asyncio diff --git a/tests/test_climate.py b/tests/test_climate.py index dbaa46e0..45f9d628 100644 --- a/tests/test_climate.py +++ b/tests/test_climate.py @@ -1,4 +1,5 @@ """Define tests for climate module.""" + import json from unittest.mock import AsyncMock, patch diff --git a/tests/test_energy.py b/tests/test_energy.py index 06ec2408..511bf815 100644 --- a/tests/test_energy.py +++ b/tests/test_energy.py @@ -1,7 +1,15 @@ """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.modules.module import EnergyHistoryMixin, MeasureInterval import pytest +import time_machine + +from tests.common import MockResponse # pylint: disable=F6401 @@ -14,3 +22,127 @@ 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 the 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": ["basic"], + "WhPerModes": [197], + "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": ["basic"], + "WhPerModes": [259], + "startTime": "2022-02-12T06:59:50Z", + "startTimeUnix": 1644649189, + } + assert len(module.historical_data) == 168 + + +@time_machine.travel(dt.datetime(2024, 7, 24, 22, 00, 10)) +@pytest.mark.asyncio +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:69" + assert module_id in home.modules + module = home.modules[module_id] + assert module.device_type == DeviceType.NLPC + + strt = int(dt.datetime.fromisoformat("2024-07-24 00:00:00").timestamp()) + end_time = int(dt.datetime.fromisoformat("2024-07-24 22:27:00").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": 20, + "duration": 30, + "endTime": "2024-07-23T22:30:00Z", + "endTimeUnix": 1721773800, + "energyMode": ["basic"], + "WhPerModes": [20], + "startTime": "2024-07-23T22:00:01Z", + "startTimeUnix": 1721772000, + } + assert module.historical_data[17] == { + "Wh": 710, + "WhPerModes": [498, 212], + "duration": 30, + "endTime": "2024-07-24T07:00:00Z", + "endTimeUnix": 1721804400, + "energyMode": ["basic", "peak"], + "startTime": "2024-07-24T06:30:01Z", + "startTimeUnix": 1721802600, + } + + assert module.historical_data[-1] == { + "Wh": 16, + "WhPerModes": [16], + "duration": 30, + "endTime": "2024-07-24T17:30:00Z", + "endTimeUnix": 1721842200, + "energyMode": ["peak"], + "startTime": "2024-07-24T17:00:01Z", + "startTimeUnix": 1721840400, + } + + assert len(module.historical_data) == 39 + assert module.sum_energy_elec == 17547 + assert module.sum_energy_elec_off_peak == 4290 + assert module.sum_energy_elec_peak == 10177 + + +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 5b1506c4..ac4a91c3 100644 --- a/tests/test_home.py +++ b/tests/test_home.py @@ -1,12 +1,12 @@ """Define tests for home module.""" -import datetime as dt + +# import datetime as dt import json from unittest.mock import AsyncMock, patch import pyatmo from pyatmo import DeviceType, NoDevice import pytest -import time_machine from tests.common import MockResponse @@ -39,6 +39,8 @@ async def test_async_home(async_home): module = async_home.modules[module_id] assert module.device_type == DeviceType.NOC + assert async_home.temperature_control_mode == "cooling" + @pytest.mark.asyncio async def test_async_home_set_schedule(async_home): @@ -144,35 +146,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/test_shutter.py b/tests/test_shutter.py index b2f3df78..f1a26ee3 100644 --- a/tests/test_shutter.py +++ b/tests/test_shutter.py @@ -1,4 +1,5 @@ """Define tests for shutter module.""" + import json from unittest.mock import AsyncMock, patch @@ -83,6 +84,12 @@ def gen_json_data(position): endpoint="api/setstate", ) + assert await module.async_move_to_preferred_position() + mock_resp.assert_awaited_with( + params=gen_json_data(-2), + endpoint="api/setstate", + ) + assert await module.async_set_target_position(47) mock_resp.assert_awaited_with( params=gen_json_data(47), diff --git a/tests/testing_main_template.py b/tests/testing_main_template.py new file mode 100644 index 00000000..5d17423a --- /dev/null +++ b/tests/testing_main_template.py @@ -0,0 +1,48 @@ +import asyncio + +from aiohttp import ClientSession +import pyatmo +from pyatmo.auth import AbstractAsyncAuth +from pyatmo.modules.module 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)