Skip to content

Commit

Permalink
add basic multi cooker tests with sample data
Browse files Browse the repository at this point in the history
  • Loading branch information
sschirr committed Apr 2, 2022
1 parent 51806a8 commit 1cbd339
Show file tree
Hide file tree
Showing 3 changed files with 263 additions and 10 deletions.
11 changes: 5 additions & 6 deletions miio/cooker_multi.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ class OperationMode(enum.Enum):
Unknown = "unknown"

@classmethod
def _missing_(cls, value):
def _missing_(cls, _):
return OperationMode.Unknown


Expand Down Expand Up @@ -118,7 +118,7 @@ def temperatures(self) -> List[int]:

@property
def raw(self) -> str:
return "".join(["{:02x}".format(value) for value in self.data])
return "".join([f"{value:02x}" for value in self.data])

def __str__(self) -> str:
return str(self.data)
Expand All @@ -137,9 +137,11 @@ def __init__(self, profile_hex: str, duration: int, schedule: int, akw: bool):
if not self.is_valid():
raise CookerException("Profile checksum error")

self.set_schedule_enabled(False)

if duration is not None:
self.set_duration(duration)
if schedule is not None:
if schedule is not None and schedule > 0 and schedule <= 1440:
self.set_schedule_enabled(True)
self.set_schedule_duration(schedule)
if akw is not None:
Expand Down Expand Up @@ -184,9 +186,6 @@ def set_schedule_enabled(self, enabled):

def set_schedule_duration(self, duration):
"""Set the schedule time (delay before cooking) in minutes."""
if duration > 1440:
return

schedule_flag = self.profile_bytes[14] & 0x80
self.profile_bytes[14] = math.floor(duration / 60) & 0xFF
self.profile_bytes[14] |= schedule_flag
Expand Down
8 changes: 4 additions & 4 deletions miio/data/cooker_profiles.json
Original file line number Diff line number Diff line change
Expand Up @@ -194,22 +194,22 @@
],
"MODEL_MULTI": [
{
"title": "Jingzhu",
"title": "Fine Rice",
"description": "60 minutes cooking for tasty rice",
"profile": "02010000000001e101000000000000800101050814000000002091827d7800050091822d781c0a0091823c781c1e0091ff827820ffff91828278000500ffff8278ffffff91828278000d00ff828778ff000091827d7800000091827d7800ffff91826078ff0100490366780701086c0078090301af540266780801086c00780a02023c5701667b0e010a71007a0d02ffff5701667b0f010a73007d0d032005000000000000000000000000000000cf53"
},
{
"title": "Kuaizhu",
"title": "Quick Rice",
"description": "Quick 40 minutes cooking",
"profile": "02010100000002e100280000000000800101050614000000002091827d7800000091823c7820000091823c781c1e0091ff827820ffff91828278000500ffff8278ffffff91828278000d00ff828778ff000082827d7800000091827d7800ffff91826078ff0164490366780701086c007409030200540266780801086c00760a0202785701667b0e010a7100780a02ffff5701667b0f010a73007b0a032005000000000000000000000000000000ddba"
},
{
"title": "Zhuzhou",
"title": "Congee",
"description": "Cooking on slow fire from 40 minutes to 4 hours",
"profile": "02010200000003e2011e0400002800800101050614000000002091827d7800000091827d7800000091827d78001e0091ff877820ffff91827d78001e0091ff8278ffffff91828278001e0091828278060f0091827d7804000091827d7800000091827d780001f54e0255261802062a0482030002eb4e0255261802062a04820300032d4e0252261802062c04820501ffff4e0152241802062c0482050120000000000000000000000000000000009ce2"
},
{
"title": "Baowen",
"title": "Keep warm",
"description": "Keeping warm at 73 degrees",
"profile": "020103000000040c00001800000100800100000000000000002091827d7800000091827d7800000091827d78000000915a7d7820000091827d7800000091826e78ff000091827d7800000091826e7810000091826e7810000091827d7800000091827d780000a082007882140010871478030000eb820078821400108714780300012d8200788214001087147a0501ffff8200788214001087147d0501200000000000000000000000000000000090e5"
}
Expand Down
254 changes: 254 additions & 0 deletions miio/tests/test_cooker_multi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
from unittest import TestCase

import pytest

from miio import MultiCooker
from miio.cooker_multi import OperationMode

from .dummies import DummyDevice

COOKING_PROFILES = {
"Fine rice": "02010000000001e101000000000000800101050814000000002091827d7800050091822d781c0a0091823c781c1e0091ff827820ffff91828278000500ffff8278ffffff91828278000d00ff828778ff000091827d7800000091827d7800ffff91826078ff0100490366780701086c0078090301af540266780801086c00780a02023c5701667b0e010a71007a0d02ffff5701667b0f010a73007d0d032005000000000000000000000000000000cf53",
"Quick rice": "02010100000002e100280000000000800101050614000000002091827d7800000091823c7820000091823c781c1e0091ff827820ffff91828278000500ffff8278ffffff91828278000d00ff828778ff000082827d7800000091827d7800ffff91826078ff0164490366780701086c007409030200540266780801086c00760a0202785701667b0e010a7100780a02ffff5701667b0f010a73007b0a032005000000000000000000000000000000ddba",
"Congee": "02010200000003e2011e0400002800800101050614000000002091827d7800000091827d7800000091827d78001e0091ff877820ffff91827d78001e0091ff8278ffffff91828278001e0091828278060f0091827d7804000091827d7800000091827d780001f54e0255261802062a0482030002eb4e0255261802062a04820300032d4e0252261802062c04820501ffff4e0152241802062c0482050120000000000000000000000000000000009ce2",
"Keep warm": "020103000000040c00001800000100800100000000000000002091827d7800000091827d7800000091827d78000000915a7d7820000091827d7800000091826e78ff000091827d7800000091826e7810000091826e7810000091827d7800000091827d780000a082007882140010871478030000eb820078821400108714780300012d8200788214001087147a0501ffff8200788214001087147d0501200000000000000000000000000000000090e5",
}

TEST_CASES = {
# Fine rice; Schedule 75; Akw True
"test_case_0": {
"expected_profile": "02010000000001e1010000000000818f0101050814000000002091827d7800050091822d781c0a0091823c781c1e0091ff827820ffff91828278000500ffff8278ffffff91828278000d00ff828778ff000091827d7800000091827d7800ffff91826078ff0100490366780701086c0078090301af540266780801086c00780a02023c5701667b0e010a71007a0d02ffff5701667b0f010a73007d0d032005000000000000000000000000000000b557",
"cooker_state": {
"status": 4,
"phase": 0,
"menu": "0000000000000000000000000000000000000001",
"t_cook": 60,
"t_left": 3600,
"t_pre": 75,
"t_kw": 0,
"taste": 8,
"temp": 24,
"rice": 261,
"favs": "00000afe",
"akw": 1,
"t_start": 0,
"t_finish": 0,
"version": 13,
"setting": 0,
"code": 0,
"en_warm": 9,
"t_congee": 90,
"t_love": 60,
"boil": 0,
},
},
# Fine rice; Schedule 75; Akw False
"test_case_1": {
"expected_profile": "02010000000001e1010000000000810f0101050814000000002091827d7800050091822d781c0a0091823c781c1e0091ff827820ffff91828278000500ffff8278ffffff91828278000d00ff828778ff000091827d7800000091827d7800ffff91826078ff0100490366780701086c0078090301af540266780801086c00780a02023c5701667b0e010a71007a0d02ffff5701667b0f010a73007d0d03200500000000000000000000000000000049ee",
"cooker_state": {
"status": 4,
"phase": 0,
"menu": "0000000000000000000000000000000000000001",
"t_cook": 60,
"t_left": 3600,
"t_pre": 75,
"t_kw": 0,
"taste": 8,
"temp": 24,
"rice": 261,
"favs": "00000afe",
"akw": 2,
"t_start": 0,
"t_finish": 0,
"version": 13,
"setting": 0,
"code": 0,
"en_warm": 8,
"t_congee": 90,
"t_love": 60,
"boil": 0,
},
},
# Quick rice; Schedule 75; Akw False
"test_case_2": {
"expected_profile": "02010100000002e1002800000000810f0101050614000000002091827d7800000091823c7820000091823c781c1e0091ff827820ffff91828278000500ffff8278ffffff91828278000d00ff828778ff000082827d7800000091827d7800ffff91826078ff0164490366780701086c007409030200540266780801086c00760a0202785701667b0e010a7100780a02ffff5701667b0f010a73007b0a0320050000000000000000000000000000005b07",
"cooker_state": {
"status": 4,
"phase": 0,
"menu": "0101000000000000000000000000000000000002",
"t_cook": 40,
"t_left": 3600,
"t_pre": 75,
"t_kw": 0,
"taste": 6,
"temp": 24,
"rice": 261,
"favs": "00000afe",
"akw": 2,
"t_start": 0,
"t_finish": 0,
"version": 13,
"setting": 0,
"code": 0,
"en_warm": 8,
"t_congee": 90,
"t_love": 60,
"boil": 0,
},
},
# Congee; Akw False
"test_case_3": {
"expected_profile": "02010200000003e2011e0400002800000101050614000000002091827d7800000091827d7800000091827d78001e0091ff877820ffff91827d78001e0091ff8278ffffff91828278001e0091828278060f0091827d7804000091827d7800000091827d780001f54e0255261802062a0482030002eb4e0255261802062a04820300032d4e0252261802062c04820501ffff4e0152241802062c048205012000000000000000000000000000000000605b",
"cooker_state": {
"status": 2,
"phase": 0,
"menu": "0202000000000000000000000000000000000003",
"t_cook": 90,
"t_left": 5396,
"t_pre": 75,
"t_kw": 0,
"taste": 6,
"temp": 24,
"rice": 261,
"favs": "00000afe",
"akw": 2,
"t_start": 0,
"t_finish": 0,
"version": 13,
"setting": 0,
"code": 0,
"en_warm": 8,
"t_congee": 90,
"t_love": 60,
"boil": 0,
},
},
# Keep warm; Duration 55
"test_case_4": {
"expected_profile": "020103000000040c00371800000100800100000000000000002091827d7800000091827d7800000091827d78000000915a7d7820000091827d7800000091826e78ff000091827d7800000091826e7810000091826e7810000091827d7800000091827d780000a082007882140010871478030000eb820078821400108714780300012d8200788214001087147a0501ffff8200788214001087147d050120000000000000000000000000000000001ab9",
"cooker_state": {
"status": 2,
"phase": 0,
"menu": "0303000000000000000000000000000000000004",
"t_cook": 55,
"t_left": 5,
"t_pre": 75,
"t_kw": 0,
"taste": 0,
"temp": 24,
"rice": 0,
"favs": "00000afe",
"akw": 1,
"t_start": 0,
"t_finish": 0,
"version": 13,
"setting": 0,
"code": 0,
"en_warm": 8,
"t_congee": 90,
"t_love": 60,
"boil": 0,
},
},
}

DEFAULT_STATE = {
"status": 1, # Waiting
"phase": 0, # Current cooking phase: Unknown / No stage
"menu": "0000000000000000000000000000000000000001", # Menu: Fine Rice
"t_cook": 60, # Total cooking time for the menu
"t_left": 3600, # Remaining cooking time for the menu
"t_pre": 0, # Remaining pre cooking time
"t_kw": 0, # Keep warm time after finish cooking the menu
"taste": 8, # Taste setting
"temp": 24, # Current temperature
"rice": 261, # Rice setting
"favs": "00000afe", # Current favorite menu configured
"akw": 0, # Keep warm enabled
"t_start": 0,
"t_finish": 0,
"version": 13,
"setting": 0,
"code": 0,
"en_warm": 15,
"t_congee": 90,
"t_love": 60,
"boil": 0,
}


class DummyMultiCooker(DummyDevice, MultiCooker):
def __init__(self, *args, **kwargs):
self.state = DEFAULT_STATE
self.return_values = {
"get_prop": self._get_state,
"set_start": lambda x: self.set_start(x),
"cancel_cooking": lambda _: self.cancel_cooking(),
}
super().__init__(args, kwargs)

def set_start(self, profile):
state = DEFAULT_STATE

for test_case in TEST_CASES:
if profile == [TEST_CASES[test_case]["expected_profile"]]:
state = TEST_CASES[test_case]["cooker_state"]

for prop in state:
self._set_state(prop, [state[prop]])

def cancel_cooking(self):
for prop in DEFAULT_STATE:
self._set_state(prop, [DEFAULT_STATE[prop]])


@pytest.fixture(scope="class")
def multicooker(request):
request.cls.device = DummyMultiCooker()
# TODO add ability to test on a real device


@pytest.mark.usefixtures("multicooker")
class TestMultiCooker(TestCase):
def test_case_0(self):
self.device.start(COOKING_PROFILES["Fine rice"], None, 75, True)
status = self.device.status()
assert status.mode == OperationMode.PreCook
assert status.menu == "Fine Rice"
assert status.cooking_delayed == 75
assert status.keep_warm is True
self.device.stop()

def test_case_1(self):
self.device.start(COOKING_PROFILES["Fine rice"], None, 75, False)
status = self.device.status()
assert status.mode == OperationMode.PreCook
assert status.menu == "Fine Rice"
assert status.cooking_delayed == 75
assert status.keep_warm is False
self.device.stop()

def test_case_2(self):
self.device.start(COOKING_PROFILES["Quick rice"], None, 75, False)
status = self.device.status()
assert status.mode == OperationMode.PreCook
assert status.menu == "Quick Rice"
assert status.cooking_delayed == 75
assert status.keep_warm is False
self.device.stop()

def test_case_3(self):
self.device.start(COOKING_PROFILES["Congee"], None, None, False)
status = self.device.status()
assert status.mode == OperationMode.Running
assert status.menu == "Congee"
assert status.keep_warm is False
self.device.stop()

def test_case_4(self):
self.device.start(COOKING_PROFILES["Keep warm"], 55, None, None)
status = self.device.status()
assert status.mode == OperationMode.Running
assert status.menu == "Keep warm"
assert status.duration == 55
self.device.stop()

0 comments on commit 1cbd339

Please sign in to comment.