Skip to content

Commit

Permalink
genericmiot: skip properties with invalid values (#1830)
Browse files Browse the repository at this point in the history
`GenericMiotStatus` now ignores invalid properties (i.e., ones with missing values) and properties where the error code != 0.
  • Loading branch information
rytilahti authored Sep 24, 2023
1 parent 8f567fe commit 76808d8
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 10 deletions.
48 changes: 38 additions & 10 deletions miio/integrations/genericmiot/status.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,44 @@ class GenericMiotStatus(DeviceStatus):
def __init__(self, response, dev):
self._model: DeviceModel = dev._miot_model
self._dev = dev
self._data = {elem["did"]: elem["value"] for elem in response}
# for hardcoded json output.. see click_common.json_output
self.data = self._data

self._data_by_siid_piid = {
(elem["siid"], elem["piid"]): elem["value"] for elem in response
}
self._data_by_normalized_name = {
self._normalize_name(elem["did"]): elem["value"] for elem in response
}
self._data = {}
self._data_by_siid_piid = {}
self._data_by_normalized_name = {}
self._initialize_data(response)

def _initialize_data(self, response):
def _is_valid_property_response(elem):
code = elem.get("code")
if code is None:
_LOGGER.debug("Ignoring due to missing 'code': %s", elem)
return False

if code != 0:
_LOGGER.warning("Ignoring due to error code '%s': %s", code, elem)
return False

needed_keys = ("did", "piid", "siid", "value")
for key in needed_keys:
if key not in elem:
_LOGGER.debug("Ignoring due to missing '%s': %s", key, elem)
return False

return True

for prop in response:
if not _is_valid_property_response(prop):
continue

self._data[prop["did"]] = prop["value"]
self._data_by_siid_piid[(prop["siid"], prop["piid"])] = prop["value"]
self._data_by_normalized_name[self._normalize_name(prop["did"])] = prop[
"value"
]

@property
def data(self):
"""Implemented to support json output."""
return self._data

def _normalize_name(self, id_: str) -> str:
"""Return a cleaned id for dict searches."""
Expand Down
38 changes: 38 additions & 0 deletions miio/integrations/genericmiot/tests/test_status.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import logging
from unittest.mock import Mock

import pytest

from ..status import GenericMiotStatus


@pytest.fixture(scope="session")
def mockdev():
yield Mock()


VALID_RESPONSE = {"code": 0, "did": "valid-response", "piid": 1, "siid": 1, "value": 1}


@pytest.mark.parametrize("key", ("did", "piid", "siid", "value", "code"))
def test_response_with_missing_value(key, mockdev, caplog: pytest.LogCaptureFixture):
"""Verify that property responses without necessary keys are ignored."""
caplog.set_level(logging.DEBUG)

prop = {"code": 0, "did": f"no-{key}-in-response", "piid": 1, "siid": 1, "value": 1}
prop.pop(key)

status = GenericMiotStatus([VALID_RESPONSE, prop], mockdev)
assert f"Ignoring due to missing '{key}'" in caplog.text
assert len(status.data) == 1


@pytest.mark.parametrize("code", (-123, 123))
def test_response_with_error_codes(code, mockdev, caplog: pytest.LogCaptureFixture):
caplog.set_level(logging.WARNING)

did = f"error-code-{code}"
prop = {"code": code, "did": did, "piid": 1, "siid": 1}
status = GenericMiotStatus([VALID_RESPONSE, prop], mockdev)
assert f"Ignoring due to error code '{code}'" in caplog.text
assert len(status.data) == 1

0 comments on commit 76808d8

Please sign in to comment.