diff --git a/miio/__init__.py b/miio/__init__.py index 4b967bcf2..5933749fb 100644 --- a/miio/__init__.py +++ b/miio/__init__.py @@ -42,7 +42,7 @@ from miio.heater import Heater from miio.heater_miot import HeaterMiot from miio.huizuo import Huizuo, HuizuoLampFan, HuizuoLampHeater, HuizuoLampScene -from miio.ihcooker import IHCooker +from miio.integrations.ihcooker import IHCooker from miio.integrations.yeelight import Yeelight from miio.miot_device import MiotDevice from miio.philips_bulb import PhilipsBulb, PhilipsWhiteBulb diff --git a/miio/discovery.py b/miio/discovery.py index e69f5ab2d..5791b1ce7 100644 --- a/miio/discovery.py +++ b/miio/discovery.py @@ -8,6 +8,16 @@ import zeroconf +from miio.integrations.ihcooker import ( + MODEL_EG1, + MODEL_EXP1, + MODEL_FW, + MODEL_HK1, + MODEL_KOREA1, + MODEL_TW1, + MODEL_V1, + IHCooker, +) from miio.integrations.yeelight import Yeelight from . import ( @@ -101,7 +111,6 @@ _LOGGER = logging.getLogger(__name__) - DEVICE_MAP: Dict[str, Union[Type[Device], partial]] = { "rockrobo-vacuum-v1": Vacuum, "roborock-vacuum-s5": Vacuum, @@ -174,6 +183,13 @@ "chunmi-cooker-normal3": Cooker, "chunmi-cooker-normal4": Cooker, "chunmi-cooker-normal5": Cooker, + MODEL_EXP1: IHCooker, + MODEL_FW: IHCooker, + MODEL_TW1: IHCooker, + MODEL_KOREA1: IHCooker, + MODEL_HK1: IHCooker, + MODEL_V1: IHCooker, + MODEL_EG1: IHCooker, "lumi-acpartner-v1": partial(AirConditioningCompanion, model=MODEL_ACPARTNER_V1), "lumi-acpartner-v2": partial(AirConditioningCompanion, model=MODEL_ACPARTNER_V2), "lumi-acpartner-v3": partial(AirConditioningCompanion, model=MODEL_ACPARTNER_V3), diff --git a/miio/ihcooker.py b/miio/integrations/ihcooker/__init__.py similarity index 98% rename from miio/ihcooker.py rename to miio/integrations/ihcooker/__init__.py index 6a9e2e96c..2faf0ee7c 100644 --- a/miio/ihcooker.py +++ b/miio/integrations/ihcooker/__init__.py @@ -9,9 +9,9 @@ import click import construct as c -from .click_common import command, format_output -from .device import Device, DeviceStatus -from .exceptions import DeviceException +from miio.click_common import command, format_output +from miio.device import Device, DeviceStatus +from miio.exceptions import DeviceException _LOGGER = logging.getLogger(__name__) @@ -643,7 +643,7 @@ def start(self, profile: Union[str, c.Container, dict], skip_confirmation=False) click.argument("skip_confirmation", type=bool, default=False), click.argument("minutes", type=int, default=60), click.argument("power", type=int, default=DEFAULT_FIRE_LEVEL), - click.argument("menu_location", type=int, default=9), + click.argument("menu_location", type=int, default=None), default_output=format_output("Cooking with temperature requested."), ) def start_temp( @@ -652,7 +652,7 @@ def start_temp( minutes=60, power=DEFAULT_FIRE_LEVEL, skip_confirmation=False, - menu_location=9, + menu_location=None, ): """Start cooking at a fixed temperature and duration. @@ -677,7 +677,7 @@ def start_temp( ) profile = self._prepare_profile(profile) - if menu_location != 9: + if menu_location is not None: self.set_menu(profile, menu_location, True) else: self.start(profile, skip_confirmation) @@ -799,9 +799,9 @@ def set_menu( - skip_confirmation, if True, request confirmation to start recipe as well. """ profile = self._prepare_profile(profile) - print(profile) - if location >= 9 or location < 1: - raise IHCookerException("location %d must be in [1,8]." % location) + + if location >= 9 or location < 0: + raise IHCookerException("location %d must be in [0,9]." % location) profile.menu_settings.save_recipe = True profile.confirm_start = confirm_start profile.menu_location = location diff --git a/miio/data/ihcooker_profiles.json b/miio/integrations/ihcooker/data/ihcooker_profiles.json similarity index 100% rename from miio/data/ihcooker_profiles.json rename to miio/integrations/ihcooker/data/ihcooker_profiles.json diff --git a/miio/data/ihcooker_recipe.json b/miio/integrations/ihcooker/data/ihcooker_rice_recipe.json similarity index 100% rename from miio/data/ihcooker_recipe.json rename to miio/integrations/ihcooker/data/ihcooker_rice_recipe.json diff --git a/miio/integrations/ihcooker/tests/__init__.py b/miio/integrations/ihcooker/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/miio/tests/test_ihcooker.py b/miio/integrations/ihcooker/tests/test_ihcooker.py similarity index 79% rename from miio/tests/test_ihcooker.py rename to miio/integrations/ihcooker/tests/test_ihcooker.py index e92175ac5..8102e4c88 100644 --- a/miio/tests/test_ihcooker.py +++ b/miio/integrations/ihcooker/tests/test_ihcooker.py @@ -3,20 +3,22 @@ import pytest -from miio import IHCooker, ihcooker +from miio.tests.dummies import DummyDevice -# V2 state from @aquarat -# ['running', '01484f54504f540000000000000000000000000000000000000000000000000001', '00306300b9', '0200013b00000000', '000a1404', '0000000100000002000000030000000400000005000000000000000000000000', '01', '0000ea4a6330303400' -from ..ihcooker import StageMode -from .dummies import DummyDevice - -# V1 state from @EUA -# ['waiting', '02537465616d2f626f696c000000000002', '0013000000', '0100010000000000', '001b0a04', '00000001000000020000000300000004000000050000a4e000000dcc00000000', '00', '0000e60a0017130f00'] +from .. import ( + MODEL_EXP1, + MODEL_V1, + IHCooker, + OperationMode, + StageMode, + profile_v1, + profile_v2, +) class DummyIHCookerV2(DummyDevice, IHCooker): def __init__(self, *args, **kwargs): - self._model = ihcooker.MODEL_EXP1 + self._model = MODEL_EXP1 self.state = [ "running", @@ -49,7 +51,7 @@ def _validate_profile(profile): class DummyIHCookerV1(DummyIHCookerV2): def __init__(self, *args, **kwargs): super().__init__(args, kwargs) - self._model = ihcooker.MODEL_V1 + self._model = MODEL_V1 self.state = [ "waiting", @@ -129,10 +131,10 @@ def test_construct(self): "00f93e001414000000000000000000df87" ) - res = ihcooker.profile_v2.parse(bytes.fromhex(recipe)) - self.assertEqual(ihcooker.profile_v2.parse(ihcooker.profile_v2.build(res)), res) - self.assertEqual(len(ihcooker.profile_v2.build(res)), len(recipe) // 2) - self.assertEqual(str(ihcooker.profile_v2.build(res).hex()), recipe) + res = profile_v2.parse(bytes.fromhex(recipe)) + self.assertEqual(profile_v2.parse(profile_v2.build(res)), res) + self.assertEqual(len(profile_v2.build(res)), len(recipe) // 2) + self.assertEqual(str(profile_v2.build(res).hex()), recipe) def test_phases(self): profile = dict( @@ -183,24 +185,24 @@ def test_phases(self): crc=0, ) - bytes_recipe = ihcooker.profile_v2.build(profile) + bytes_recipe = profile_v2.build(profile) hex_recipe = bytes_recipe.hex() self.assertEqual( - "030405546573740a52656369706500000000000000000000000000000000000000000003ea8001370000000000000100008137323c111113028006333d121012188007343e130f11188007343e130f11008000f9e52d1414008000f9e52d1414008000f9e52d1414008000f9e52d1414008000f9e52d1414008000f9e52d1414008000f9e52d1414008000f9e52d1414008000f9e52d1414008000f9e52d1414008000f9e52d14140000000000000000000a6b", + "030405546573740a52656369706500000000000000000000000000000000000000000003ea8001370000000000000100008137323c111113028006333d121012188007343e130f11188007343e130f11008032f9e52d1414008032f9e52d1414008032f9e52d1414008032f9e52d1414008032f9e52d1414008032f9e52d1414008032f9e52d1414008032f9e52d1414008032f9e52d1414008032f9e52d1414008032f9e52d1414000000000000000000386d", hex_recipe, ) self.assertEqual( hex_recipe, - ihcooker.profile_v2.build(ihcooker.profile_v2.parse(bytes_recipe)).hex(), + profile_v2.build(profile_v2.parse(bytes_recipe)).hex(), ) - self.assertEqual(115, ihcooker.profile_v2.parse(bytes_recipe).duration_minutes) + self.assertEqual(115, profile_v2.parse(bytes_recipe).duration_minutes) def test_crc(self): recipe = {} self.device.start(recipe) def test_mode(self): - self.assertEqual(ihcooker.OperationMode.Running, self.state().mode) + self.assertEqual(OperationMode.Running, self.state().mode) def test_temperature(self): self.assertEqual(60, self.state().temperature) @@ -239,9 +241,9 @@ def test_start_temp(self): def test_construct(self): recipe = "030405546573740a52656369706500000000000000000000000000000000000000000000000000000000000000000100008005323c111113028006333d121012188007343e130f11000000f93e001414000000f93e001414000000f93e001414000000f93e001414000000f93e001414000000f93e001414000000f93e001414000000f93e001414000000f93e001414000000f93e001414000000f93e000000000000000000000000000000000000000071ba" - res = ihcooker.profile_v1.parse(bytes.fromhex(recipe)) - self.assertEqual(len(ihcooker.profile_v1.build(res)), len(recipe) // 2) - self.assertEqual(str(ihcooker.profile_v1.build(res).hex()), recipe) + res = profile_v1.parse(bytes.fromhex(recipe)) + self.assertEqual(len(profile_v1.build(res)), len(recipe) // 2) + self.assertEqual(str(profile_v1.build(res).hex()), recipe) def test_phases(self): profile = dict( @@ -292,19 +294,19 @@ def test_phases(self): crc=0, ) - bytes_recipe = ihcooker.profile_v1.build(profile) + bytes_recipe = profile_v1.build(profile) hex_recipe = bytes_recipe.hex() self.assertEqual( - "030405546573740a526563697065000000000003ea8001370000000000000100000000000000008137323c111113028006333d121012188007343e130f11188007343e130f11008000f9e52d1414008000f9e52d1414008000f9e52d1414008000f9e52d1414008000f9e52d1414008000f9e52d1414008000f9e52d1414008000f9e52d1414008000f9e52d1414008000f9e52d1414008000f9e52d14140000000000000000000000000000000000000005ce", + "030405546573740a526563697065000000000003ea8001370000000000000100000000000000008137323c111113028006333d121012188007343e130f11188007343e130f11008032f9e52d1414008032f9e52d1414008032f9e52d1414008032f9e52d1414008032f9e52d1414008032f9e52d1414008032f9e52d1414008032f9e52d1414008032f9e52d1414008032f9e52d1414008032f9e52d1414000000000000000000000000000000000000002c57", hex_recipe, ) self.assertEqual( hex_recipe, - ihcooker.profile_v1.build(ihcooker.profile_v1.parse(bytes_recipe)).hex(), + profile_v1.build(profile_v1.parse(bytes_recipe)).hex(), ) def test_mode(self): - self.assertEqual(ihcooker.OperationMode.Waiting, self.state().mode) + self.assertEqual(OperationMode.Waiting, self.state().mode) def test_temperature(self): self.assertEqual(19, self.state().temperature)