diff --git a/cli.py b/cli.py index 6e03cb5..9877386 100644 --- a/cli.py +++ b/cli.py @@ -5,9 +5,10 @@ import aiohttp from cli_ac_menu import show_aircon_menu +from cli_dryer_menu import show_dryer_menu from cli_oven_menu import show_oven_menu from cli_refrigerator_menu import show_refrigerator_menu -from cli_washerdryer_menu import show_washerdryer_menu +from cli_washer_menu import show_washer_menu from whirlpool.appliancesmanager import AppliancesManager from whirlpool.auth import Auth from whirlpool.backendselector import BackendSelector, Brand, Region @@ -69,8 +70,11 @@ async def start(): if appliance_manager.aircons: print("\n".join(map(str, appliance_manager.aircons))) - if appliance_manager.washer_dryers: - print("\n".join(map(str, appliance_manager.washer_dryers))) + if appliance_manager.dryers: + print("\n".join(map(str, appliance_manager.dryers))) + + if appliance_manager.washers: + print("\n".join(map(str, appliance_manager.washers))) if appliance_manager.ovens: print("\n".join(map(str, appliance_manager.ovens))) @@ -99,9 +103,14 @@ async def __aexit__(self, *args) -> None: await show_aircon_menu(ac_data) return - for wd_data in appliance_manager.washer_dryers: - if wd_data.said == args.said: - await show_washerdryer_menu(wd_data) + for dr_data in appliance_manager.dryers: + if dr_data.said == args.said: + await show_dryer_menu(dr_data) + return + + for wr_data in appliance_manager.washers: + if wr_data.said == args.said: + await show_washer_menu(wr_data) return for mo_data in appliance_manager.ovens: diff --git a/cli_dryer_menu.py b/cli_dryer_menu.py new file mode 100644 index 0000000..4a6267c --- /dev/null +++ b/cli_dryer_menu.py @@ -0,0 +1,74 @@ +import json + +import aioconsole + +from whirlpool.dryer import Dryer + + +async def show_dryer_menu(dr: Dryer) -> None: + def print_menu(): + print("\n") + print(30 * "-", "MENU", 30 * "-") + print("u. Update status from server") + print("p. Print status") + print("v. Print raw status") + print("c. Custom command") + print("q. Exit") + print(67 * "-") + + def print_status(dr: Dryer): + print(f"online: {dr.get_online()}") + print(f"state: {dr.get_machine_state()}") + print(f"door open: {dr.get_door_open()}") + print(f"est time remaining: {dr.get_est_time_remaining()}") + print(f"extra power changeable: {dr.get_extra_power_changeable()}") + print(f"steam changeable: {dr.get_steam_changeable()}") + print(f"cycle select: {dr.get_cycle_changeable()}") + print(f"dryness: {dr.get_dryness_changeable()}") + print(f"manual dry time: {dr.get_manual_dry_time_changeable()}") + print(f"static guard: {dr.get_static_guard_changeable()}") + print(f"temperature: {dr.get_temperature_changeable()}") + print(f"wrinkle shield: {dr.get_wrinkle_shield_changeable()}") + print(f"airflow status: {dr.get_cycle_status_airflow_status()}") + print(f"cool down: {dr.get_cycle_status_cool_down()}") + print(f"damp: {dr.get_cycle_status_damp()}") + print(f"drying: {dr.get_cycle_status_drying()}") + print(f"limited cycle: {dr.get_cycle_status_limited_cycle()}") + print(f"sensing: {dr.get_cycle_status_sensing()}") + print(f"static reduce: {dr.get_cycle_status_static_reduce()}") + print(f"steaming: {dr.get_cycle_status_steaming()}") + print(f"wet: {dr.get_cycle_status_wet()}") + print(f"cycle count: {dr.get_cycle_count()}") + + print(f"set dryness: {dr.get_dryness()}") + print(f"set manual dry time: {dr.get_manual_dry_time()}") + print(f"set cycle select: {dr.get_cycle()}") + print(f"set temperature: {dr.get_temperature()}") + print(f"set wrinkle shield: {dr.get_wrinkle_shield()}") + + def attr_upd(): + print("Attributes updated") + + dr.register_attr_callback(attr_upd) + + loop = True + while loop: + print_menu() + choice = await aioconsole.ainput("Enter your choice: ") + + if choice == "p": + print_status(dr) + elif choice == "u": + await dr.fetch_data() + print_status(dr) + elif choice == "v": + print(json.dumps(dr._data_dict, indent=4)) + elif choice == "c": + cmd = await aioconsole.ainput("Command: ") + val = await aioconsole.ainput("Value: ") + await dr.send_attributes({cmd: val}) + elif choice == "q": + print("Bye") + loop = False + else: + print("Wrong option selection. Enter any key to try again..") diff --git a/cli_washerdryer_menu.py b/cli_washer_menu.py similarity index 50% rename from cli_washerdryer_menu.py rename to cli_washer_menu.py index 21e25d9..5dca36f 100644 --- a/cli_washerdryer_menu.py +++ b/cli_washer_menu.py @@ -2,10 +2,10 @@ import aioconsole -from whirlpool.washerdryer import WasherDryer +from whirlpool.washer import Washer -async def show_washerdryer_menu(wd: WasherDryer) -> None: +async def show_washer_menu(wr: Washer) -> None: def print_menu(): print("\n") print(30 * "-", "MENU", 30 * "-") @@ -16,20 +16,20 @@ def print_menu(): print("q. Exit") print(67 * "-") - def print_status(wd: WasherDryer): - print("online: " + str(wd.get_online())) - print("state: " + str(wd.get_machine_state())) - print("sensing: " + str(wd.get_cycle_status_sensing())) - print("filling: " + str(wd.get_cycle_status_filling())) - print("soaking: " + str(wd.get_cycle_status_soaking())) - print("washing: " + str(wd.get_cycle_status_washing())) - print("rinsing: " + str(wd.get_cycle_status_rinsing())) - print("spinning: " + str(wd.get_cycle_status_spinning())) + def print_status(wr: Washer): + print("online: " + str(wr.get_online())) + print("state: " + str(wr.get_machine_state())) + print("sensing: " + str(wr.get_cycle_status_sensing())) + print("filling: " + str(wr.get_cycle_status_filling())) + print("soaking: " + str(wr.get_cycle_status_soaking())) + print("washing: " + str(wr.get_cycle_status_washing())) + print("rinsing: " + str(wr.get_cycle_status_rinsing())) + print("spinning: " + str(wr.get_cycle_status_spinning())) def attr_upd(): print("Attributes updated") - wd.register_attr_callback(attr_upd) + wr.register_attr_callback(attr_upd) loop = True while loop: @@ -37,16 +37,16 @@ def attr_upd(): choice = await aioconsole.ainput("Enter your choice: ") if choice == "p": - print_status(wd) + print_status(wr) elif choice == "u": - await wd.fetch_data() - print_status(wd) + await wr.fetch_data() + print_status(wr) elif choice == "v": - print(json.dumps(wd._data_dict, indent=4)) + print(json.dumps(wr._data_dict, indent=4)) elif choice == "c": cmd = await aioconsole.ainput("Command: ") val = await aioconsole.ainput("Value: ") - await wd.send_attributes({cmd: val}) + await wr.send_attributes({cmd: val}) elif choice == "q": print("Bye") loop = False diff --git a/tests/data/owned_appliances.json b/tests/data/owned_appliances.json index 9cf9472..20bf66b 100644 --- a/tests/data/owned_appliances.json +++ b/tests/data/owned_appliances.json @@ -88,6 +88,39 @@ "CATEGORY_NAME": "Climate", "MODEL_NO": "CoolMyBeer", "SERIAL": "FR12345678" + }, + { + "DATA_MODEL_KEY": "DDM_LAUNDRY_VMAX20_MAYTAG_DRYER_6_V1", + "CATEGORY_NAME": "FabricCare", + "MODEL_NO": "MGD6230HW3", + "REPLENISHMENT_DEVICE_MODEL": null, + "IMAGE_PATH": null, + "APPLIANCE_ID": 999, + "APPLIANCE_MASTER_ID": 3703, + "MODEL_SKU_ID": null, + "CREATED_AT": 1693943005000, + "UPDATED_AT": 1693943005000, + "APPLIANCE_NAME": "Dryer", + "SAID": "SAIDDRYER1", + "NEST_AWAY": 0, + "CYCLE_HANDOFF": 0, + "NEST_THERMOSTAT_ID": 0, + "THERMOSTAT_INFLUENCE_THRESHOLD": null, + "THERMOSTAT_DESIRED_OFFSET": null, + "THERMOSTAT_OFFSET_NEEDED": null, + "DELETE_FLAG": 0, + "DISPLAY_POSITION": null, + "SERIAL": "MC1234567", + "LOCATION_ID": 999, + "MACHINE_ID": null, + "MACHINE_POSITION": 0, + "ISVOICEDEFAULT": 1, + "DEVICE_ID": "9d83427a-b994-4859-a2b9-2472c5dcbc26", + "IS_ENROLLED": null, + "STATUS": "CLAIMED", + "APPLIANCE_TYPE_ID": null, + "MACHINE_STATUS": null, + "APPLIANCE_MODE": 2 } ] } diff --git a/tests/test_dryer.py b/tests/test_dryer.py new file mode 100644 index 0000000..77c1835 --- /dev/null +++ b/tests/test_dryer.py @@ -0,0 +1,39 @@ +from whirlpool.appliancesmanager import AppliancesManager +from whirlpool.dryer import ( + Cycle, + Dryness, + MachineState, + Temperature, + WrinkleShield, +) + + +async def test_attributes(appliances_manager: AppliancesManager): + dryer = appliances_manager.dryers[0] + assert dryer.get_machine_state() == MachineState.Standby + assert not dryer.get_door_open() + assert dryer.get_est_time_remaining() == 1800 + assert not dryer.get_drum_light_on() + assert dryer.get_steam_changeable() + assert not dryer.get_cycle_changeable() + assert dryer.get_dryness_changeable() + assert dryer.get_manual_dry_time_changeable() + assert dryer.get_steam_changeable() + assert dryer.get_wrinkle_shield_changeable() + assert dryer.get_dryness() == Dryness.High + assert dryer.get_manual_dry_time() == 1800 + assert dryer.get_cycle() == Cycle.TimedDry + assert not dryer.get_cycle_status_airflow_status() + assert not dryer.get_cycle_status_cool_down() + assert not dryer.get_cycle_status_damp() + assert not dryer.get_cycle_status_drying() + assert not dryer.get_cycle_status_limited_cycle() + assert not dryer.get_cycle_status_sensing() + assert not dryer.get_cycle_status_static_reduce() + assert not dryer.get_cycle_status_steaming() + assert not dryer.get_cycle_status_wet() + assert dryer.get_cycle_count() == 195 + assert dryer.get_damp_notification_tone_volume() == 0 + assert dryer.get_alert_tone_volume() == 0 + assert dryer.get_temperature() == Temperature.Cool + assert dryer.get_wrinkle_shield() == WrinkleShield.Off diff --git a/tests/test_washer.py b/tests/test_washer.py new file mode 100644 index 0000000..4e41732 --- /dev/null +++ b/tests/test_washer.py @@ -0,0 +1,19 @@ +from whirlpool.appliancesmanager import AppliancesManager +from whirlpool.washer import MachineState + + +async def test_attributes(appliances_manager: AppliancesManager): + washer = appliances_manager.washers[0] + + assert washer.get_machine_state() == MachineState.Standby + assert washer.get_cycle_status_sensing() is False + assert washer.get_cycle_status_filling() is False + assert washer.get_cycle_status_soaking() is False + assert washer.get_cycle_status_washing() is False + assert washer.get_cycle_status_rinsing() is False + assert washer.get_cycle_status_spinning() is False + assert washer.get_dispense_1_level() == 4 + assert washer.get_door_open() is True + assert washer.get_time_remaining() == 4080 + + diff --git a/whirlpool/appliancesmanager.py b/whirlpool/appliancesmanager.py index 40a1927..19f65ff 100644 --- a/whirlpool/appliancesmanager.py +++ b/whirlpool/appliancesmanager.py @@ -11,10 +11,11 @@ from .appliance import Appliance from .auth import Auth from .backendselector import BackendSelector +from .dryer import Dryer from .oven import Oven from .refrigerator import Refrigerator from .types import ApplianceInfo -from .washerdryer import WasherDryer +from .washer import Washer LOGGER = logging.getLogger(__name__) @@ -31,7 +32,8 @@ def __init__( self._session: aiohttp.ClientSession = session self._event_socket: EventSocket | None = None self._aircons: dict[str, Any] = {} - self._washerdryers: dict[str, Any] = {} + self._dryers: dict[str, Any] = {} + self._washers: dict[str, Any] = {} self._ovens: dict[str, Any] = {} self._refrigerators: dict[str, Any] = {} @@ -39,7 +41,8 @@ def __init__( def all_appliances(self) -> dict[str, Appliance]: return { **self._aircons, - **self._washerdryers, + **self._dryers, + **self._washers, **self._ovens, **self._refrigerators, } @@ -49,8 +52,12 @@ def aircons(self) -> list[Aircon]: return list(self._aircons.values()) @property - def washer_dryers(self) -> list[WasherDryer]: - return list(self._washerdryers.values()) + def dryers(self) -> list[Dryer]: + return list(self._dryers.values()) + + @property + def washers(self) -> list[Washer]: + return list(self._washers.values()) @property def ovens(self) -> list[Oven]: @@ -85,8 +92,12 @@ def _add_appliance(self, appliance: dict[str, Any]) -> None: self._aircons[appliance_data.said] = Aircon( self._backend_selector, self._auth, self._session, appliance_data ) - elif "dryer" in data_model or "washer" in data_model: - self._washerdryers[appliance_data.said] = WasherDryer( + elif "dryer" in data_model: + self._dryers[appliance_data.said] = Dryer( + self._backend_selector, self._auth, self._session, appliance_data + ) + elif "washer" in data_model: + self._washers[appliance_data.said] = Washer( self._backend_selector, self._auth, self._session, appliance_data ) elif any(model in data_model for model in oven_models): diff --git a/whirlpool/dryer.py b/whirlpool/dryer.py new file mode 100644 index 0000000..a06bc26 --- /dev/null +++ b/whirlpool/dryer.py @@ -0,0 +1,339 @@ +from enum import Enum + +from .appliance import Appliance + +# Machine State +ATTR_MACHINE_STATE = "Cavity_CycleStatusMachineState" + +ATTRVAL_MACHINE_STATE_STANDBY = "0" +ATTRVAL_MACHINE_STATE_SETTING = "1" +ATTRVAL_MACHINE_STATE_DELAY_COUNT_DOWN_MODE = "2" +ATTRVAL_MACHINE_STATE_DELAY_PAUSE = "3" +ATTRVAL_MACHINE_STATE_SMART_DELAY = "4" +ATTRVAL_MACHINE_STATE_SMART_GRID_PAUSE = "5" +ATTRVAL_MACHINE_STATE_PAUSE = "6" +ATTRVAL_MACHINE_STATE_RUNNING_MAIN_CYCLE = "7" +ATTRVAL_MACHINE_STATE_RUNNING_POST_CYCLE = "8" +ATTRVAL_MACHINE_STATE_EXCEPTIONS = "9" +ATTRVAL_MACHINE_STATE_COMPLETE = "10" +ATTRVAL_MACHINE_STATE_POWER_FAILURE = "11" +ATTRVAL_MACHINE_STATE_SERVICE_DIAGNOSTIC = "12" +ATTRVAL_MACHINE_STATE_FACTORY_DIAGNOSTIC = "13" +ATTRVAL_MACHINE_STATE_LIFE_TEST = "14" +ATTRVAL_MACHINE_STATE_CUSTOMER_FOCUS_MODE = "15" +ATTRVAL_MACHINE_STATE_DEMO_MODE = "16" +ATTRVAL_MACHINE_STATE_HARD_STOP_OR_ERROR = "17" +ATTRVAL_MACHINE_STATE_SYSTEM_INIT = "18" +ATTRVAL_MACHINE_STATE_CANCELLED = "19" + +ATTR_DOOR_OPEN = "Cavity_OpStatusDoorOpen" +ATTR_EST_TIME_REMAINING = "Cavity_TimeStatusEstTimeRemaining" +ATTR_DRUM_LIGHT_ON = "Cavity_DisplaySetDrumLightOn" + +ATTR_EXTRA_POWER_CHANGEABLE = "Cavity_ChangeStatusExtraPowerChangeable" +ATTR_STEAM_CHANGEABLE = "Cavity_ChangeStatusSteamChangeable" +ATTR_CYCLE_CHANGEABLE = "DryCavity_ChangeStatusCycleSelect" +ATTR_DRYNESS_CHANGEABLE = "DryCavity_ChangeStatusDryness" +ATTR_MANUAL_DRY_TIME_CHANGEABLE = "DryCavity_ChangeStatusManualDryTime" +ATTR_STATIC_GUARD_CHANGEABLE = "DryCavity_ChangeStatusStaticGuard" +ATTR_TEMPERATURE_CHANGEABLE = "DryCavity_ChangeStatusTemperature" +ATTR_WRINKLE_SHIELD_CHANGEABLE = "DryCavity_ChangeStatusWrinkleShield" + +ATTR_CYCLE = "DryCavity_CycleSetCycleSelect" + +ATTRVAL_CYCLE_REGULAR = "1" +ATTRVAL_CYCLE_HEAVY_DUTY = "2" +ATTRVAL_CYCLE_DENIM = "3" +ATTRVAL_CYCLE_DELICATES = "4" +ATTRVAL_CYCLE_WRINKLE_CONTROL = "5" +ATTRVAL_CYCLE_BULKY_ITEMS = "6" +ATTRVAL_CYCLE_QUICK_DRY = "7" +ATTRVAL_CYCLE_SANITIZE = "9" +ATTRVAL_CYCLE_STEAM_REFRESH = "10" +ATTRVAL_CYCLE_TIMED_DRY = "11" +ATTRVAL_CYCLE_COLORS_BRIGHTS = "13" +ATTRVAL_CYCLE_TOWELS = "15" +ATTRVAL_CYCLE_WHITES = "16" +ATTRVAL_CYCLE_NORMAL = "41" + +ATTR_DRYNESS = "DryCavity_CycleSetDryness" + +ATTRVAL_DRYNESS_LOW = "0" +ATTRVAL_DRYNESS_LESS = "1" +ATTRVAL_DRYNESS_NORMAL = "4" +ATTRVAL_DRYNESS_MORE = "7" +ATTRVAL_DRYNESS_HIGH = "10" + +ATTR_MANUAL_DRY_TIME = "DryCavity_CycleSetManualDryTime" + +ATTR_TEMPERATURE = "DryCavity_CycleSetTemperature" + +ATTRVAL_TEMPERATURE_AIR = "0" +ATTRVAL_TEMPERATURE_COOL = "2" +ATTRVAL_TEMPERATURE_WARM = "5" +ATTRVAL_TEMPERATURE_WARM_HIGH = "6" +ATTRVAL_TEMPERATURE_HOT = "8" + +ATTR_WRINKLE_SHIELD = "DryCavity_CycleSetWrinkleShield" + +ATTRVAL_WRINKLE_SHIELD_OFF = "0" +ATTRVAL_WRINKLE_SHIELD_ON = "1" +ATTRVAL_WRINKLE_SHIELD_ON_WITH_STEAM = "2" + +ATTR_CYCLE_STATUS_AIR_FLOW_STATUS = "DryCavity_CycleStatusAirFlowStatus" +ATTR_CYCLE_STATUS_COOL_DOWN = "DryCavity_CycleStatusCoolDown" +ATTR_CYCLE_STATUS_DAMP = "DryCavity_CycleStatusDamp" +ATTR_CYCLE_STATUS_DRYING = "DryCavity_CycleStatusDrying" +ATTR_CYCLE_STATUS_LIMITED_CYCLE = "DryCavity_CycleStatusLimitedCycle" +ATTR_CYCLE_STATUS_SENSING = "DryCavity_CycleStatusSensing" +ATTR_CYCLE_STATUS_STATIC_REDUCE = "DryCavity_CycleStatusStaticReduce" +ATTR_CYCLE_STATUS_STEAMING = "DryCavity_CycleStatusSteaming" +ATTR_CYCLE_STATUS_WET = "DryCavity_CycleStatusWet" + +ATTR_DAMP_NOTIFICATION_TONE_VOLUME = "DrySys_OpSetDampNotificationToneVolume" +ATTR_ALERT_TONE_VOLUME = "Sys_OpSetAlertToneVolume" +ATTR_CYCLE_COUNT = "XCat_OdometerStatusCycleCount" + + +class Cycle(Enum): + Regular = 1 + HeavyDuty = 2 + Denim = 3 + Delicates = 4 + WrinkleControl = 5 + BulkyItems = 6 + QuickDry = 7 + Sanitize = 9 + SteamRefresh = 10 + TimedDry = 11 + ColorsBrights = 13 + Towels = 15 + Whites = 16 + Normal = 41 + + +CYCLE_MAP = { + ATTRVAL_CYCLE_REGULAR: Cycle.Regular, + ATTRVAL_CYCLE_HEAVY_DUTY: Cycle.HeavyDuty, + ATTRVAL_CYCLE_DENIM: Cycle.Denim, + ATTRVAL_CYCLE_DELICATES: Cycle.Delicates, + ATTRVAL_CYCLE_WRINKLE_CONTROL: Cycle.WrinkleControl, + ATTRVAL_CYCLE_BULKY_ITEMS: Cycle.BulkyItems, + ATTRVAL_CYCLE_QUICK_DRY: Cycle.QuickDry, + ATTRVAL_CYCLE_SANITIZE: Cycle.Sanitize, + ATTRVAL_CYCLE_STEAM_REFRESH: Cycle.SteamRefresh, + ATTRVAL_CYCLE_TIMED_DRY: Cycle.TimedDry, + ATTRVAL_CYCLE_COLORS_BRIGHTS: Cycle.ColorsBrights, + ATTRVAL_CYCLE_TOWELS: Cycle.Towels, + ATTRVAL_CYCLE_WHITES: Cycle.Whites, + ATTRVAL_CYCLE_NORMAL: Cycle.Normal, +} + + +class Dryness(Enum): + Low = 0 + Less = 1 + Normal = 4 + More = 7 + High = 10 + + +DRYNESS_MAP = { + ATTRVAL_DRYNESS_LOW: Dryness.Low, + ATTRVAL_DRYNESS_LESS: Dryness.Less, + ATTRVAL_DRYNESS_NORMAL: Dryness.Normal, + ATTRVAL_DRYNESS_MORE: Dryness.More, + ATTRVAL_DRYNESS_HIGH: Dryness.High, +} + + +class MachineState(Enum): + Standby = 0 + Setting = 1 + DelayCountdownMode = 2 + DelayPause = 3 + SmartDelay = 4 + SmartGridPause = 5 + Pause = 6 + RunningMainCycle = 7 + RunningPostCycle = 8 + Exceptions = 9 + Complete = 10 + PowerFailure = 11 + ServiceDiagnostic = 12 + FactoryDiagnostic = 13 + LifeTest = 14 + CustomerFocusMode = 15 + DemoMode = 16 + HardStopOrError = 17 + SystemInit = 18 + Cancelled = 19 + + +MACHINE_STATE_MAP = { + ATTRVAL_MACHINE_STATE_STANDBY: MachineState.Standby, + ATTRVAL_MACHINE_STATE_SETTING: MachineState.Setting, + ATTRVAL_MACHINE_STATE_DELAY_COUNT_DOWN_MODE: MachineState.DelayCountdownMode, + ATTRVAL_MACHINE_STATE_DELAY_PAUSE: MachineState.DelayPause, + ATTRVAL_MACHINE_STATE_SMART_DELAY: MachineState.SmartDelay, + ATTRVAL_MACHINE_STATE_SMART_GRID_PAUSE: MachineState.SmartGridPause, + ATTRVAL_MACHINE_STATE_PAUSE: MachineState.Pause, + ATTRVAL_MACHINE_STATE_RUNNING_MAIN_CYCLE: MachineState.RunningMainCycle, + ATTRVAL_MACHINE_STATE_RUNNING_POST_CYCLE: MachineState.RunningPostCycle, + ATTRVAL_MACHINE_STATE_EXCEPTIONS: MachineState.Exceptions, + ATTRVAL_MACHINE_STATE_COMPLETE: MachineState.Complete, + ATTRVAL_MACHINE_STATE_POWER_FAILURE: MachineState.PowerFailure, + ATTRVAL_MACHINE_STATE_SERVICE_DIAGNOSTIC: MachineState.ServiceDiagnostic, + ATTRVAL_MACHINE_STATE_FACTORY_DIAGNOSTIC: MachineState.FactoryDiagnostic, + ATTRVAL_MACHINE_STATE_LIFE_TEST: MachineState.LifeTest, + ATTRVAL_MACHINE_STATE_CUSTOMER_FOCUS_MODE: MachineState.CustomerFocusMode, + ATTRVAL_MACHINE_STATE_DEMO_MODE: MachineState.DemoMode, + ATTRVAL_MACHINE_STATE_HARD_STOP_OR_ERROR: MachineState.HardStopOrError, + ATTRVAL_MACHINE_STATE_SYSTEM_INIT: MachineState.SystemInit, +} + + +class Temperature(Enum): + Air = 0 + Cool = 2 + Warm = 5 + WarmHigh = 6 + Hot = 8 + + +TEMPERATURE_MAP = { + ATTRVAL_TEMPERATURE_AIR: Temperature.Air, + ATTRVAL_TEMPERATURE_COOL: Temperature.Cool, + ATTRVAL_TEMPERATURE_WARM: Temperature.Warm, + ATTRVAL_TEMPERATURE_WARM_HIGH: Temperature.WarmHigh, + ATTRVAL_TEMPERATURE_HOT: Temperature.Hot, +} + + +class WrinkleShield(Enum): + Off = 0 + On = 1 + OnWithSteam = 2 + + +WRINKLE_SHIELD_MAP = { + ATTRVAL_WRINKLE_SHIELD_OFF: WrinkleShield.Off, + ATTRVAL_WRINKLE_SHIELD_ON: WrinkleShield.On, + ATTRVAL_WRINKLE_SHIELD_ON_WITH_STEAM: WrinkleShield.OnWithSteam, +} + + +class Dryer(Appliance): + def get_machine_state(self) -> MachineState | None: + state_raw = self._get_attribute(ATTR_MACHINE_STATE) + if state_raw is None: + return None + return MACHINE_STATE_MAP.get(state_raw, None) + + def get_door_open(self) -> bool | None: + return self.attr_value_to_bool(self._get_attribute(ATTR_DOOR_OPEN)) + + def get_est_time_remaining(self) -> int | None: + return self._get_int_attribute(ATTR_EST_TIME_REMAINING) + + def get_drum_light_on(self) -> bool | None: + return self.attr_value_to_bool(self._get_attribute(ATTR_DRUM_LIGHT_ON)) + + def get_extra_power_changeable(self) -> bool | None: + return self.attr_value_to_bool(self._get_attribute(ATTR_EXTRA_POWER_CHANGEABLE)) + + def get_steam_changeable(self) -> bool | None: + return self.attr_value_to_bool(self._get_attribute(ATTR_STEAM_CHANGEABLE)) + + def get_cycle_changeable(self) -> int | None: + return self.attr_value_to_bool(self._get_attribute(ATTR_CYCLE_CHANGEABLE)) + + def get_dryness_changeable(self) -> bool | None: + return self.attr_value_to_bool(self._get_attribute(ATTR_DRYNESS_CHANGEABLE)) + + def get_manual_dry_time_changeable(self) -> int | None: + return self.attr_value_to_bool( + self._get_attribute(ATTR_MANUAL_DRY_TIME_CHANGEABLE) + ) + + def get_static_guard_changeable(self) -> bool | None: + return self.attr_value_to_bool( + self._get_attribute(ATTR_STATIC_GUARD_CHANGEABLE) + ) + + def get_temperature_changeable(self) -> bool | None: + return self.attr_value_to_bool(self._get_attribute(ATTR_TEMPERATURE_CHANGEABLE)) + + def get_wrinkle_shield_changeable(self) -> bool | None: + return self.attr_value_to_bool( + self._get_attribute(ATTR_WRINKLE_SHIELD_CHANGEABLE) + ) + + def get_dryness(self) -> Dryness | None: + dryness_raw = self._get_attribute(ATTR_DRYNESS) + if dryness_raw is None: + return None + return DRYNESS_MAP.get(dryness_raw, None) + + def get_manual_dry_time(self) -> int | None: + return self._get_int_attribute(ATTR_MANUAL_DRY_TIME) + + def get_cycle(self) -> Cycle | None: + cycle_raw = self._get_attribute(ATTR_CYCLE) + if cycle_raw is None: + return None + return CYCLE_MAP.get(cycle_raw, None) + + def get_cycle_status_airflow_status(self) -> bool | None: + return self.attr_value_to_bool( + self._get_attribute(ATTR_CYCLE_STATUS_AIR_FLOW_STATUS) + ) + + def get_cycle_status_cool_down(self) -> bool | None: + return self.attr_value_to_bool(self._get_attribute(ATTR_CYCLE_STATUS_COOL_DOWN)) + + def get_cycle_status_damp(self) -> bool | None: + return self.attr_value_to_bool(self._get_attribute(ATTR_CYCLE_STATUS_DAMP)) + + def get_cycle_status_drying(self) -> bool | None: + return self.attr_value_to_bool(self._get_attribute(ATTR_CYCLE_STATUS_DRYING)) + + def get_cycle_status_limited_cycle(self) -> bool | None: + return self.attr_value_to_bool( + self._get_attribute(ATTR_CYCLE_STATUS_LIMITED_CYCLE) + ) + + def get_cycle_status_sensing(self) -> bool | None: + return self.attr_value_to_bool(self._get_attribute(ATTR_CYCLE_STATUS_SENSING)) + + def get_cycle_status_static_reduce(self) -> bool | None: + return self.attr_value_to_bool( + self._get_attribute(ATTR_CYCLE_STATUS_STATIC_REDUCE) + ) + + def get_cycle_status_steaming(self) -> bool | None: + return self.attr_value_to_bool(self._get_attribute(ATTR_CYCLE_STATUS_STEAMING)) + + def get_cycle_status_wet(self) -> bool | None: + return self.attr_value_to_bool(self._get_attribute(ATTR_CYCLE_STATUS_WET)) + + def get_cycle_count(self) -> int | None: + return self._get_int_attribute(ATTR_CYCLE_COUNT) + + def get_damp_notification_tone_volume(self) -> int | None: + return self._get_int_attribute(ATTR_DAMP_NOTIFICATION_TONE_VOLUME) + + def get_alert_tone_volume(self) -> int | None: + return self._get_int_attribute(ATTR_ALERT_TONE_VOLUME) + + def get_temperature(self) -> Temperature | None: + temperature_raw = self._get_attribute(ATTR_TEMPERATURE) + if temperature_raw is None: + return None + return TEMPERATURE_MAP.get(temperature_raw, None) + + def get_wrinkle_shield(self) -> WrinkleShield | None: + shield_raw = self._get_attribute(ATTR_WRINKLE_SHIELD) + if shield_raw is None: + return None + return WRINKLE_SHIELD_MAP.get(shield_raw, None) diff --git a/whirlpool/washerdryer.py b/whirlpool/washer.py similarity index 50% rename from whirlpool/washerdryer.py rename to whirlpool/washer.py index 122c694..814c9d9 100644 --- a/whirlpool/washerdryer.py +++ b/whirlpool/washer.py @@ -1,40 +1,38 @@ -import logging from enum import Enum from .appliance import Appliance -LOGGER = logging.getLogger(__name__) - ATTR_CYCLE_STATUS_SENSING = "WashCavity_CycleStatusSensing" ATTR_CYCLE_STATUS_FILLING = "WashCavity_CycleStatusFilling" ATTR_CYCLE_STATUS_SOAKING = "WashCavity_CycleStatusSoaking" ATTR_CYCLE_STATUS_WASHING = "WashCavity_CycleStatusWashing" ATTR_CYCLE_STATUS_RINSING = "WashCavity_CycleStatusRinsing" ATTR_CYCLE_STATUS_SPINNING = "WashCavity_CycleStatusSpinning" +ATTR_CYCLE_STATUS_MACHINE_STATE = "Cavity_CycleStatusMachineState" +ATTR_CYCLE_STATUS_TIME_REMAINING = "Cavity_TimeStatusEstTimeRemaining" + ATTR_DISPENSE_1_LEVEL = "WashCavity_OpStatusBulkDispense1Level" ATTR_DOOR_OPEN = "Cavity_OpStatusDoorOpen" -ATTR_MACHINE_STATE = "Cavity_CycleStatusMachineState" -ATTR_TIME_REMAINING = "Cavity_TimeStatusEstTimeRemaining" ATTRVAL_MACHINE_STATE_STANDBY = "0" ATTRVAL_MACHINE_STATE_SETTING = "1" -ATTRVAL_MACHINE_STATE_DELAYCOUNTDOWNMODE = "2" -ATTRVAL_MACHINE_STATE_DELAYPAUSE = "3" -ATTRVAL_MACHINE_STATE_SMARTDELAY = "4" -ATTRVAL_MACHINE_STATE_SMARTGRIDPAUSE = "5" +ATTRVAL_MACHINE_STATE_DELAY_COUNT_DOWN_MODE = "2" +ATTRVAL_MACHINE_STATE_DELAY_PAUSE = "3" +ATTRVAL_MACHINE_STATE_SMART_DELAY = "4" +ATTRVAL_MACHINE_STATE_SMART_GRID_PAUSE = "5" ATTRVAL_MACHINE_STATE_PAUSE = "6" -ATTRVAL_MACHINE_STATE_RUNNINGMAINCYCLE = "7" -ATTRVAL_MACHINE_STATE_RUNNINGPOSTCYCLE = "8" +ATTRVAL_MACHINE_STATE_RUNNING_MAIN_CYCLE = "7" +ATTRVAL_MACHINE_STATE_RUNNING_POST_CYCLE = "8" ATTRVAL_MACHINE_STATE_EXCEPTIONS = "9" ATTRVAL_MACHINE_STATE_COMPLETE = "10" -ATTRVAL_MACHINE_STATE_POWERFAILURE = "11" -ATTRVAL_MACHINE_STATE_SERVICEDIAGNOSTIC = "12" -ATTRVAL_MACHINE_STATE_FACTORYDIAGNOSTIC = "13" -ATTRVAL_MACHINE_STATE_LIFETEST = "14" -ATTRVAL_MACHINE_STATE_CUSTOMERFOCUSMODE = "15" -ATTRVAL_MACHINE_STATE_DEMOMODE = "16" -ATTRVAL_MACHINE_STATE_HARDSTOPORERROR = "17" -ATTRVAL_MACHINE_STATE_SYSTEMINIT = "18" +ATTRVAL_MACHINE_STATE_POWER_FAILURE = "11" +ATTRVAL_MACHINE_STATE_SERVICE_DIAGNOSTIC = "12" +ATTRVAL_MACHINE_STATE_FACTORY_DIAGNOSTIC = "13" +ATTRVAL_MACHINE_STATE_LIFE_TEST = "14" +ATTRVAL_MACHINE_STATE_CUSTOMER_FOCUS_MODE = "15" +ATTRVAL_MACHINE_STATE_DEMO_MODE = "16" +ATTRVAL_MACHINE_STATE_HARD_STOP_OR_ERROR = "17" +ATTRVAL_MACHINE_STATE_SYSTEM_INIT = "18" class MachineState(Enum): @@ -60,35 +58,34 @@ class MachineState(Enum): MACHINE_STATE_MAP = { - MachineState.Standby: ATTRVAL_MACHINE_STATE_STANDBY, - MachineState.Setting: ATTRVAL_MACHINE_STATE_SETTING, - MachineState.DelayCountdownMode: ATTRVAL_MACHINE_STATE_DELAYCOUNTDOWNMODE, - MachineState.DelayPause: ATTRVAL_MACHINE_STATE_DELAYPAUSE, - MachineState.SmartDelay: ATTRVAL_MACHINE_STATE_SMARTDELAY, - MachineState.SmartGridPause: ATTRVAL_MACHINE_STATE_SMARTGRIDPAUSE, - MachineState.Pause: ATTRVAL_MACHINE_STATE_PAUSE, - MachineState.RunningMainCycle: ATTRVAL_MACHINE_STATE_RUNNINGMAINCYCLE, - MachineState.RunningPostCycle: ATTRVAL_MACHINE_STATE_RUNNINGPOSTCYCLE, - MachineState.Exceptions: ATTRVAL_MACHINE_STATE_EXCEPTIONS, - MachineState.Complete: ATTRVAL_MACHINE_STATE_COMPLETE, - MachineState.PowerFailure: ATTRVAL_MACHINE_STATE_POWERFAILURE, - MachineState.ServiceDiagnostic: ATTRVAL_MACHINE_STATE_SERVICEDIAGNOSTIC, - MachineState.FactoryDiagnostic: ATTRVAL_MACHINE_STATE_FACTORYDIAGNOSTIC, - MachineState.LifeTest: ATTRVAL_MACHINE_STATE_LIFETEST, - MachineState.CustomerFocusMode: ATTRVAL_MACHINE_STATE_CUSTOMERFOCUSMODE, - MachineState.DemoMode: ATTRVAL_MACHINE_STATE_DEMOMODE, - MachineState.HardStopOrError: ATTRVAL_MACHINE_STATE_HARDSTOPORERROR, - MachineState.SystemInit: ATTRVAL_MACHINE_STATE_SYSTEMINIT, + ATTRVAL_MACHINE_STATE_STANDBY: MachineState.Standby, + ATTRVAL_MACHINE_STATE_SETTING: MachineState.Setting, + ATTRVAL_MACHINE_STATE_DELAY_COUNT_DOWN_MODE: MachineState.DelayCountdownMode, + ATTRVAL_MACHINE_STATE_DELAY_PAUSE: MachineState.DelayPause, + ATTRVAL_MACHINE_STATE_SMART_DELAY: MachineState.SmartDelay, + ATTRVAL_MACHINE_STATE_SMART_GRID_PAUSE: MachineState.SmartGridPause, + ATTRVAL_MACHINE_STATE_PAUSE: MachineState.Pause, + ATTRVAL_MACHINE_STATE_RUNNING_MAIN_CYCLE: MachineState.RunningMainCycle, + ATTRVAL_MACHINE_STATE_RUNNING_POST_CYCLE: MachineState.RunningPostCycle, + ATTRVAL_MACHINE_STATE_EXCEPTIONS: MachineState.Exceptions, + ATTRVAL_MACHINE_STATE_COMPLETE: MachineState.Complete, + ATTRVAL_MACHINE_STATE_POWER_FAILURE: MachineState.PowerFailure, + ATTRVAL_MACHINE_STATE_SERVICE_DIAGNOSTIC: MachineState.ServiceDiagnostic, + ATTRVAL_MACHINE_STATE_FACTORY_DIAGNOSTIC: MachineState.FactoryDiagnostic, + ATTRVAL_MACHINE_STATE_LIFE_TEST: MachineState.LifeTest, + ATTRVAL_MACHINE_STATE_CUSTOMER_FOCUS_MODE: MachineState.CustomerFocusMode, + ATTRVAL_MACHINE_STATE_DEMO_MODE: MachineState.DemoMode, + ATTRVAL_MACHINE_STATE_HARD_STOP_OR_ERROR: MachineState.HardStopOrError, + ATTRVAL_MACHINE_STATE_SYSTEM_INIT: MachineState.SystemInit } -class WasherDryer(Appliance): +class Washer(Appliance): def get_machine_state(self) -> MachineState | None: - state_raw = self._get_attribute(ATTR_MACHINE_STATE) - for k, v in MACHINE_STATE_MAP.items(): - if v == state_raw: - return k - return None + state_raw = self._get_attribute(ATTR_CYCLE_STATUS_MACHINE_STATE) + if state_raw is None: + return None + return MACHINE_STATE_MAP.get(state_raw, None) def get_cycle_status_sensing(self) -> bool | None: return self.attr_value_to_bool(self._get_attribute(ATTR_CYCLE_STATUS_SENSING)) @@ -115,4 +112,4 @@ def get_door_open(self) -> bool | None: return self.attr_value_to_bool(self._get_attribute(ATTR_DOOR_OPEN)) def get_time_remaining(self) -> int | None: - return self._get_int_attribute(ATTR_TIME_REMAINING) + return self._get_int_attribute(ATTR_CYCLE_STATUS_TIME_REMAINING)