"""
Each device has a specification - list of classes (XEntity childs). Platform
will setup entity if it isinstance() of platform entity class.

User can override SwitchEntity of any device via YAML (device_class option).

XEntity properties:
- params - required, set of parameters that this entity can read
- param - optional, entity main parameter (useful for sensors)
- uid - optional, entity unique_id tail

Developer can change global properties of existing classes via spec function.
"""

from homeassistant.components.binary_sensor import BinarySensorEntity
from homeassistant.components.light import ColorMode, LightEntity
from homeassistant.components.sensor import SensorEntity
from homeassistant.components.switch import SwitchEntity

from .ewelink import XDevice
from ..alarm_control_panel import XPanelAlarm
from ..binary_sensor import (
    XBinarySensor,
    XHumanSensor,
    XLightSensor,
    XWaterSensor,
    XWiFiDoor,
    XZigbeeMotion,
)
from ..button import XButton
from ..climate import XClimateNS, XClimateTH, XThermostat, XThermostatTRVZB
from ..core.entity import XEntity
from ..cover import XCover, XCoverDualR3, XCoverOP, XCoverT5, XZigbeeCover
from ..fan import XDiffuserFan, XFan, XFanDualR3, XToggleFan
from ..light import (
    XDiffuserLight,
    XDimmer,
    XFanLight,
    XLight57,
    XLightB02,
    XLightB05B,
    XLightB1,
    XLightD1,
    XLightGroup,
    XLightL1,
    XLightL3,
    XOnOffLight,
    XT5Light,
    XZigbeeColorTemp,
    XZigbeeLight,
)
from ..media_player import XPanelBuzzer
from ..number import XPulseWidth, XSensitivity
from ..remote import XRemote
from ..select import XSelectStartup
from ..sensor import (
    XEnergySensor,
    XEnergySensorDualR3,
    XEnergySensorPOWR3,
    XEnergyTotal,
    XHexVoltageTRVZB,
    XHumCorrection,
    XHumidityTH,
    XOutdoorTempNS,
    XRemoteButton,
    XSensor,
    XT5Action,
    XTempCorrection,
    XTemperatureTH,
    XTodayWaterUsage,
    XUnknown,
    XWiFiDoorBattery,
)
from ..switch import (
    XBoolSwitch,
    XDetach,
    XPanelScreen,
    XSwitch,
    XSwitchPOWR3,
    XSwitchTH,
    XSwitches,
    XT5WorkMode,
    XToggle,
    XZigbeeSwitches,
)

# supported custom device_class
DEVICE_CLASS = {
    "binary_sensor": (XEntity, BinarySensorEntity),
    "fan": (XToggleFan,),  # using custom class for overriding is_on function
    "light": (XOnOffLight,),  # fix color modes support
    "sensor": (XEntity, SensorEntity),
    "switch": (XEntity, SwitchEntity),
}


def unwrap_cached_properties(attrs: dict):
    """Fix metaclass CachedProperties problem in latest Hass."""
    for k, v in list(attrs.items()):
        if k.startswith("_attr_") and f"_{k}" in attrs and isinstance(v, property):
            attrs[k] = attrs.pop(f"_{k}")
    return attrs


def spec(cls, base: str = None, enabled: bool = None, **kwargs) -> type:
    """Make duplicate for cls class with changes in kwargs params.

    If `base` param provided - can change Entity base class for cls. So it can
    be added to different Hass domain.
    """
    if enabled is not None:
        kwargs["_attr_entity_registry_enabled_default"] = enabled
    if base:
        attrs = cls.__mro__[-len(XSwitch.__mro__) :: -1]
        attrs = {k: v for b in attrs for k, v in b.__dict__.items()}
        attrs = unwrap_cached_properties({**attrs, **kwargs})
        return type(cls.__name__, DEVICE_CLASS[base], attrs)
    return type(cls.__name__, (cls,), kwargs)


Switch1 = spec(XSwitches, channel=0, uid="1")
Switch2 = spec(XSwitches, channel=1, uid="2")
Switch3 = spec(XSwitches, channel=2, uid="3")
Switch4 = spec(XSwitches, channel=3, uid="4")

Startup1 = spec(XSelectStartup, channel=0, uid="1")
Startup2 = spec(XSelectStartup, channel=1, uid="2")
Startup3 = spec(XSelectStartup, channel=2, uid="3")
Startup4 = spec(XSelectStartup, channel=3, uid="4")

XSensor100 = spec(XSensor, multiply=0.01, round=2)

Battery = spec(XSensor, param="battery")
LED = spec(XToggle, param="sledOnline", uid="led", enabled=False)
RSSI = spec(XSensor, param="rssi", enabled=False)
STARTUP = spec(XToggle, param="startup", enabled=False)
PULSE = spec(XToggle, param="pulse", enabled=False)
ZRSSI = spec(XSensor, param="subDevRssi", uid="rssi", enabled=False)

SPEC_SWITCH = [XSwitch, LED, RSSI, PULSE, XPulseWidth]
SPEC_1CH = [Switch1, LED, RSSI]
SPEC_2CH = [Switch1, Switch2, LED, RSSI]
SPEC_3CH = [Switch1, Switch2, Switch3, LED, RSSI]
SPEC_4CH = [Switch1, Switch2, Switch3, Switch4, LED, RSSI]

Current1 = spec(XSensor100, param="current_00", uid="current_1")
Current2 = spec(XSensor100, param="current_01", uid="current_2")
Current3 = spec(XSensor100, param="current_02", uid="current_3")
Current4 = spec(XSensor100, param="current_03", uid="current_4")
Voltage1 = spec(XSensor100, param="voltage_00", uid="voltage_1")
Voltage2 = spec(XSensor100, param="voltage_01", uid="voltage_2")
Voltage3 = spec(XSensor100, param="voltage_02", uid="voltage_3")
Voltage4 = spec(XSensor100, param="voltage_03", uid="voltage_4")
Power1 = spec(XSensor100, param="actPow_00", uid="power_1")
Power2 = spec(XSensor100, param="actPow_01", uid="power_2")
Power3 = spec(XSensor100, param="actPow_02", uid="power_3")
Power4 = spec(XSensor100, param="actPow_03", uid="power_4")

EnergyPOW = spec(
    XEnergySensor,
    param="hundredDaysKwhData",
    uid="energy",
    get_params={"hundredDaysKwh": "get"},
)

# backward compatibility for unique_id
DoorLock = spec(XBinarySensor, param="lock", uid="", default_class="door")

XT5Alarm = spec(XButton, param="soundAction", value=1, uid="alarm", enabled=False)
XT5Bell = spec(XButton, param="soundAction", value=2, uid="bell", enabled=False)

# https://github.com/CoolKit-Technologies/eWeLink-API/blob/main/en/UIIDProtocol.md
DEVICES = {
    1: SPEC_SWITCH,
    2: SPEC_2CH,
    3: SPEC_3CH,
    4: SPEC_4CH,
    # Sonoff POW (first)
    5: [
        XSwitch,
        LED,
        RSSI,
        spec(XSensor, param="power"),
        EnergyPOW,
    ],
    6: SPEC_SWITCH,
    # Sonoff T1 2CH
    7: SPEC_2CH,
    # Sonoff T1 3CH
    8: SPEC_3CH,
    9: SPEC_4CH,
    # King Art - King Q4 Cover (only cloud)
    11: [XCover, LED, RSSI],
    # Sonoff Basic (3rd party)
    14: SPEC_SWITCH,
    # Sonoff TH16
    15: [
        XSwitchTH,
        XClimateTH,
        XTemperatureTH,
        XHumidityTH,
        LED,
        RSSI,
    ],
    18: [
        spec(XSensor, param="temperature"),
        spec(XSensor, param="humidity"),
        spec(XSensor, param="dusty"),
        spec(XSensor, param="light"),
        spec(XSensor, param="noise"),
    ],
    # Sonoff B1 (only cloud)
    22: [XLightB1, RSSI],
    # Essential Oils Diffuser (YB-01), https://github.com/AlexxIT/SonoffLAN/issues/173
    25: [
        XDiffuserFan,
        XDiffuserLight,
        RSSI,
        spec(XBinarySensor, param="water", uid=""),
    ],
    # Sonoff RF Brigde 433
    28: [XRemote, LED, RSSI],
    29: SPEC_2CH,
    30: SPEC_3CH,
    31: SPEC_4CH,
    # Sonoff POWR2
    32: [
        XSwitch,
        LED,
        RSSI,
        spec(XSensor, param="current"),
        spec(XSensor, param="power"),
        spec(XSensor, param="voltage"),
        EnergyPOW,
    ],
    # https://github.com/AlexxIT/SonoffLAN/issues/985
    33: [XLightL1, RSSI],
    # Sonoff iFan02 and iFan03
    34: [
        XFan,
        XFanLight,
        LED,
        RSSI,
    ],
    # KING-M4 (dimmer, only cloud)
    36: [XDimmer, RSSI],
    # Sonoff D1
    44: [XLightD1, RSSI],
    # Mosquito Killer Lamp
    57: [XLight57, RSSI],
    # Sonoff LED (only cloud)
    59: [XLightL1, RSSI],
    # ZigBee Bridge
    66: [RSSI, LED, spec(XBinarySensor, param="zled", enabled=False)],
    # KingArt Garage Door Opener (KING-Q1)
    # https://github.com/AlexxIT/SonoffLAN/issues/1257
    67: [XCoverOP],
    # Sonoff Micro
    77: SPEC_1CH,
    # AltoBeam, Single channel switch，multi -channel
    # https://github.com/AlexxIT/SonoffLAN/issues/615
    78: SPEC_1CH,
    81: SPEC_1CH,
    82: SPEC_2CH,
    83: SPEC_3CH,
    84: SPEC_4CH,
    # ST-03, https://github.com/AlexxIT/SonoffLAN/issues/1304
    91: [XCoverOP],
    # Sonoff DW2 Door/Window sensor
    102: [XWiFiDoor, XWiFiDoorBattery, RSSI],
    # Sonoff B02 CCT bulb
    103: [XLightB02, RSSI],
    # Sonoff B05-B RGB+CCT color bulb
    104: [XLightB05B, RSSI],
    107: SPEC_1CH,
    # Sonoff DualR3
    126: [
        Switch1,
        Switch2,
        Startup1,
        Startup2,
        RSSI,
        Current1,
        Current2,
        Voltage1,
        Voltage2,
        Power1,
        Power2,
        spec(
            XEnergySensorDualR3,
            param="kwhHistories_00",
            uid="energy_1",
            get_params={"getKwh_00": 2},
        ),
        spec(
            XEnergySensorDualR3,
            param="kwhHistories_01",
            uid="energy_2",
            get_params={"getKwh_01": 2},
        ),
    ],
    # https://github.com/AlexxIT/SonoffLAN/issues/358
    127: [XThermostat],
    # SPM-Main
    128: [LED],
    # SPM-4Relay, https://github.com/AlexxIT/SonoffLAN/issues/658
    130: [
        Switch1,
        Switch2,
        Switch3,
        Switch4,
        Startup1,
        Startup2,
        Startup3,
        Startup4,
        Current1,
        Current2,
        Current3,
        Current4,
        Voltage1,
        Voltage2,
        Voltage3,
        Voltage4,
        Power1,
        Power2,
        Power3,
        Power4,
        spec(
            XEnergySensorDualR3,
            param="kwhHistories_00",
            uid="energy_1",
            get_params={"getKwh_00": 2},
        ),
        spec(
            XEnergySensorDualR3,
            param="kwhHistories_01",
            uid="energy_2",
            get_params={"getKwh_01": 2},
        ),
        spec(
            XEnergySensorDualR3,
            param="kwhHistories_02",
            uid="energy_3",
            get_params={"getKwh_02": 2},
        ),
        spec(
            XEnergySensorDualR3,
            param="kwhHistories_03",
            uid="energy_4",
            get_params={"getKwh_03": 2},
        ),
    ],
    # Sonoff NS Panel, https://github.com/AlexxIT/SonoffLAN/issues/751
    133: [
        # Humidity. ALWAYS 50... NSPanel DOESN'T HAVE HUMIDITY SENSOR
        Switch1,
        Switch2,
        XClimateNS,
        XTempCorrection,
        XOutdoorTempNS,
    ],
    # Sonoff B02-BL, https://github.com/AlexxIT/SonoffLAN/issues/1026
    135: [XLightB02, RSSI],
    # Sonoff B05-BL
    # https://github.com/AlexxIT/SonoffLAN/issues/766
    # https://github.com/AlexxIT/SonoffLAN/issues/890
    # https://github.com/AlexxIT/SonoffLAN/pull/892
    # https://github.com/AlexxIT/SonoffLAN/pull/1035
    136: [spec(XLightB05B, min_ct=0, max_ct=100), RSSI],
    137: [XLightL1, RSSI],
    # MINIR3, https://github.com/AlexxIT/SonoffLAN/issues/623#issuecomment-1365841454
    # MINIR4
    # MINIR4M, https://github.com/AlexxIT/SonoffLAN/issues/1632
    138: [
        Switch1,
        Startup1,
        LED,
        RSSI,
        XDetach,
        spec(XRemoteButton, param="action"),
    ],
    # DW2-Wi-Fi-L, https://github.com/AlexxIT/SonoffLAN/issues/808
    154: [XWiFiDoor, Battery, RSSI],
    # Sonoff SwitchMan M5-1C, https://github.com/AlexxIT/SonoffLAN/issues/1432
    160: SPEC_1CH,
    # Sonoff SwitchMan M5-2C, https://github.com/AlexxIT/SonoffLAN/issues/1432
    161: SPEC_2CH,
    # Sonoff SwitchMan M5-3C, https://github.com/AlexxIT/SonoffLAN/issues/659
    162: SPEC_3CH,
    # DualR3 Lite, without power consumption
    165: [
        Switch1,
        Switch2,
        Startup1,
        Startup2,
        RSSI,
    ],
    # ZBBridge-P, https://github.com/AlexxIT/SonoffLAN/issues/857
    168: [RSSI],
    # Sonoff L3-5M-P
    173: [XLightL3, RSSI],
    # Sonoff R5 (6-key remote)
    174: [XRemoteButton],
    # Sonoff S-Mate
    177: [XRemoteButton],
    # Sonoff THR320D or THR316D
    181: [
        XSwitchTH,
        XTemperatureTH,
        XHumidityTH,
        LED,
        RSSI,
    ],
    # Sonoff S40
    182: [
        Switch1,
        LED,
        RSSI,
        spec(XSensor, param="current"),
        spec(XSensor, param="power"),
        spec(XSensor, param="voltage"),
        EnergyPOW,
    ],
    # Sonoff POWR3
    # S60TPF, https://github.com/AlexxIT/SonoffLAN/issues/1514
    190: [
        XSwitchPOWR3,
        LED,
        RSSI,
        spec(XSensor100, param="current"),
        spec(XSensor100, param="power"),
        spec(XSensor100, param="voltage"),
        spec(XEnergyTotal, param="dayKwh", uid="energy_day", multiply=0.01, round=2),
        spec(
            XEnergyTotal, param="monthKwh", uid="energy_month", multiply=0.01, round=2
        ),
        spec(
            XEnergySensorPOWR3,
            param="hoursKwhData",
            uid="energy",
            get_params={"getHoursKwh": {"start": 0, "end": 24 * 30 - 1}},
        ),
    ],
    # NSPanel Pro, https://github.com/AlexxIT/SonoffLAN/issues/984
    195: [XTemperatureTH, XPanelAlarm, XPanelBuzzer, XPanelScreen],
    # Sonoff TX ULTIMATE T5-1C-86, https://github.com/AlexxIT/SonoffLAN/issues/1183
    209: [Switch1, Startup1, XT5Light, XT5Action, XT5Alarm, XT5Bell],
    # Sonoff TX ULTIMATE T5-2C-86
    210: [
        Switch1,
        Switch2,
        Startup1,
        Startup2,
        XT5Light,
        XT5Action,
        XT5Alarm,
        XT5Bell,
    ],
    # Sonoff TX ULTIMATE T5-3C-86
    211: [
        Switch1,
        Switch2,
        Switch3,
        Startup1,
        Startup2,
        Startup3,
        XT5Light,
        XT5Action,
        XT5Alarm,
        XT5Bell,
        XCoverT5,
        XT5WorkMode,
    ],
    # Sonoff TX ULTIMATE T5-4C-86, https://github.com/AlexxIT/SonoffLAN/issues/1251
    212: [
        Switch1,
        Switch2,
        Switch3,
        Switch4,
        Startup1,
        Startup2,
        Startup3,
        Startup4,
        XT5Light,
        XT5Action,
        XT5Alarm,
        XT5Bell,
    ],
    # CK-BL602-PCSW-01(225), https://github.com/AlexxIT/SonoffLAN/issues/1616
    225: [
        spec(XBoolSwitch, param="switch"),
        spec(XButton, param="restart", value=True, uid="restart"),
        spec(XButton, param="forceShutdown", value=True, uid="shutdown"),
        spec(XBoolSwitch, param="childLock", uid="child_lock", enabled=False),
        LED,
        RSSI,
        STARTUP,
    ],
    # CK-BL602-W102SW18-01(226)
    226: [
        spec(XBoolSwitch, param="switch"),
        LED,
        RSSI,
        spec(XSensor, param="phase_0_c", uid="current"),
        spec(XSensor, param="phase_0_p", uid="power"),
        spec(XSensor, param="phase_0_v", uid="voltage"),
        spec(XEnergyTotal, param="totalPower", uid="energy"),
    ],
    # https://github.com/AlexxIT/SonoffLAN/issues/1634
    258: [XCover, LED, RSSI],
    # CK-BL602-SWP1-02(262), https://github.com/AlexxIT/SonoffLAN/issues/1630
    262: [
        Switch1,
        Switch2,
        Switch3,
        Switch4,
        LED,
        RSSI,
        spec(XSensor100, param="power"),
        spec(XSensor100, param="current"),
        spec(XSensor100, param="voltage"),
    ],
    # zigbee_ON_OFF_SWITCH_1000
    1000: [XRemoteButton, Battery],
    # ZCL_HA_DEVICEID_ON_OFF_LIGHT, https://github.com/AlexxIT/SonoffLAN/issues/1195
    1256: [XSwitch],
    # ZigbeeWhiteLight
    1257: [XLightD1],
    # https://github.com/AlexxIT/SonoffLAN/issues/1557
    1258: [XZigbeeColorTemp],
    # https://github.com/AlexxIT/SonoffLAN/issues/972
    1514: [XZigbeeCover, spec(XSensor, param="battery", multiply=2)],
    # ZCL_HA_DEVICEID_TEMPERATURE_SENSOR
    1770: [
        spec(XSensor100, param="temperature"),
        spec(XSensor100, param="humidity"),
        Battery,
    ],
    # https://github.com/AlexxIT/SonoffLAN/issues/1150
    1771: [
        spec(XSensor100, param="temperature"),
        spec(XSensor100, param="humidity"),
        Battery,
    ],
    # ZIGBEE_MOBILE_SENSOR
    2026: [XZigbeeMotion, Battery],
    # ZIGBEE_DOOR_AND_WINDOW_SENSOR
    3026: [DoorLock, Battery],
    # ZigbeeColorTunableWhiteLight, https://github.com/AlexxIT/SonoffLAN/issues/1265
    3258: [XZigbeeLight],
    # https://github.com/AlexxIT/SonoffLAN/issues/852
    4026: [
        spec(XBinarySensor, param="water", uid="", default_class="moisture"),
        Battery,
    ],
    4256: [
        spec(XZigbeeSwitches, channel=0, uid="1"),
        spec(XZigbeeSwitches, channel=1, uid="2"),
        spec(XZigbeeSwitches, channel=2, uid="3"),
        spec(XZigbeeSwitches, channel=3, uid="4"),
    ],
    7000: [XRemoteButton, Battery],
    # SNZB-03P, https://github.com/AlexxIT/SonoffLAN/issues/1435
    7002: [XZigbeeMotion, XLightSensor, Battery, ZRSSI],
    # SNZB-04P, https://github.com/AlexxIT/SonoffLAN/issues/1439
    7003: [DoorLock, Battery, ZRSSI],
    # ZBMINIL2, https://github.com/AlexxIT/SonoffLAN/issues/1398
    7004: [XSwitch, ZRSSI],
    # https://github.com/AlexxIT/SonoffLAN/issues/1283
    7006: [XZigbeeCover, Battery],
    # CK-BL702-AL-01(7009_Z102LG03-1), https://github.com/AlexxIT/SonoffLAN/issues/1456
    7009: [XZigbeeLight],
    # ZBMicro, https://github.com/AlexxIT/SonoffLAN/issues/1525
    7010: [XSwitch, ZRSSI],
    # https://github.com/AlexxIT/SonoffLAN/issues/1166
    7014: [
        spec(XSensor100, param="temperature"),
        spec(XSensor100, param="humidity"),
        Battery,
    ],
    # SNZB-06P
    7016: [XHumanSensor, XLightSensor, XSensitivity, ZRSSI],
    7017: [
        XThermostatTRVZB,
        spec(XSensor, param="workMode", uid="work_mode"),
        spec(XSensor, param="workState", uid="work_state"),
        spec(XSensor, param="temperature", multiply=0.1),
        spec(
            XSensor,
            param="manTargetTemp",
            multiply=0.1,
            uid="manual_target_temperature",
        ),
        spec(
            XSensor,
            param="autoTargetTemp",
            multiply=0.1,
            uid="auto_target_temperature",
        ),
        spec(
            XSensor,
            param="curTargetTemp",
            multiply=0.1,
            uid="current_target_temperature",
        ),
        spec(
            XSensor,
            param="ecoTargetTemp",
            multiply=0.1,
            uid="eco_target_temperature",
        ),
        spec(
            XSensor,
            param="tempCorrection",
            multiply=0.1,
            uid="temperature_correction",
        ),
        spec(XBoolSwitch, param="childLock", uid="child_lock"),
        spec(XBoolSwitch, param="windowSwitch", uid="window_switch"),
        spec(XHexVoltageTRVZB, param="runVoltage", uid="run_voltage"),
        spec(XHexVoltageTRVZB, param="limitVoltage", uid="limit_voltage"),
        Battery,
        ZRSSI,
    ],
    # SNZB-05P, https://github.com/AlexxIT/SonoffLAN/issues/1496
    7019: [XWaterSensor, Battery],
    # SWV, https://github.com/AlexxIT/SonoffLAN/issues/1497
    7027: [
        spec(XBoolSwitch, param="switch"),
        Battery,
        XTodayWaterUsage,
        ZRSSI,
    ],
    # S60ZBTPF, https://github.com/AlexxIT/SonoffLAN/issues/1615
    7032: [
        Switch1,
        spec(XSensor100, param="power"),
        spec(XSensor100, param="current"),
        spec(XSensor100, param="voltage"),
    ],
    # SNZB-02WD, https://github.com/AlexxIT/SonoffLAN/issues/1612
    7033: [XTempCorrection, XHumCorrection, Battery, ZRSSI],
}


def get_spec(device: dict) -> list:
    uiid = device["extra"]["uiid"]

    if uiid in DEVICES:
        classes = DEVICES[uiid]
    elif "switch" in device["params"]:
        classes = SPEC_SWITCH
    elif "switches" in device["params"]:
        classes = SPEC_4CH
    else:
        classes = [XUnknown]

    # DualR3 in cover mode
    if uiid in [126, 165] and device["params"].get("workMode") == 2:
        classes = [cls for cls in classes if XSwitches not in cls.__bases__]
        classes = [XCoverDualR3, XFanDualR3] + classes

    # NSPanel Climate disable without switch configuration
    if uiid in [133] and not device["params"].get("HMI_ATCDevice"):
        classes = [cls for cls in classes if XClimateNS not in cls.__bases__]

    # SNZB-06P has no battery
    if uiid in [2026] and not device["params"].get("battery"):
        classes = [cls for cls in classes if cls != Battery]

    if "device_class" in device:
        classes = get_custom_spec(classes, device["device_class"])

    return classes


def get_custom_spec(classes: list, device_class):
    """Supported device_class formats:
    1. Single channel:
       device_class: light
    2. Multiple channels:
       device_class: [light, fan, switch]
    3. Light with brightness control
       device_class:
         - switch  # entity 1 (channel 1)
         - light: [2, 3]  # entity 2 (channels 2 and 3)
         - fan: 4  # entity 3 (channel 4)
    """
    # 1. single channel
    if isinstance(device_class, str):
        if device_class in DEVICE_CLASS:
            classes = [spec(classes[0], base=device_class)] + classes[1:]

    elif isinstance(device_class, list):
        # remove all default multichannel classes from spec
        base = classes[0].__base__
        classes = [cls for cls in classes if base not in cls.__bases__]

        for i, sub_class in enumerate(device_class):
            # 2. simple multichannel
            if isinstance(sub_class, str):
                classes.append(spec(base, channel=i, uid=str(i + 1), base=sub_class))

            elif isinstance(sub_class, dict):
                sub_class, i = next(iter(sub_class.items()))

                # 3. light with brightness
                if isinstance(i, list) and sub_class == "light":
                    chs = [x - 1 for x in i]
                    uid = "".join(str(x) for x in i)
                    classes.append(spec(XLightGroup, channels=chs, uid=uid))

                # 4. multichannel
                elif isinstance(i, int):
                    classes.append(
                        spec(base, channel=(i - 1), uid=str(i), base=sub_class)
                    )

    return classes


def get_spec_wrapper(func, sensors: list):
    def wrapped(device: dict) -> list:
        classes = func(device)
        for uid in sensors:
            if (uid in device["params"] or uid == "host") and all(
                cls.param != uid and cls.uid != uid for cls in classes
            ):
                classes.append(spec(XSensor, param=uid))
        return classes

    return wrapped


def set_default_class(device_class: str):
    if device_class != "light":
        return
    for cls in (XSwitch, XSwitches):
        cls.__bases__ = (XEntity, LightEntity)
        cls._attr_color_mode = ColorMode.ONOFF
        cls._attr_supported_color_modes = {ColorMode.ONOFF}


# Cloud: NSPanel
DIY = {
    # DIY type, UIID, Brand, Model/Name
    "plug": [1, None, "Single Channel DIY"],  # POWR316
    "strip": [4, None, "Multi Channel DIY"],  # 4CHPROR3
    "diy_plug": [1, "SONOFF", "MINI DIY"],
    "enhanced_plug": [5, "SONOFF", "POW DIY"],  # POWR2
    "th_plug": [15, "SONOFF", "TH DIY"],  # TH16R2
    "rf": [28, "SONOFF", "RFBridge DIY"],
    "fan_light": [34, "SONOFF", "iFan DIY"],
    "light": [44, "SONOFF", "D1 DIY"],  # don't know if light exist
    "diylight": [44, "SONOFF", "D1 DIY"],
    "diy_light": [136, "SONOFF", "B0x-BL DIY"],
    "switch_radar": [77, "SONOFF", "Micro DIY"],  # Micro
    "multifun_switch": [126, "SONOFF", "DualR3 DIY"],
}


def setup_diy(device: dict) -> XDevice:
    ltype = device["localtype"]
    try:
        uiid, brand, model = DIY[ltype]
        # https://github.com/AlexxIT/SonoffLAN/issues/1136
        # https://github.com/AlexxIT/SonoffLAN/issues/1156
        if ltype == "diy_plug" and "switches" in device["params"]:
            uiid = 77
            model = "MINI R3 DIY"
        device["name"] = model
        device["brandName"] = brand
        device["extra"] = {"uiid": uiid}
        device["productModel"] = model
    except Exception:
        device["name"] = "Unknown DIY"
        device["extra"] = {"uiid": 0}
        device["productModel"] = ltype
    return device
