Skip to content

Commit

Permalink
apply converter for button
Browse files Browse the repository at this point in the history
  • Loading branch information
al-one committed Sep 23, 2024
1 parent 11a04e5 commit 772a74e
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 17 deletions.
2 changes: 1 addition & 1 deletion custom_components/xiaomi_miot/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1260,7 +1260,7 @@ async def async_update_for_main_entity(self):
pls = self.custom_config_list(f'{d}_properties') or []
if pls:
self._update_sub_entities(pls, '*', domain=d)
for d in ['button', 'text', 'select']:
for d in ['text', 'select']:
als = self.custom_config_list(f'{d}_actions') or []
if als:
self._update_sub_entities(None, '*', domain=d, actions=als)
Expand Down
26 changes: 21 additions & 5 deletions custom_components/xiaomi_miot/button.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@

from homeassistant.components.button import (
DOMAIN as ENTITY_DOMAIN,
ButtonEntity,
ButtonEntity as BaseEntity,
)

from . import (
DOMAIN,
CONF_MODEL,
XIAOMI_CONFIG_SCHEMA as PLATFORM_SCHEMA, # noqa: F401
HassEntry,
XEntity,
MiotEntity,
MiotPropertySubEntity,
BaseSubEntity,
Expand Down Expand Up @@ -53,7 +54,22 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
bind_services_to_entries(hass, SERVICE_TO_METHOD)


class MiotButtonEntity(MiotEntity, ButtonEntity):
class ButtonEntity(XEntity, BaseEntity):
def on_init(self):
self._attr_available = True
if des := getattr(self.conv, 'description', None):
self._attr_name = f'{self._attr_name} {des}'

def set_state(self, data: dict):
pass

async def async_press(self):
await self.device.async_write({self.attr: getattr(self.conv, 'value', None)})

XEntity.CLS[ENTITY_DOMAIN] = ButtonEntity


class MiotButtonEntity(MiotEntity, BaseEntity):
def __init__(self, config, miot_service: MiotService):
super().__init__(miot_service, config=config, logger=_LOGGER)

Expand All @@ -62,7 +78,7 @@ def press(self) -> None:
raise NotImplementedError()


class MiotButtonSubEntity(MiotPropertySubEntity, ButtonEntity):
class MiotButtonSubEntity(MiotPropertySubEntity, BaseEntity):
def __init__(self, parent, miot_property: MiotProperty, value, option=None):
super().__init__(parent, miot_property, option, domain=ENTITY_DOMAIN)
self._miot_property_value = value
Expand All @@ -89,7 +105,7 @@ def press(self):
return self.set_parent_property(self._miot_property_value)


class MiotButtonActionSubEntity(BaseSubEntity, ButtonEntity):
class MiotButtonActionSubEntity(BaseSubEntity, BaseEntity):
def __init__(self, parent, miot_action: MiotAction, option=None):
self._miot_action = miot_action
super().__init__(parent, miot_action.full_name, option, domain=ENTITY_DOMAIN)
Expand Down Expand Up @@ -118,7 +134,7 @@ def press(self):
return self.call_parent('call_action', self._miot_action, pms)


class ButtonSubEntity(ButtonEntity, BaseSubEntity):
class ButtonSubEntity(BaseEntity, BaseSubEntity):
def __init__(self, parent, attr, option=None):
BaseSubEntity.__init__(self, parent, attr, option)
self._available = True
Expand Down
36 changes: 33 additions & 3 deletions custom_components/xiaomi_miot/core/converters.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Any
from dataclasses import dataclass
from .miot_spec import MiotSpec

if TYPE_CHECKING:
from .device import Device
from .miot_spec import MiotProperty
from .miot_spec import MiotProperty, MiotAction


@dataclass
Expand Down Expand Up @@ -48,8 +48,38 @@ def decode(self, device: 'Device', payload: dict, value):
value = self.prop.list_description(value)
super().decode(device, payload, value)

# from hass
def encode(self, device: 'Device', payload: dict, value):
if self.desc:
value = self.prop.list_value(value)
super().encode(device, payload, value)

@dataclass
class MiotPropValueConv(MiotPropConv):
value: Any = None
description: str = None

def decode(self, device: 'Device', payload: dict, value):
pass

@dataclass
class MiotActionConv(BaseConv):
action: 'MiotAction' = None

def __post_init__(self):
super().__post_init__()
if not self.mi:
self.mi = MiotSpec.unique_prop(self.action.siid, aiid=self.action.iid)

def decode(self, device: 'Device', payload: dict, value):
super().decode(device, payload, value)

def encode(self, device: 'Device', payload: dict, value):
ins = value if isinstance(value, list) else [] if value is None else [value]
_, s, p = self.mi.split('.')
payload['method'] = 'action'
payload['param'] = {
'did': device.did,
'siid': int(s),
'aiid': int(p),
'in': ins,
}
46 changes: 39 additions & 7 deletions custom_components/xiaomi_miot/core/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from .const import DOMAIN, DEVICE_CUSTOMIZES, DEFAULT_NAME, CONF_CONN_MODE, DEFAULT_CONN_MODE
from .hass_entry import HassEntry
from .converters import BaseConv, MiotPropConv
from .converters import BaseConv, MiotPropConv, MiotPropValueConv, MiotActionConv
from .miot_spec import MiotSpec, MiotResults
from .miio2miot import Miio2MiotHelper
from .xiaomi_cloud import MiotCloud, MiCloudException
Expand Down Expand Up @@ -232,18 +232,39 @@ async def get_urn(self):
return urn

def init_converters(self):
if not self.spec:
return

for d in [
'sensor', 'binary_sensor', 'switch', 'number', 'select',
'fan', 'cover', 'button', 'scanner', 'number_select',
]:
if not self.spec:
break
pls = self.custom_config_list(f'{d}_properties') or []
if not pls:
continue
for prop in self.spec.get_properties(*pls):
desc = prop.value_list and d in ['sensor', 'select']
self.converters.append(MiotPropConv(prop.full_name, d, prop=prop, desc=desc))
if d == 'button':
if prop.value_list:
for pv in prop.value_list:
val = pv.get('value')
des = pv.get('description') or val
attr = f'{prop.full_name}-{val}'
conv = MiotPropValueConv(attr, d, prop=prop, value=val, description=des)
self.converters.append(conv)
elif prop.is_bool:
conv = MiotPropValueConv(prop.full_name, d, prop=prop, value=True)
self.converters.append(conv)
else:
desc = bool(prop.value_list and d in ['sensor', 'select'])
self.converters.append(MiotPropConv(prop.full_name, d, prop=prop, desc=desc))

for d in ['button']:
als = self.custom_config_list(f'{d}_actions') or []
if not als:
continue
for srv in self.spec.services.values():
for action in srv.get_actions(*als):
self.converters.append(MiotActionConv(action.full_name, d, action=action))

def add_entity(self, entity: 'XEntity'):
if entity not in self.entities:
Expand Down Expand Up @@ -306,8 +327,7 @@ async def async_write(self, payload: dict):
for param in params:
siid = param['siid']
piid = param.get('piid')
aiid = param.get('aiid')
if not self.miio2miot.has_setter(siid, piid=piid, aiid=aiid):
if not self.miio2miot.has_setter(siid, piid=piid):
continue
cmd = partial(self.miio2miot.set_property, self.local, siid, piid, param['value'])
result.append(await self.hass.async_add_executor_job(cmd))
Expand All @@ -316,6 +336,18 @@ async def async_write(self, payload: dict):
elif self.cloud:
result = await self.cloud.async_set_props(params)

if method == 'action':
param = data.get('param', {})
siid = param['siid']
aiid = param.get('aiid')
if self.miio2miot and self._local_state and self.miio2miot.has_setter(siid, aiid=aiid):
cmd = partial(self.miio2miot.call_action, self.local, siid, aiid, param.get('in', []))
result = await self.hass.async_add_executor_job(cmd)
elif self.local and self._local_state:
result = await self.local.async_send(method, param)
elif self.cloud:
result = await self.cloud.async_do_action(param)

_LOGGER.info('%s: Device write: %s', self.name_model, [payload, data, result])
if result:
self.dispatch(payload)
Expand Down
3 changes: 3 additions & 0 deletions custom_components/xiaomi_miot/core/device_customizes.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,9 @@
**ENERGY_KWH,
'value_ratio': 0.001,
},
'chunmi.microwave.n23l01': {
'button_actions': 'pause',
},
'chunmi.ysj.*': {
'sensor_properties': 'water_dispenser.status,filter_life_level,home_temp,clean_precent',
'switch_properties': 'winter_mode,cold_keep,cup_check',
Expand Down
12 changes: 11 additions & 1 deletion custom_components/xiaomi_miot/core/hass_entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

from .utils import get_customize_via_entity, wildcard_models
from .miot_spec import MiotService, MiotProperty, MiotAction
from .converters import BaseConv, MiotPropConv
from .converters import BaseConv, MiotPropConv, MiotActionConv

if TYPE_CHECKING:
from .device import Device
Expand Down Expand Up @@ -80,6 +80,7 @@ class XEntity(BasicEntity):
_attr_has_entity_name = True
_miot_service: Optional[MiotService] = None
_miot_property: Optional[MiotProperty] = None
_miot_action: Optional[MiotAction] = None

def __init__(self, device: 'Device', conv: 'BaseConv'):
self.device = device
Expand All @@ -93,6 +94,15 @@ def __init__(self, device: 'Device', conv: 'BaseConv'):
self._attr_translation_key = conv.prop.friendly_name
self._miot_service = conv.prop.service
self._miot_property = conv.prop

elif isinstance(conv, MiotActionConv):
self.entity_id = device.spec.generate_entity_id(self, conv.action.name, conv.domain)
self._attr_name = str(conv.action.friendly_desc)
self._attr_translation_key = conv.action.friendly_name
self._miot_service = conv.action.service
self._miot_action = conv.action
self._miot_property = conv.action.in_properties()[0] if conv.action.ins else None

else:
prefix = device.spec.generate_entity_id(self)
self.entity_id = f'{prefix}_{self.attr}'
Expand Down

0 comments on commit 772a74e

Please sign in to comment.