Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support to WasherDryer Tower device (FR #649) #683

Merged
merged 2 commits into from
Jan 29, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
10 changes: 9 additions & 1 deletion custom_components/smartthinq_sensors/wideq/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -376,12 +376,15 @@ def __init__(
client: ClientAsync,
device_info: DeviceInfo,
status: DeviceStatus | None = None,
*,
sub_device: str | None = None,
):
"""Create a wrapper for a `DeviceInfo` object associated with a Client."""

self._client = client
self._device_info = device_info
self._status = status
self._sub_device = sub_device
self._model_data = None
self._model_info: ModelInfo | None = None
self._model_lang_pack = None
Expand All @@ -396,6 +399,9 @@ def __init__(
# attributes for properties
self._attr_unique_id = self._device_info.device_id
self._attr_name = self._device_info.name
if sub_device:
self._attr_unique_id += f"-{sub_device}"
self._attr_name += f" {sub_device.capitalize()}"

# for logging unknown states received
self._unknown_states = []
Expand Down Expand Up @@ -456,7 +462,9 @@ async def init_device_info(self) -> bool:
if self._model_data is None:
return False

self._model_info = ModelInfo.get_model_info(self._model_data)
self._model_info = ModelInfo.get_model_info(
self._model_data, self._sub_device
)
if self._model_info is None:
return False

Expand Down
4 changes: 4 additions & 0 deletions custom_components/smartthinq_sensors/wideq/device_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class DeviceType(Enum):
DISHWASHER = 204
TOWER_WASHER = 221
TOWER_DRYER = 222
TOWER_WASHERDRYER = 223
RANGE = 301
MICROWAVE = 302
COOKTOP = 303
Expand Down Expand Up @@ -60,9 +61,12 @@ class DeviceType(Enum):
DeviceType.DRYER,
DeviceType.TOWER_DRYER,
DeviceType.TOWER_WASHER,
DeviceType.TOWER_WASHERDRYER,
DeviceType.WASHER,
]

WM_COMPLEX_DEVICES = {DeviceType.TOWER_WASHERDRYER: ["washer", "dryer"]}

SET_TIME_DEVICE_TYPES = [
DeviceType.MICROWAVE,
]
Expand Down
19 changes: 12 additions & 7 deletions custom_components/smartthinq_sensors/wideq/devices/washerDryer.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
]

WM_ROOT_DATA = "washerDryer"
WM_SUB_DEV = {"mini": "miniState"}
WM_SUB_KEYS = {"mini": "miniState"}

POWER_STATUS_KEY = ["State", "state"]

Expand Down Expand Up @@ -62,22 +62,27 @@
_LOGGER = logging.getLogger(__name__)


def get_sub_devices(device_info: DeviceInfo) -> list[str]:
def get_sub_keys(device_info: DeviceInfo, sub_device: str | None = None) -> list[str]:
"""Search for valid sub devices and return related sub keys."""
if not (snapshot := device_info.snapshot):
return []
if not (payload := snapshot.get(WM_ROOT_DATA)):
if not (payload := snapshot.get(sub_device or WM_ROOT_DATA)):
return []
return [k for k, s in WM_SUB_DEV.items() if s in payload]
return [k for k, s in WM_SUB_KEYS.items() if s in payload]


class WMDevice(Device):
"""A higher-level interface for washer and dryer."""

def __init__(
self, client: ClientAsync, device_info: DeviceInfo, sub_key: str | None = None
self,
client: ClientAsync,
device_info: DeviceInfo,
*,
sub_device: str | None = None,
sub_key: str | None = None,
):
super().__init__(client, device_info, WMStatus(self))
super().__init__(client, device_info, WMStatus(self), sub_device=sub_device)
self._sub_key = sub_key
if sub_key:
self._attr_unique_id += f"-{sub_key}"
Expand Down Expand Up @@ -311,7 +316,7 @@ def _set_remote_start_opt(self, res):
async def poll(self) -> WMStatus | None:
"""Poll the device's current state."""

res = await self._device_poll(WM_ROOT_DATA)
res = await self._device_poll(self._sub_device or WM_ROOT_DATA)
if not res:
self._stand_by = False
return None
Expand Down
25 changes: 19 additions & 6 deletions custom_components/smartthinq_sensors/wideq/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from .core_async import ClientAsync
from .device import Device
from .device_info import (
WM_COMPLEX_DEVICES,
WM_DEVICE_TYPES,
DeviceInfo,
DeviceType,
Expand All @@ -22,10 +23,17 @@
from .devices.range import RangeDevice
from .devices.refrigerator import RefrigeratorDevice
from .devices.styler import StylerDevice
from .devices.washerDryer import WMDevice, get_sub_devices
from .devices.washerDryer import WMDevice, get_sub_keys
from .devices.waterheater import WaterHeaterDevice


def _get_sub_devices(device_type: DeviceType) -> list[str | None]:
"""Return a list of complex devices"""
if sub_devices := WM_COMPLEX_DEVICES.get(device_type):
return sub_devices
return [None]


def get_lge_device(
client: ClientAsync, device_info: DeviceInfo, temp_unit=TemperatureUnit.CELSIUS
) -> list[Device] | None:
Expand Down Expand Up @@ -63,9 +71,14 @@ def get_lge_device(
if device_type == DeviceType.WATER_HEATER:
return [WaterHeaterDevice(client, device_info, temp_unit)]
if device_type in WM_DEVICE_TYPES:
main_dev = [WMDevice(client, device_info)]
return main_dev + [
WMDevice(client, device_info, sub_key=key)
for key in get_sub_devices(device_info)
]
dev_list = []
for sub_device in _get_sub_devices(device_type):
dev_list.append(WMDevice(client, device_info, sub_device=sub_device))
dev_list.extend(
[
WMDevice(client, device_info, sub_device=sub_device, sub_key=key)
for key in get_sub_keys(device_info, sub_device)
]
)
return dev_list
return None
23 changes: 15 additions & 8 deletions custom_components/smartthinq_sensors/wideq/model_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,24 @@ class ModelInfo(ABC):
"""The base abstract class for a device model's capabilities."""

@staticmethod
def get_model_info(model_data: dict) -> ModelInfo | None:
def get_model_info(
model_data: dict, sub_device: str | None = None
) -> ModelInfo | None:
"""Return the correct model info."""
if ModelInfoV2AC.is_valid_model_data(model_data):
if sub_device is not None:
data = {"Info": model_data["Info"], **model_data[sub_device]}
else:
data = model_data

if ModelInfoV2AC.is_valid_model_data(data):
# this is new V2 model for AC
return ModelInfoV2AC(model_data)
if ModelInfoV1.is_valid_model_data(model_data):
return ModelInfoV2AC(data)
if ModelInfoV1.is_valid_model_data(data):
# this is old V1 model
return ModelInfoV1(model_data)
if ModelInfoV2.is_valid_model_data(model_data):
return ModelInfoV1(data)
if ModelInfoV2.is_valid_model_data(data):
# this is new V2 model
return ModelInfoV2(model_data)
return ModelInfoV2(data)
return None

@staticmethod
Expand All @@ -63,7 +70,7 @@ def as_dict(self):
"""Return the data dictionary"""
if not self._data:
return {}
return self._data.copy()
return deepcopy(self._data)

@property
@abstractmethod
Expand Down
Loading