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

feat(b8): first implementation #225

Merged
merged 18 commits into from
Jul 30, 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
154 changes: 154 additions & 0 deletions midealocal/devices/b8/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
"""Midea local B8 device."""

import logging
from enum import IntEnum
from typing import Any

from midealocal.device import MideaDevice
from midealocal.devices.b8.const import (
B8CleanMode,
B8ControlType,
B8DeviceAttributes,
B8ErrorCanFixDescription,
B8ErrorType,
B8FanLevel,
B8FunctionType,
B8MopState,
B8Moviment,
B8Speed,
B8WaterLevel,
B8WorkMode,
B8WorkStatus,
)
from midealocal.devices.b8.message import (
MessageB8Response,
MessageQuery,
MessageSet,
MessageSetCommand,
)

_LOGGER = logging.getLogger(__name__)


class MideaB8Device(MideaDevice):
"""Midea B8 device."""

def __init__(
self,
name: str,
device_id: int,
ip_address: str,
port: int,
token: str,
key: str,
protocol: int,
model: str,
subtype: int,
customize: str, # noqa: ARG002
) -> None:
"""Initialize Midea B8 device."""
super().__init__(
name=name,
device_id=device_id,
device_type=0xB8,
ip_address=ip_address,
port=port,
token=token,
key=key,
protocol=protocol,
model=model,
subtype=subtype,
attributes={
B8DeviceAttributes.WORK_STATUS: B8WorkStatus.NONE.name.lower(),
B8DeviceAttributes.FUNCTION_TYPE: B8FunctionType.NONE.name.lower(),
B8DeviceAttributes.CONTROL_TYPE: B8ControlType.NONE.name.lower(),
B8DeviceAttributes.MOVE_DIRECTION: B8Moviment.NONE.name.lower(),
B8DeviceAttributes.CLEAN_MODE: B8CleanMode.NONE.name.lower(),
B8DeviceAttributes.FAN_LEVEL: B8FanLevel.OFF.name.lower(),
B8DeviceAttributes.AREA: 0,
B8DeviceAttributes.WATER_LEVEL: B8WaterLevel.OFF.name.lower(),
B8DeviceAttributes.VOICE_VOLUME: 0,
B8DeviceAttributes.MOP: B8MopState.OFF.name.lower(),
B8DeviceAttributes.CARPET_SWITCH: False,
B8DeviceAttributes.SPEED: B8Speed.HIGH.name.lower(),
B8DeviceAttributes.HAVE_RESERVE_TASK: False,
B8DeviceAttributes.BATTERY_PERCENT: 0,
B8DeviceAttributes.WORK_TIME: 0,
B8DeviceAttributes.UV_SWITCH: False,
B8DeviceAttributes.WIFI_SWITCH: False,
B8DeviceAttributes.VOICE_SWITCH: False,
B8DeviceAttributes.COMMAND_SOURCE: False,
B8DeviceAttributes.ERROR_TYPE: B8ErrorType.NO.name.lower(),
B8DeviceAttributes.ERROR_DESC: B8ErrorCanFixDescription.NO.name.lower(),
B8DeviceAttributes.DEVICE_ERROR: False,
B8DeviceAttributes.BOARD_COMMUNICATION_ERROR: False,
B8DeviceAttributes.LASER_SENSOR_SHELTER: False,
B8DeviceAttributes.LASER_SENSOR_ERROR: False,
},
)

def build_query(self) -> list[MessageQuery]:
"""Midea B8 device build query."""
return [MessageQuery(self._protocol_version)]

def process_message(self, msg: bytes) -> dict[str, Any]:
"""Midea B8 device process message."""
message = MessageB8Response(msg)
_LOGGER.debug("[%s] Received: %s", self.device_id, message)
new_status = {}
for status in self._attributes:
if hasattr(message, str(status)):
value = getattr(message, str(status))
if isinstance(value, IntEnum): # lowercase name for IntEnums
value = value.name.lower()
self._attributes[status] = value
new_status[str(status)] = self._attributes[status]
return new_status

def _gen_set_msg_default_values(self) -> MessageSet:
msg = MessageSet(self._protocol_version)
msg.clean_mode = B8CleanMode[
self.attributes[B8DeviceAttributes.CLEAN_MODE].upper()
]
msg.fan_level = B8FanLevel[
self.attributes[B8DeviceAttributes.FAN_LEVEL].upper()
]
msg.water_level = B8WaterLevel[
self.attributes[B8DeviceAttributes.WATER_LEVEL].upper()
]
msg.voice_volume = self.attributes[B8DeviceAttributes.VOICE_VOLUME]
return msg

def set_work_mode(self, work_mode: B8WorkMode) -> None:
"""Midea B8 device set work mode."""
if work_mode == B8WorkMode.WORK:
self.set_attribute(
B8DeviceAttributes.CLEAN_MODE,
self.attributes[B8DeviceAttributes.CLEAN_MODE],
)
return

msg = MessageSetCommand(self._protocol_version, work_mode=work_mode)
self.build_send(msg)

def set_attribute(self, attr: str, value: bool | int | str) -> None:
"""Midea B8 device set attribute."""
rokam marked this conversation as resolved.
Show resolved Hide resolved
try:
msg = self._gen_set_msg_default_values()
if attr == B8DeviceAttributes.CLEAN_MODE:
msg.clean_mode = B8CleanMode[str(value).upper()]
elif attr == B8DeviceAttributes.FAN_LEVEL:
msg.fan_level = B8FanLevel[str(value).upper()]
elif attr == B8DeviceAttributes.WATER_LEVEL:
msg.water_level = B8WaterLevel[str(value).upper()]
elif attr == B8DeviceAttributes.VOICE_VOLUME:
msg.voice_volume = int(value)

if msg is not None:
self.build_send(msg)
except KeyError:
_LOGGER.exception("Wrong value for attribute %s: %s", attr, value)


class MideaAppliance(MideaB8Device):
"""Midea B8 appliance."""
200 changes: 200 additions & 0 deletions midealocal/devices/b8/const.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
"""Midea local B8 device const."""

from enum import IntEnum, StrEnum


class B8DeviceAttributes(StrEnum):
"""Midea B8 device attributes."""

WORK_STATUS = "work_status"
FUNCTION_TYPE = "function_type"
CONTROL_TYPE = "control_type"
MOVE_DIRECTION = "move_direction"
CLEAN_MODE = "clean_mode"
FAN_LEVEL = "fan_level"
AREA = "area"
WATER_LEVEL = "water_level"
VOICE_VOLUME = "voice_volume"
MOP = "mop"
CARPET_SWITCH = "carpet_switch"
SPEED = "speed"
HAVE_RESERVE_TASK = "have_reserve_task"
BATTERY_PERCENT = "battery_percent"
WORK_TIME = "work_time"
UV_SWITCH = "uv_switch"
WIFI_SWITCH = "wifi_switch"
VOICE_SWITCH = "voice_switch"
COMMAND_SOURCE = "command_source"
ERROR_TYPE = "error_type"
ERROR_DESC = "error_desc"
DEVICE_ERROR = "device_error"
BOARD_COMMUNICATION_ERROR = "board_communication_error"
LASER_SENSOR_SHELTER = "laser_sensor_shelter"
LASER_SENSOR_ERROR = "laser_sensor_error"


class B8WorkMode(IntEnum):
"""Midea B8 work mode."""

CHARGE = 0x01
WORK = 0x02
STOP = 0x03
PAUSE = 0x1B


class B8WorkStatus(IntEnum):
"""Midea B8 work status."""

NONE = 0x00
CHARGE = 0x01
WORK = 0x02
STOP = 0x03
CHARGING_ON_DOCK = 0x04
RESERVE_TASK_FINISHED = 0x05
CHARGE_FINISH = 0x06
CHARGING_WITH_WIRE = 0x07
PAUSE = 0x08
UPDATING = 0x09
SAVING_MAP = 0x0A
ERROR = 0x0B
SLEEP = 0x0C
CHARGE_PAUSE = 0x0D
RELOCATE = 0x0E
ELECTROLYSED_WATER_MAKING = 0x0F
DUST_COLLECTING = 0x10
BACK_DUST_COLLECTING = 0x11
SLEEP_IN_STATION = 0x12


class B8FunctionType(IntEnum):
"""Midea B8 function type."""

NONE = 0x00
DUST_BOX_CLEANING = 0x01
WATER_TANK_CLEANING = 0x02


class B8ControlType(IntEnum):
"""Midea B8 control type."""

NONE = 0x0
MANUAL = 0x1
AUTO = 0x2


class B8Moviment(IntEnum):
"""Midea B8 movement."""

NONE = 0x0
FORWARD = 0x1
BACK = 0x2
LEFT = 0x3
RIGHT = 0x4


class B8CleanMode(IntEnum):
"""Midea B8 clean mode."""

NONE = 0x00
RANDOM = 0x01
ARC = 0x02
EDGE = 0x03
EMPHASES = 0x04
SCREW = 0x05
BED = 0x06
WIDE_SCREW = 0x07
AUTO = 0x08
AREA = 0x09
ZONE_INDEX = 0x0A
ZONE_RECT = 0x0B
PATH = 0x0C


class B8FanLevel(IntEnum):
"""Midea B8 fan level."""

OFF = 0x0
SOFT = 0x1
NORMAL = 0x2
HIGH = 0x3
LOW = 0x4


class B8WaterLevel(IntEnum):
"""Midea B8 water level."""

OFF = 0x0
LOW = 0x1
NORMAL = 0x2
HIGH = 0x3


class B8MopState(IntEnum):
"""Midea B8 mop state."""

OFF = 0x0
ON = 0x1
LACK_WATER = 0x2


class B8Speed(IntEnum):
"""Midea B8 speed."""

LOW = 0x1
HIGH = 0x0


class B8ErrorType(IntEnum):
"""Midea B8 error type."""

NO = 0x00
CAN_FIX = 0x01
REBOOT = 0x02
WARNING = 0x03


class B8ErrorCanFixDescription(IntEnum):
"""Midea B8 error can fix description."""

NO = 0x0
FIX_DUST = 0x01
FIX_WHEEL_HANG = 0x02
FIX_WHEEL_OVERLOAD = 0x03
FIX_SIDE_BRUSH_OVERLOAD = 0x04
FIX_ROLL_BRUSH_OVERLOAD = 0x05
FIX_DUST_ENGINE = 0x06
FIX_FRONT_PANEL = 0x07
FIX_RADAR_MASK = 0x08
FIX_DROP_SENSOR = 0x09
FIX_LOW_BATTERY = 0x0A
FIX_ABNORMAL_POSTURE = 0x0B
FIX_LASER_SENSOR = 0x0C
FIX_EDGE_SENSOR = 0x0D
FIX_START_IN_FORBID_AREA = 0x0E
FIX_START_IN_STRONG_MAGNETIC = 0x0F
FIX_LASER_SENSOR_BLOCKED = 0x10


class B8ErrorRebootDescription(IntEnum):
"""Midea B8 error reboot description."""

NO = 0x00
REBOOT_LASER_COMM_FAIL = 0x01
REBOOT_ROBOT_COMM_FAIL = 0x02
REBOOT_INNER_FAIL = 0x03


class B8ErrorWarningDescription(IntEnum):
"""Midea B8 error warning description."""

NO = 0x00
WARN_LOCATION_FAIL = 0x01
WARN_LOW_BATTERY = 0x02
WARN_FULL_DUST = 0x03
WARN_LOW_WATER = 0x04


class B8StatusType(IntEnum):
"""B8 Status Type."""

X01 = 0x01
Loading
Loading