mirror of
https://github.com/XiaoMi/ha_xiaomi_home.git
synced 2026-01-12 03:40:43 +08:00
Merge f5bc880c62 into 001af5384a
This commit is contained in:
commit
7142283370
@ -47,11 +47,12 @@ Light entities for Xiaomi Home.
|
|||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
import logging
|
import logging
|
||||||
from typing import Any, Optional
|
from typing import Any, Optional, List, Dict
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
from homeassistant.helpers.entity_registry import async_get as async_get_entity_registry
|
||||||
from homeassistant.components.light import (
|
from homeassistant.components.light import (
|
||||||
ATTR_BRIGHTNESS,
|
ATTR_BRIGHTNESS,
|
||||||
ATTR_COLOR_TEMP_KELVIN,
|
ATTR_COLOR_TEMP_KELVIN,
|
||||||
@ -86,7 +87,7 @@ async def async_setup_entry(
|
|||||||
for miot_device in device_list:
|
for miot_device in device_list:
|
||||||
for data in miot_device.entity_list.get('light', []):
|
for data in miot_device.entity_list.get('light', []):
|
||||||
new_entities.append(
|
new_entities.append(
|
||||||
Light(miot_device=miot_device, entity_data=data))
|
Light(miot_device=miot_device, entity_data=data, hass=hass))
|
||||||
|
|
||||||
if new_entities:
|
if new_entities:
|
||||||
async_add_entities(new_entities)
|
async_add_entities(new_entities)
|
||||||
@ -106,10 +107,13 @@ class Light(MIoTServiceEntity, LightEntity):
|
|||||||
_mode_map: Optional[dict[Any, Any]]
|
_mode_map: Optional[dict[Any, Any]]
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, miot_device: MIoTDevice, entity_data: MIoTEntityData
|
self, miot_device: MIoTDevice, entity_data: MIoTEntityData,hass: HomeAssistant
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize the Light."""
|
"""Initialize the Light."""
|
||||||
super().__init__(miot_device=miot_device, entity_data=entity_data)
|
super().__init__(miot_device=miot_device, entity_data=entity_data)
|
||||||
|
self.hass = hass
|
||||||
|
self.miot_device = miot_device
|
||||||
|
self._command_send_mode_entity_id = None
|
||||||
self._attr_color_mode = None
|
self._attr_color_mode = None
|
||||||
self._attr_supported_color_modes = set()
|
self._attr_supported_color_modes = set()
|
||||||
self._attr_supported_features = LightEntityFeature(0)
|
self._attr_supported_features = LightEntityFeature(0)
|
||||||
@ -252,6 +256,142 @@ class Light(MIoTServiceEntity, LightEntity):
|
|||||||
"""
|
"""
|
||||||
# on
|
# on
|
||||||
# Dirty logic for lumi.gateway.mgl03 indicator light
|
# Dirty logic for lumi.gateway.mgl03 indicator light
|
||||||
|
# Determine whether the device sends the light-on properties in batches or one by one
|
||||||
|
# Search entityid through unique_id to avoid the user modifying entityid and causing command_send_mode to not match
|
||||||
|
# 获取开灯模式
|
||||||
|
if self._command_send_mode_entity_id is None:
|
||||||
|
entity_registry = async_get_entity_registry(self.hass)
|
||||||
|
device_id = list(
|
||||||
|
self.miot_device.device_info.get("identifiers"))[0][1]
|
||||||
|
self._command_send_mode_entity_id = entity_registry.async_get_entity_id(
|
||||||
|
"select", DOMAIN, f"select.light_{device_id}_command_send_mode")
|
||||||
|
if self._command_send_mode_entity_id is None:
|
||||||
|
_LOGGER.error(
|
||||||
|
"light command_send_mode not found, %s",
|
||||||
|
self.entity_id,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
command_send_mode = self.hass.states.get(
|
||||||
|
self._command_send_mode_entity_id)
|
||||||
|
|
||||||
|
# 判断是先发送亮度还是先发送色温
|
||||||
|
send_brightness_first = False
|
||||||
|
if ATTR_BRIGHTNESS in kwargs:
|
||||||
|
brightness_new = kwargs[ATTR_BRIGHTNESS]
|
||||||
|
brightness_old = self.brightness
|
||||||
|
if brightness_old and brightness_new <= brightness_old:
|
||||||
|
send_brightness_first = True
|
||||||
|
|
||||||
|
# 开始发送开灯命令
|
||||||
|
if command_send_mode and command_send_mode.state == "Send Together":
|
||||||
|
set_properties_list: List[Dict[str, Any]] = []
|
||||||
|
# mode
|
||||||
|
if ATTR_EFFECT in kwargs:
|
||||||
|
set_properties_list.append({
|
||||||
|
"prop":self._prop_mode,
|
||||||
|
"value":self.get_map_key(
|
||||||
|
map_=self._mode_map,value=kwargs[ATTR_EFFECT]),
|
||||||
|
})
|
||||||
|
# brightness
|
||||||
|
if send_brightness_first and ATTR_BRIGHTNESS in kwargs:
|
||||||
|
brightness = brightness_to_value(
|
||||||
|
self._brightness_scale,kwargs[ATTR_BRIGHTNESS])
|
||||||
|
set_properties_list.append({
|
||||||
|
"prop": self._prop_brightness,
|
||||||
|
"value": brightness
|
||||||
|
})
|
||||||
|
# color-temperature
|
||||||
|
if ATTR_COLOR_TEMP_KELVIN in kwargs:
|
||||||
|
set_properties_list.append({
|
||||||
|
"prop": self._prop_color_temp,
|
||||||
|
"value": kwargs[ATTR_COLOR_TEMP_KELVIN],
|
||||||
|
})
|
||||||
|
self._attr_color_mode = ColorMode.COLOR_TEMP
|
||||||
|
# rgb color
|
||||||
|
if ATTR_RGB_COLOR in kwargs:
|
||||||
|
r = kwargs[ATTR_RGB_COLOR][0]
|
||||||
|
g = kwargs[ATTR_RGB_COLOR][1]
|
||||||
|
b = kwargs[ATTR_RGB_COLOR][2]
|
||||||
|
rgb = (r << 16) | (g << 8) | b
|
||||||
|
set_properties_list.append({
|
||||||
|
"prop": self._prop_color,
|
||||||
|
"value": rgb
|
||||||
|
})
|
||||||
|
self._attr_color_mode = ColorMode.RGB
|
||||||
|
# brightness
|
||||||
|
if not send_brightness_first and ATTR_BRIGHTNESS in kwargs:
|
||||||
|
brightness = brightness_to_value(
|
||||||
|
self._brightness_scale,kwargs[ATTR_BRIGHTNESS])
|
||||||
|
set_properties_list.append({
|
||||||
|
"prop": self._prop_brightness,
|
||||||
|
"value": brightness
|
||||||
|
})
|
||||||
|
|
||||||
|
if self._prop_on:
|
||||||
|
value_on = True if self._prop_on.format_ == bool else 1
|
||||||
|
set_properties_list.append({
|
||||||
|
"prop": self._prop_on,
|
||||||
|
"value": value_on
|
||||||
|
})
|
||||||
|
await self.set_properties_async(set_properties_list,write_ha_state=False)
|
||||||
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
elif command_send_mode and command_send_mode.state == "Send Turn On First":
|
||||||
|
set_properties_list: List[Dict[str, Any]] = []
|
||||||
|
if self._prop_on:
|
||||||
|
value_on = True if self._prop_on.format_ == bool else 1
|
||||||
|
set_properties_list.append({
|
||||||
|
"prop": self._prop_on,
|
||||||
|
"value": value_on
|
||||||
|
})
|
||||||
|
# mode
|
||||||
|
if ATTR_EFFECT in kwargs:
|
||||||
|
set_properties_list.append({
|
||||||
|
"prop":
|
||||||
|
self._prop_mode,
|
||||||
|
"value":
|
||||||
|
self.get_map_key(
|
||||||
|
map_=self._mode_map,value=kwargs[ATTR_EFFECT]),
|
||||||
|
})
|
||||||
|
# brightness
|
||||||
|
if send_brightness_first and ATTR_BRIGHTNESS in kwargs:
|
||||||
|
brightness = brightness_to_value(
|
||||||
|
self._brightness_scale,kwargs[ATTR_BRIGHTNESS])
|
||||||
|
set_properties_list.append({
|
||||||
|
"prop": self._prop_brightness,
|
||||||
|
"value": brightness
|
||||||
|
})
|
||||||
|
# color-temperature
|
||||||
|
if ATTR_COLOR_TEMP_KELVIN in kwargs:
|
||||||
|
set_properties_list.append({
|
||||||
|
"prop": self._prop_color_temp,
|
||||||
|
"value": kwargs[ATTR_COLOR_TEMP_KELVIN],
|
||||||
|
})
|
||||||
|
self._attr_color_mode = ColorMode.COLOR_TEMP
|
||||||
|
# rgb color
|
||||||
|
if ATTR_RGB_COLOR in kwargs:
|
||||||
|
r = kwargs[ATTR_RGB_COLOR][0]
|
||||||
|
g = kwargs[ATTR_RGB_COLOR][1]
|
||||||
|
b = kwargs[ATTR_RGB_COLOR][2]
|
||||||
|
rgb = (r << 16) | (g << 8) | b
|
||||||
|
set_properties_list.append({
|
||||||
|
"prop": self._prop_color,
|
||||||
|
"value": rgb
|
||||||
|
})
|
||||||
|
self._attr_color_mode = ColorMode.RGB
|
||||||
|
# brightness
|
||||||
|
if not send_brightness_first and ATTR_BRIGHTNESS in kwargs:
|
||||||
|
brightness = brightness_to_value(
|
||||||
|
self._brightness_scale,kwargs[ATTR_BRIGHTNESS])
|
||||||
|
set_properties_list.append({
|
||||||
|
"prop": self._prop_brightness,
|
||||||
|
"value": brightness
|
||||||
|
})
|
||||||
|
|
||||||
|
await self.set_properties_async(set_properties_list,write_ha_state=False)
|
||||||
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
else:
|
||||||
if self._prop_on:
|
if self._prop_on:
|
||||||
value_on = True if self._prop_on.format_ == bool else 1
|
value_on = True if self._prop_on.format_ == bool else 1
|
||||||
await self.set_property_async(
|
await self.set_property_async(
|
||||||
|
|||||||
@ -46,7 +46,7 @@ off Xiaomi or its affiliates' products.
|
|||||||
MIoT client instance.
|
MIoT client instance.
|
||||||
"""
|
"""
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from typing import Any, Callable, Optional, final
|
from typing import Any, Callable, Optional, final, Dict, List
|
||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
@ -710,6 +710,84 @@ class MIoTClient:
|
|||||||
f'{self._i18n.translate("miot.client.device_exec_error")}, '
|
f'{self._i18n.translate("miot.client.device_exec_error")}, '
|
||||||
f'{self._i18n.translate("error.common.-10007")}')
|
f'{self._i18n.translate("error.common.-10007")}')
|
||||||
|
|
||||||
|
async def set_props_async(
|
||||||
|
self, props_list: List[Dict[str, Any]],
|
||||||
|
) -> bool:
|
||||||
|
# props_list = [{'did': str, 'siid': int, 'piid': int, 'value': Any}......]
|
||||||
|
# 判断是不是只有一个did
|
||||||
|
did_set = {prop["did"] for prop in props_list}
|
||||||
|
if len(did_set) != 1:
|
||||||
|
raise MIoTClientError(f"more than one or no did once, {did_set}")
|
||||||
|
did = did_set.pop()
|
||||||
|
|
||||||
|
if did not in self._device_list_cache:
|
||||||
|
raise MIoTClientError(f"did not exist, {did}")
|
||||||
|
# Priority local control
|
||||||
|
if self._ctrl_mode == CtrlMode.AUTO:
|
||||||
|
# Gateway control
|
||||||
|
device_gw = self._device_list_gateway.get(did, None)
|
||||||
|
if (
|
||||||
|
device_gw and device_gw.get("online", False)
|
||||||
|
and device_gw.get("specv2_access", False) and "group_id" in device_gw
|
||||||
|
):
|
||||||
|
mips = self._mips_local.get(device_gw["group_id"], None)
|
||||||
|
if mips is None:
|
||||||
|
_LOGGER.error(
|
||||||
|
"no gateway route, %s, try control through cloud",
|
||||||
|
device_gw)
|
||||||
|
else:
|
||||||
|
result = await mips.set_props_async(
|
||||||
|
did=did,props_list=props_list)
|
||||||
|
_LOGGER.debug("gateway set props, %s -> %s", props_list, result)
|
||||||
|
rc = [(r or {}).get("code",
|
||||||
|
MIoTErrorCode.CODE_MIPS_INVALID_RESULT.value)
|
||||||
|
for r in result]
|
||||||
|
if all(t in [0, 1] for t in rc):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
raise MIoTClientError(
|
||||||
|
self.__get_exec_error_with_rc(rc=next(x for x in rc if x not in (0, 1))))
|
||||||
|
# Lan control
|
||||||
|
device_lan = self._device_list_lan.get(did, None)
|
||||||
|
if device_lan and device_lan.get("online", False):
|
||||||
|
result = await self._miot_lan.set_props_async(
|
||||||
|
did=did, props_list=props_list)
|
||||||
|
_LOGGER.debug("lan set props, %s -> %s", props_list, result)
|
||||||
|
rc = [(r or {}).get("code",
|
||||||
|
MIoTErrorCode.CODE_MIPS_INVALID_RESULT.value)
|
||||||
|
for r in result]
|
||||||
|
if all(t in [0, 1] for t in rc):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
raise MIoTClientError(
|
||||||
|
self.__get_exec_error_with_rc(rc=next(x for x in rc if x not in (0, 1))))
|
||||||
|
# Cloud control
|
||||||
|
device_cloud = self._device_list_cloud.get(did, None)
|
||||||
|
if device_cloud and device_cloud.get("online", False):
|
||||||
|
result = await self._http.set_props_async(params=props_list)
|
||||||
|
_LOGGER.debug(
|
||||||
|
"cloud set props, %s, result, %s",
|
||||||
|
props_list, result)
|
||||||
|
if result and len(result) == len(props_list):
|
||||||
|
rc = [(r or {}).get("code",
|
||||||
|
MIoTErrorCode.CODE_MIPS_INVALID_RESULT.value)
|
||||||
|
for r in result]
|
||||||
|
if all(t in [0, 1] for t in rc):
|
||||||
|
return True
|
||||||
|
if any(t in [-704010000, -704042011] for t in rc):
|
||||||
|
# Device remove or offline
|
||||||
|
_LOGGER.error("device may be removed or offline, %s", did)
|
||||||
|
self._main_loop.create_task(
|
||||||
|
await
|
||||||
|
self.__refresh_cloud_device_with_dids_async(dids=[did]))
|
||||||
|
raise MIoTClientError(
|
||||||
|
self.__get_exec_error_with_rc(rc=next(x for x in rc if x not in (0, 1))))
|
||||||
|
|
||||||
|
# Show error message
|
||||||
|
raise MIoTClientError(
|
||||||
|
f'{self._i18n.translate("miot.client.device_exec_error")}, '
|
||||||
|
f'{self._i18n.translate("error.common.-10007")}')
|
||||||
|
|
||||||
def request_refresh_prop(
|
def request_refresh_prop(
|
||||||
self, did: str, siid: int, piid: int
|
self, did: str, siid: int, piid: int
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|||||||
@ -825,6 +825,22 @@ class MIoTHttpClient:
|
|||||||
|
|
||||||
return res_obj['result']
|
return res_obj['result']
|
||||||
|
|
||||||
|
async def set_props_async(self, params: list) -> list:
|
||||||
|
"""
|
||||||
|
params = [{"did": "xxxx", "siid": 2, "piid": 1, "value": False}]
|
||||||
|
"""
|
||||||
|
res_obj = await self.__mihome_api_post_async(
|
||||||
|
url_path='/app/v2/miotspec/prop/set',
|
||||||
|
data={
|
||||||
|
'params': params
|
||||||
|
},
|
||||||
|
timeout=15
|
||||||
|
)
|
||||||
|
if 'result' not in res_obj:
|
||||||
|
raise MIoTHttpError('invalid response result')
|
||||||
|
|
||||||
|
return res_obj['result']
|
||||||
|
|
||||||
async def action_async(
|
async def action_async(
|
||||||
self, did: str, siid: int, aiid: int, in_list: list[dict]
|
self, did: str, siid: int, aiid: int, in_list: list[dict]
|
||||||
) -> dict:
|
) -> dict:
|
||||||
|
|||||||
@ -47,7 +47,7 @@ MIoT device instance.
|
|||||||
"""
|
"""
|
||||||
import asyncio
|
import asyncio
|
||||||
from abc import abstractmethod
|
from abc import abstractmethod
|
||||||
from typing import Any, Callable, Optional
|
from typing import Any, Callable, Optional, Dict, List
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
@ -1090,6 +1090,49 @@ class MIoTServiceEntity(Entity):
|
|||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
async def set_properties_async(
|
||||||
|
self, set_properties_list: List[Dict[str, Any]],
|
||||||
|
update_value: bool = True, write_ha_state: bool = True,
|
||||||
|
) -> bool:
|
||||||
|
# set_properties_list = [{'prop': Optional[MIoTSpecProperty],
|
||||||
|
# 'value': Any}....]
|
||||||
|
for set_property in set_properties_list:
|
||||||
|
prop = set_property.get("prop")
|
||||||
|
value = set_property.get("value")
|
||||||
|
if not prop:
|
||||||
|
raise RuntimeError(
|
||||||
|
f'set property failed, property is None, '
|
||||||
|
f'{self.entity_id}, {self.name}')
|
||||||
|
value = prop.value_format(value)
|
||||||
|
value = prop.value_precision(value)
|
||||||
|
# 因为下面还有判断在这个循环里 所以这里要赋值回去
|
||||||
|
set_property["value"] = value
|
||||||
|
if prop not in self.entity_data.props:
|
||||||
|
raise RuntimeError(
|
||||||
|
f'set property failed, unknown property, '
|
||||||
|
f'{self.entity_id}, {self.name}, {prop.name}')
|
||||||
|
if not prop.writable:
|
||||||
|
raise RuntimeError(
|
||||||
|
f'set property failed, not writable, '
|
||||||
|
f'{self.entity_id}, {self.name}, {prop.name}')
|
||||||
|
try:
|
||||||
|
await self.miot_device.miot_client.set_props_async([{
|
||||||
|
"did": self.miot_device.did,
|
||||||
|
"siid": set_property["prop"].service.iid,
|
||||||
|
"piid": set_property["prop"].iid,
|
||||||
|
"value": set_property["value"],
|
||||||
|
} for set_property in set_properties_list])
|
||||||
|
except MIoTClientError as e:
|
||||||
|
raise RuntimeError(
|
||||||
|
f"{e}, {self.entity_id}, {self.name}, {'&'.join([set_property['prop'].name for set_property in set_properties_list])}") from e
|
||||||
|
if update_value:
|
||||||
|
for set_property in set_properties_list:
|
||||||
|
self._prop_value_map[
|
||||||
|
set_property["prop"]] = set_property["value"]
|
||||||
|
if write_ha_state:
|
||||||
|
self.async_write_ha_state()
|
||||||
|
return True
|
||||||
|
|
||||||
async def get_property_async(self, prop: MIoTSpecProperty) -> Any:
|
async def get_property_async(self, prop: MIoTSpecProperty) -> Any:
|
||||||
if not prop:
|
if not prop:
|
||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
|
|||||||
@ -58,7 +58,7 @@ import secrets
|
|||||||
import socket
|
import socket
|
||||||
import struct
|
import struct
|
||||||
import threading
|
import threading
|
||||||
from typing import Any, Callable, Coroutine, Optional, final
|
from typing import Any, Callable, Coroutine, Optional, final, Dict, List
|
||||||
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
||||||
from cryptography.hazmat.primitives import padding
|
from cryptography.hazmat.primitives import padding
|
||||||
from cryptography.hazmat.backends import default_backend
|
from cryptography.hazmat.backends import default_backend
|
||||||
@ -857,6 +857,29 @@ class MIoTLan:
|
|||||||
return result_obj
|
return result_obj
|
||||||
raise MIoTError('Invalid result', MIoTErrorCode.CODE_INTERNAL_ERROR)
|
raise MIoTError('Invalid result', MIoTErrorCode.CODE_INTERNAL_ERROR)
|
||||||
|
|
||||||
|
@final
|
||||||
|
async def set_props_async(
|
||||||
|
self,did: str,props_list: List[Dict[str, Any]],
|
||||||
|
timeout_ms: int = 10000) -> dict:
|
||||||
|
# props_list = [{'did': did, 'siid': siid, 'piid': piid, 'value': value}......]
|
||||||
|
self.__assert_service_ready()
|
||||||
|
result_obj = await self.__call_api_async(
|
||||||
|
did=did, msg={
|
||||||
|
'method': 'set_properties',
|
||||||
|
'params': props_list,
|
||||||
|
}, timeout_ms=timeout_ms)
|
||||||
|
if result_obj:
|
||||||
|
if (
|
||||||
|
'result' in result_obj and
|
||||||
|
len(result_obj['result']) == len(props_list)
|
||||||
|
and result_obj['result'][0].get('did') == did
|
||||||
|
and all('code' in item for item in result_obj['result'])
|
||||||
|
):
|
||||||
|
return result_obj['result']
|
||||||
|
if 'code' in result_obj:
|
||||||
|
return result_obj
|
||||||
|
raise MIoTError('Invalid result', MIoTErrorCode.CODE_INTERNAL_ERROR)
|
||||||
|
|
||||||
@final
|
@final
|
||||||
async def action_async(
|
async def action_async(
|
||||||
self, did: str, siid: int, aiid: int, in_list: list,
|
self, did: str, siid: int, aiid: int, in_list: list,
|
||||||
|
|||||||
@ -56,7 +56,7 @@ import threading
|
|||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from enum import Enum, auto
|
from enum import Enum, auto
|
||||||
from typing import Any, Callable, Optional, final, Coroutine
|
from typing import Any, Callable, Optional, final, Coroutine, Dict, List
|
||||||
|
|
||||||
from paho.mqtt.client import (
|
from paho.mqtt.client import (
|
||||||
MQTT_ERR_SUCCESS,
|
MQTT_ERR_SUCCESS,
|
||||||
@ -1342,6 +1342,41 @@ class MipsLocalClient(_MipsClient):
|
|||||||
'code': MIoTErrorCode.CODE_INTERNAL_ERROR.value,
|
'code': MIoTErrorCode.CODE_INTERNAL_ERROR.value,
|
||||||
'message': 'Invalid result'}
|
'message': 'Invalid result'}
|
||||||
|
|
||||||
|
@final
|
||||||
|
async def set_props_async(
|
||||||
|
self, did: str, props_list: List[Dict[str, Any]],
|
||||||
|
timeout_ms: int = 10000
|
||||||
|
) -> dict:
|
||||||
|
# props_list= [{
|
||||||
|
# 'did': did,
|
||||||
|
# 'siid': siid,
|
||||||
|
# 'piid': piid,
|
||||||
|
# 'value': value
|
||||||
|
# }]
|
||||||
|
payload_obj: dict = {
|
||||||
|
"did": did,
|
||||||
|
"rpc": {
|
||||||
|
"id": self.__gen_mips_id,
|
||||||
|
"method": "set_properties",
|
||||||
|
"params": props_list,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result_obj = await self.__request_async(
|
||||||
|
topic="proxy/rpcReq",
|
||||||
|
payload=json.dumps(payload_obj),
|
||||||
|
timeout_ms=timeout_ms)
|
||||||
|
if result_obj:
|
||||||
|
if ("result" in result_obj and
|
||||||
|
len(result_obj["result"]) == len(props_list) and
|
||||||
|
result_obj["result"][0].get("did") == did and
|
||||||
|
all("code" in item for item in result_obj["result"])):
|
||||||
|
return result_obj["result"]
|
||||||
|
if "error" in result_obj:
|
||||||
|
return result_obj["error"]
|
||||||
|
return {
|
||||||
|
'code': MIoTErrorCode.CODE_INTERNAL_ERROR.value,
|
||||||
|
'message': 'Invalid result'}
|
||||||
|
|
||||||
@final
|
@final
|
||||||
async def action_async(
|
async def action_async(
|
||||||
self, did: str, siid: int, aiid: int, in_list: list,
|
self, did: str, siid: int, aiid: int, in_list: list,
|
||||||
|
|||||||
@ -52,6 +52,8 @@ from homeassistant.config_entries import ConfigEntry
|
|||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.components.select import SelectEntity
|
from homeassistant.components.select import SelectEntity
|
||||||
|
from homeassistant.helpers.entity import EntityCategory
|
||||||
|
from homeassistant.helpers.restore_state import RestoreEntity
|
||||||
|
|
||||||
from .miot.const import DOMAIN
|
from .miot.const import DOMAIN
|
||||||
from .miot.miot_device import MIoTDevice, MIoTPropertyEntity
|
from .miot.miot_device import MIoTDevice, MIoTPropertyEntity
|
||||||
@ -75,6 +77,17 @@ async def async_setup_entry(
|
|||||||
if new_entities:
|
if new_entities:
|
||||||
async_add_entities(new_entities)
|
async_add_entities(new_entities)
|
||||||
|
|
||||||
|
# create select for light
|
||||||
|
new_light_select_entities = []
|
||||||
|
for miot_device in device_list:
|
||||||
|
# Add it to all devices with light entities, because some bathroom heaters and clothes drying racks also have lights.
|
||||||
|
# if "device:light" in miot_device.spec_instance.urn:
|
||||||
|
if miot_device.entity_list.get("light", []):
|
||||||
|
device_id = list(miot_device.device_info.get("identifiers"))[0][1]
|
||||||
|
new_light_select_entities.append(
|
||||||
|
LightCommandSendMode(hass=hass, device_id=device_id))
|
||||||
|
if new_light_select_entities:
|
||||||
|
async_add_entities(new_light_select_entities)
|
||||||
|
|
||||||
class Select(MIoTPropertyEntity, SelectEntity):
|
class Select(MIoTPropertyEntity, SelectEntity):
|
||||||
"""Select entities for Xiaomi Home."""
|
"""Select entities for Xiaomi Home."""
|
||||||
@ -94,3 +107,38 @@ class Select(MIoTPropertyEntity, SelectEntity):
|
|||||||
def current_option(self) -> Optional[str]:
|
def current_option(self) -> Optional[str]:
|
||||||
"""Return the current selected option."""
|
"""Return the current selected option."""
|
||||||
return self.get_vlist_description(value=self._value)
|
return self.get_vlist_description(value=self._value)
|
||||||
|
|
||||||
|
|
||||||
|
class LightCommandSendMode(SelectEntity, RestoreEntity):
|
||||||
|
"""To control whether to turn on the light, you need to send the light-on command first and
|
||||||
|
then send other color temperatures and brightness or send them all at the same time.
|
||||||
|
The default is to send one by one."""
|
||||||
|
|
||||||
|
def __init__(self, hass: HomeAssistant, device_id: str):
|
||||||
|
super().__init__()
|
||||||
|
self.hass = hass
|
||||||
|
self._device_id = device_id
|
||||||
|
self._attr_name = "Command Send Mode"
|
||||||
|
self.entity_id = f"select.light_{device_id}_command_send_mode"
|
||||||
|
self._attr_unique_id = self.entity_id
|
||||||
|
self._attr_options = [
|
||||||
|
"Send One by One", "Send Turn On First", "Send Together"
|
||||||
|
]
|
||||||
|
self._attr_device_info = {"identifiers": {(DOMAIN, device_id)}}
|
||||||
|
self._attr_current_option = self._attr_options[0]
|
||||||
|
self._attr_entity_category = EntityCategory.CONFIG
|
||||||
|
|
||||||
|
async def async_select_option(self, option: str):
|
||||||
|
if option in self._attr_options:
|
||||||
|
self._attr_current_option = option
|
||||||
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
async def async_added_to_hass(self):
|
||||||
|
await super().async_added_to_hass()
|
||||||
|
if (last_state := await self.async_get_last_state()
|
||||||
|
) and last_state.state in self._attr_options:
|
||||||
|
self._attr_current_option = last_state.state
|
||||||
|
|
||||||
|
@property
|
||||||
|
def current_option(self):
|
||||||
|
return self._attr_current_option
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user