Skip to content

Commit a664401

Browse files
authored
fix(client): prevent KeyError when inputs/outputs/sectors are not in sync with the cloud (#132)
1 parent a254504 commit a664401

File tree

2 files changed

+135
-1
lines changed

2 files changed

+135
-1
lines changed

src/elmo/api/client.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -653,13 +653,16 @@ def query(self, query):
653653
# Address potential data inconsistency between cloud data and main unit.
654654
# In some installations, they may be out of sync, resulting in the cloud
655655
# providing a sector/input/output that doesn't actually exist in the main unit.
656+
# This case happens also when all inputs or sectors or outputs are used in the
657+
# main unit, but their strings are not synchronized with the cloud.
656658
# To handle this, we default the name to "Unknown" if its description
657659
# isn't found in the cloud data to prevent KeyError.
660+
description = descriptions.get(query, {})
658661
item = {
659662
"id": entry.get("Id"),
660663
"index": entry.get("Index"),
661664
"element": entry.get("Element"),
662-
"name": descriptions[query].get(entry["Index"], "Unknown"),
665+
"name": description.get(entry["Index"], "Unknown"),
663666
}
664667

665668
if query == q.SECTORS:

tests/test_client.py

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1928,6 +1928,137 @@ def test_client_get_outputs_status(server, mocker):
19281928
}
19291929

19301930

1931+
def test_client_missing_sectors_strings(server, mocker):
1932+
"""The query should return an empty list if outputs strings are not synchronized.
1933+
Regression test for: https://github.com/palazzem/ha-econnect-alarm/issues/115
1934+
"""
1935+
html = """[
1936+
{
1937+
"Active": true,
1938+
"ActivePartial": false,
1939+
"Max": false,
1940+
"Activable": true,
1941+
"ActivablePartial": false,
1942+
"InUse": true,
1943+
"Id": 1,
1944+
"Index": 0,
1945+
"Element": 1,
1946+
"CommandId": 0,
1947+
"InProgress": false
1948+
}
1949+
]"""
1950+
server.add(responses.POST, "https://example.com/api/areas", body=html, status=200)
1951+
client = ElmoClient(base_url="https://example.com", domain="domain")
1952+
client._session_id = "test"
1953+
mocker.patch.object(client, "_get_descriptions")
1954+
client._get_descriptions.return_value = {}
1955+
# Test
1956+
sectors = client.query(query.SECTORS)
1957+
# Expected output
1958+
assert client._get_descriptions.called is True
1959+
assert len(server.calls) == 1
1960+
assert sectors == {
1961+
"last_id": 1,
1962+
"sectors": {
1963+
0: {
1964+
"element": 1,
1965+
"id": 1,
1966+
"index": 0,
1967+
"status": True,
1968+
"activable": True,
1969+
"name": "Unknown",
1970+
},
1971+
},
1972+
}
1973+
1974+
1975+
def test_client_missing_inputs_strings(server, mocker):
1976+
"""The query should return an empty list if outputs strings are not synchronized.
1977+
Regression test for: https://github.com/palazzem/ha-econnect-alarm/issues/115
1978+
"""
1979+
html = """[
1980+
{
1981+
"Alarm": true,
1982+
"MemoryAlarm": false,
1983+
"Excluded": false,
1984+
"InUse": true,
1985+
"IsVideo": false,
1986+
"Id": 1,
1987+
"Index": 0,
1988+
"Element": 1,
1989+
"CommandId": 0,
1990+
"InProgress": false
1991+
}
1992+
]"""
1993+
server.add(responses.POST, "https://example.com/api/inputs", body=html, status=200)
1994+
client = ElmoClient(base_url="https://example.com", domain="domain")
1995+
client._session_id = "test"
1996+
mocker.patch.object(client, "_get_descriptions")
1997+
client._get_descriptions.return_value = {}
1998+
# Test
1999+
inputs = client.query(query.INPUTS)
2000+
# Expected output
2001+
assert client._get_descriptions.called is True
2002+
assert len(server.calls) == 1
2003+
assert inputs == {
2004+
"last_id": 1,
2005+
"inputs": {
2006+
0: {
2007+
"element": 1,
2008+
"id": 1,
2009+
"index": 0,
2010+
"status": True,
2011+
"excluded": False,
2012+
"name": "Unknown",
2013+
},
2014+
},
2015+
}
2016+
2017+
2018+
def test_client_missing_outputs_strings(server, mocker):
2019+
"""The query should return an empty list if outputs strings are not synchronized.
2020+
Regression test for: https://github.com/palazzem/ha-econnect-alarm/issues/115
2021+
"""
2022+
html = """[
2023+
{
2024+
"Active": true,
2025+
"InUse": true,
2026+
"DoNotRequireAuthentication": true,
2027+
"ControlDeniedToUsers": false,
2028+
"Id": 400258,
2029+
"Index": 0,
2030+
"Element": 1,
2031+
"CommandId": 0,
2032+
"InProgress": false
2033+
}
2034+
]"""
2035+
server.add(responses.POST, "https://example.com/api/outputs", body=html, status=200)
2036+
client = ElmoClient(base_url="https://example.com", domain="domain")
2037+
client._session_id = "test"
2038+
mocker.patch.object(client, "_get_descriptions")
2039+
client._get_descriptions.return_value = {}
2040+
# Test
2041+
outputs = client.query(query.OUTPUTS)
2042+
# Expected output
2043+
assert client._get_descriptions.called is True
2044+
assert len(server.calls) == 1
2045+
2046+
assert outputs == {
2047+
"last_id": 400258,
2048+
"outputs": {
2049+
0: {
2050+
"control_denied_to_users": False,
2051+
"do_not_require_authentication": True,
2052+
"element": 1,
2053+
"id": 400258,
2054+
"index": 0,
2055+
"name": "Unknown",
2056+
"status": True,
2057+
},
2058+
},
2059+
}
2060+
2061+
19312062
def test_client_get_sectors_missing_area(server, mocker):
19322063
"""Should set an Unknown `sector` name if the description is missing.
19332064
Regression test for: https://github.com/palazzem/econnect-python/issues/91"""

0 commit comments

Comments
 (0)