From 0d14aec32fa0c82c534f88d22ec3c7e643d585ad Mon Sep 17 00:00:00 2001 From: Emanuele Palazzetti Date: Tue, 5 Mar 2024 18:31:39 +0100 Subject: [PATCH] fix(device): discard Cloud APIs empty states (connection resets) (#153) --- .../econnect_metronet/devices.py | 6 ++ tests/test_devices.py | 59 +++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/custom_components/econnect_metronet/devices.py b/custom_components/econnect_metronet/devices.py index 88064ef..c88aeb1 100644 --- a/custom_components/econnect_metronet/devices.py +++ b/custom_components/econnect_metronet/devices.py @@ -295,6 +295,12 @@ def update(self): self.connected = False raise err + # `last_id` equal to 1 means the connection has been reset and the update + # is an empty state. See: https://github.com/palazzem/ha-econnect-alarm/issues/148 + if sectors.get("last_id") == 1: + _LOGGER.debug("Device | The connection has been reset, skipping the update") + return self._inventory + # Update the _inventory self.connected = True self._inventory.update({q.SECTORS: sectors["sectors"]}) diff --git a/tests/test_devices.py b/tests/test_devices.py index 53c7fef..7d9b70d 100644 --- a/tests/test_devices.py +++ b/tests/test_devices.py @@ -1,4 +1,5 @@ import pytest +import responses from elmo import query as q from elmo.api.exceptions import CodeError, CredentialError, LockError, ParseError from homeassistant.const import ( @@ -22,6 +23,8 @@ ) from custom_components.econnect_metronet.devices import AlarmDevice +from .fixtures import responses as r + def test_device_constructor(client): """Should initialize defaults attributes to run properly.""" @@ -601,6 +604,62 @@ def test_device_inventory_update_managed_sectors(alarm_device): } +def test_device_inventory_update_after_connection_reset(mocker, alarm_device): + # Ensure that after a connection reset (last_id == 1), the inventory is not updated + # Regression test for: https://github.com/palazzem/ha-econnect-alarm/issues/148 + AREAS = """[ + { + "Active": false, + "ActivePartial": false, + "Max": false, + "Activable": false, + "ActivablePartial": false, + "InUse": true, + "Id": 1, + "Index": 0, + "Element": 1, + "CommandId": 0, + "InProgress": false + }, + { + "Active": false, + "ActivePartial": false, + "Max": false, + "Activable": false, + "ActivablePartial": false, + "InUse": true, + "Id": 1, + "Index": 1, + "Element": 2, + "CommandId": 0, + "InProgress": false + }, + { + "Active": false, + "ActivePartial": false, + "Max": false, + "Activable": false, + "ActivablePartial": false, + "InUse": true, + "Id": 1, + "Index": 2, + "Element": 3, + "CommandId": 0, + "InProgress": false + } + ]""" + # Test + with responses.RequestsMock() as server: + server.add(responses.POST, "https://example.com/api/areas", body=AREAS, status=200) + server.add(responses.POST, "https://example.com/api/inputs", body=r.INPUTS, status=200) + server.add(responses.POST, "https://example.com/api/outputs", body=r.OUTPUTS, status=200) + server.add(responses.POST, "https://example.com/api/statusadv", body=r.STATUS, status=200) + inventory = alarm_device.update() + assert inventory[q.SECTORS][0]["status"] is True + assert inventory[q.SECTORS][1]["status"] is True + assert inventory[q.SECTORS][2]["status"] is False + + class TestInputsView: def test_property_populated(self, alarm_device): """Should check if the device property is correctly populated"""