Skip to content
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions miio/integrations/roidmi/vacuum/roidmivacuum_miot.py
Original file line number Diff line number Diff line change
Expand Up @@ -558,7 +558,6 @@ def status(self) -> RoidmiVacuumStatus:
return RoidmiVacuumStatus(
{
prop["did"]: prop["value"] if prop["code"] == 0 else None
Comment thread
dsh0416 marked this conversation as resolved.
# max_properties limmit to 10 to avoid "Checksum error" messages from the device.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, this comment should be removed, since the max_properties is not properly limited at all, and there is also a typo in this comment. Maybe it is copy-pasted from somewhere else.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's probably just due to some copy&pasting. Please remove changes to roidmivacuum_miot from this PR (as they are unrelated, but feel free to create a separate PR to remove those if you wish!) and then this is good to go 👍

for prop in self.get_properties_for_mapping()
}
)
Expand All @@ -569,7 +568,6 @@ def consumable_status(self) -> RoidmiConsumableStatus:
return RoidmiConsumableStatus(
{
prop["did"]: prop["value"] if prop["code"] == 0 else None
Comment thread
dsh0416 marked this conversation as resolved.
# max_properties limmit to 10 to avoid "Checksum error" messages from the device.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here.

for prop in self.get_properties_for_mapping()
}
)
Expand All @@ -580,7 +578,6 @@ def cleaning_summary(self) -> RoidmiCleaningSummary:
return RoidmiCleaningSummary(
{
prop["did"]: prop["value"] if prop["code"] == 0 else None
Comment thread
dsh0416 marked this conversation as resolved.
# max_properties limmit to 10 to avoid "Checksum error" messages from the device.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and same here.

for prop in self.get_properties_for_mapping()
}
)
Expand Down
35 changes: 34 additions & 1 deletion miio/integrations/zhimi/airpurifier/airpurifier_miot.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,36 @@
"led_brightness": {"siid": 13, "piid": 2},
}

# https://miot-spec.org/miot-spec-v2/instance?type=urn:miot-spec-v2:device:air-purifier:0000A007:xiaomi-va2b:1
_MAPPING_VA2B = {
# Air Purifier
"power": {"siid": 2, "piid": 1},
"mode": {"siid": 2, "piid": 4},
"fan_level": {"siid": 2, "piid": 5},
"anion": {"siid": 2, "piid": 6},
# Environment
"humidity": {"siid": 3, "piid": 1},
"temperature": {"siid": 3, "piid": 2},
"aqi": {"siid": 3, "piid": 4},
# Filter
"filter_life_remaining": {"siid": 4, "piid": 1},
"filter_hours_used": {"siid": 4, "piid": 2},
"filter_left_time": {"siid": 4, "piid": 3},
# Alarm
"buzzer": {"siid": 6, "piid": 1},
# Physical Control Locked
"child_lock": {"siid": 8, "piid": 1},
# custom-service
"motor_speed": {"siid": 9, "piid": 1},
# RFID
"filter_rfid_tag": {"siid": 12, "piid": 1},
"filter_rfid_product_id": {"siid": 12, "piid": 3},
# Screen
"led_brightness": {"siid": 13, "piid": 1},
# Air Purifier Favorite
"favorite_level": {"siid": 14, "piid": 1},
}

# https://miot-spec.org/miot-spec-v2/instance?type=urn:miot-spec-v2:device:air-purifier:0000A007:zhimi-vb4:1
_MAPPING_VB4 = {
# Air Purifier
Expand Down Expand Up @@ -276,6 +306,7 @@
"zhimi.airp.mb5": _MAPPING_VA2, # airpurifier 4
"zhimi.airp.mb5a": _MAPPING_VA2, # airpurifier 4
"zhimi.airp.va2": _MAPPING_VA2, # airpurifier 4 pro
"xiaomi.airp.va2b": _MAPPING_VA2B, # airpurifier 4 pro
"zhimi.airp.vb4": _MAPPING_VB4, # airpurifier 4 pro
"zhimi.airpurifier.rma1": _MAPPING_RMA1, # airpurifier 4 lite
"zhimi.airpurifier.rma2": _MAPPING_RMA2, # airpurifier 4 lite
Expand Down Expand Up @@ -568,8 +599,10 @@ def status(self) -> AirPurifierMiotStatus:

return AirPurifierMiotStatus(
{
# max_properties limited to 10 to avoid "user ack timeout"
# messages from some of the devices. (e.g. xiaomi.airp.va2b)
prop["did"]: prop["value"] if prop["code"] == 0 else None
for prop in self.get_properties_for_mapping()
for prop in self.get_properties_for_mapping(max_properties=10)
},
self.model,
)
Expand Down
87 changes: 87 additions & 0 deletions miio/integrations/zhimi/airpurifier/tests/test_airpurifier_miot.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,26 @@
"button_pressed": "power",
}

_INITIAL_STATE_VA2B = {
"power": True,
"aqi": 10,
"anion": True,
"humidity": 62,
"temperature": 18.599999,
"fan_level": 2,
"mode": 0,
"led_brightness": 1,
"buzzer": False,
"favorite_level": 10,
"filter_life_remaining": 80,
"filter_hours_used": 682,
"filter_left_time": 309,
"motor_speed": 354,
"filter_rfid_product_id": "0:0:41:30",
"filter_rfid_tag": "10:20:30:40:50:60:7",
"button_pressed": "power",
}


class DummyAirPurifierMiot(DummyMiotDevice, AirPurifierMiot):
def __init__(self, *args, **kwargs):
Expand Down Expand Up @@ -306,6 +326,13 @@ def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)


class DummyAirPurifierMiotVA2B(DummyAirPurifierMiot):
def __init__(self, *args, **kwargs):
self._model = "xiaomi.airp.va2b"
self.state = _INITIAL_STATE_VA2B
super().__init__(*args, **kwargs)


class DummyAirPurifierMiotMB5(DummyAirPurifierMiot):
def __init__(self, *args, **kwargs):
self._model = "zhimi.airp.mb5"
Expand Down Expand Up @@ -373,3 +400,63 @@ def anion():

self.device.set_anion(False)
assert anion() is False


@pytest.fixture(scope="function")
def airpurifierVA2B(request):
request.cls.device = DummyAirPurifierMiotVA2B()


@pytest.mark.usefixtures("airpurifierVA2B")
class TestAirPurifierVA2B(TestCase):
def test_status(self):
status = self.device.status()
assert status.is_on is _INITIAL_STATE_VA2B["power"]
assert status.anion == _INITIAL_STATE_VA2B["anion"]
assert status.aqi == _INITIAL_STATE_VA2B["aqi"]
assert status.humidity == _INITIAL_STATE_VA2B["humidity"]
assert status.temperature == 18.6
assert status.fan_level == _INITIAL_STATE_VA2B["fan_level"]
assert status.mode == OperationMode(_INITIAL_STATE_VA2B["mode"])
assert status.led is None
assert status.led_brightness == LedBrightness(
_INITIAL_STATE_VA2B["led_brightness"]
)
assert status.buzzer == _INITIAL_STATE_VA2B["buzzer"]
assert status.child_lock == _INITIAL_STATE_VA2B["child_lock"]
assert status.favorite_level == _INITIAL_STATE_VA2B["favorite_level"]
assert (
status.filter_life_remaining == _INITIAL_STATE_VA2B["filter_life_remaining"]
)
assert status.filter_hours_used == _INITIAL_STATE_VA2B["filter_hours_used"]
assert status.filter_left_time == _INITIAL_STATE_VA2B["filter_left_time"]
assert status.use_time is None
assert status.motor_speed == _INITIAL_STATE_VA2B["motor_speed"]
assert (
status.filter_rfid_product_id
== _INITIAL_STATE_VA2B["filter_rfid_product_id"]
)
assert status.filter_type == FilterType.AntiBacterial

def test_set_led_brightness(self):
def led_brightness():
return self.device.status().led_brightness

self.device.set_led_brightness(LedBrightness.Bright)
assert led_brightness() == LedBrightness.Bright

self.device.set_led_brightness(LedBrightness.Dim)
assert led_brightness() == LedBrightness.Dim

self.device.set_led_brightness(LedBrightness.Off)
assert led_brightness() == LedBrightness.Off

def test_set_anion(self):
def anion():
return self.device.status().anion

self.device.set_anion(True)
assert anion() is True

self.device.set_anion(False)
assert anion() is False