first commit
This commit is contained in:
@@ -0,0 +1,176 @@
|
||||
import asyncio
|
||||
|
||||
from homeassistant.components.light import *
|
||||
from homeassistant.const import STATE_ON
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.restore_state import RestoreEntity
|
||||
|
||||
from . import DOMAIN
|
||||
from .core.converters import ZIGBEE, MESH_GROUP_MODEL, Converter
|
||||
from .core.device import XDevice
|
||||
from .core.entity import XEntity
|
||||
from .core.gateway import XGateway
|
||||
|
||||
CONF_DEFAULT_TRANSITION = 'default_transition'
|
||||
|
||||
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
def setup(gateway: XGateway, device: XDevice, conv: Converter):
|
||||
if conv.attr in device.entities:
|
||||
entity: XEntity = device.entities[conv.attr]
|
||||
entity.gw = gateway
|
||||
elif device.type == ZIGBEE:
|
||||
entity = XiaomiZigbeeLight(gateway, device, conv)
|
||||
elif device.model == MESH_GROUP_MODEL:
|
||||
entity = XiaomiMeshGroup(gateway, device, conv)
|
||||
else:
|
||||
entity = XiaomiMeshLight(gateway, device, conv)
|
||||
async_add_entities([entity])
|
||||
|
||||
gw: XGateway = hass.data[DOMAIN][config_entry.entry_id]
|
||||
gw.add_setup(__name__, setup)
|
||||
|
||||
|
||||
# noinspection PyAbstractClass
|
||||
class XiaomiLight(XEntity, LightEntity, RestoreEntity):
|
||||
_attr_is_on = None
|
||||
|
||||
def __init__(self, gateway: 'XGateway', device: XDevice, conv: Converter):
|
||||
super().__init__(gateway, device, conv)
|
||||
|
||||
for conv in device.converters:
|
||||
if conv.attr == ATTR_BRIGHTNESS:
|
||||
self._attr_supported_features |= (
|
||||
SUPPORT_BRIGHTNESS | SUPPORT_TRANSITION
|
||||
)
|
||||
elif conv.attr == ATTR_COLOR_TEMP:
|
||||
self._attr_supported_features |= SUPPORT_COLOR_TEMP
|
||||
if hasattr(conv, "minm") and hasattr(conv, "maxm"):
|
||||
self._attr_min_mireds = conv.minm
|
||||
self._attr_max_mireds = conv.maxm
|
||||
elif hasattr(conv, "mink") and hasattr(conv, "maxk"):
|
||||
self._attr_min_mireds = int(1000000 / conv.maxk)
|
||||
self._attr_max_mireds = int(1000000 / conv.mink)
|
||||
|
||||
@callback
|
||||
def async_set_state(self, data: dict):
|
||||
if self.attr in data:
|
||||
self._attr_is_on = data[self.attr]
|
||||
# sometimes brightness and color_temp stored as string in Xiaomi DB
|
||||
if ATTR_BRIGHTNESS in data:
|
||||
self._attr_brightness = data[ATTR_BRIGHTNESS]
|
||||
if ATTR_COLOR_TEMP in data:
|
||||
self._attr_color_temp = data[ATTR_COLOR_TEMP]
|
||||
|
||||
@callback
|
||||
def async_restore_last_state(self, state: str, attrs: dict):
|
||||
self._attr_is_on = state == STATE_ON
|
||||
self._attr_brightness = attrs.get(ATTR_BRIGHTNESS)
|
||||
self._attr_color_temp = attrs.get(ATTR_COLOR_TEMP)
|
||||
|
||||
async def async_update(self):
|
||||
await self.device_read(self.subscribed_attrs)
|
||||
|
||||
|
||||
# noinspection PyAbstractClass
|
||||
class XiaomiZigbeeLight(XiaomiLight):
|
||||
async def async_turn_on(self, **kwargs):
|
||||
if ATTR_TRANSITION in kwargs:
|
||||
tr = kwargs.pop(ATTR_TRANSITION)
|
||||
elif CONF_DEFAULT_TRANSITION in self.customize:
|
||||
tr = self.customize[CONF_DEFAULT_TRANSITION]
|
||||
else:
|
||||
tr = None
|
||||
|
||||
if tr is not None:
|
||||
if kwargs:
|
||||
# For the Aqara bulb, it is important that the brightness
|
||||
# parameter comes before the color_temp parameter. Only this
|
||||
# way transition will work. So we use `kwargs.pop` func to set
|
||||
# the exact order of parameters.
|
||||
for k in (ATTR_BRIGHTNESS, ATTR_COLOR_TEMP):
|
||||
if k in kwargs:
|
||||
kwargs[k] = (kwargs.pop(k), tr)
|
||||
else:
|
||||
kwargs[ATTR_BRIGHTNESS] = (255, tr)
|
||||
|
||||
if not kwargs:
|
||||
kwargs[self.attr] = True
|
||||
|
||||
await self.device_send(kwargs)
|
||||
|
||||
async def async_turn_off(self, **kwargs):
|
||||
if ATTR_TRANSITION in kwargs:
|
||||
tr = kwargs[ATTR_TRANSITION]
|
||||
elif CONF_DEFAULT_TRANSITION in self.customize:
|
||||
tr = self.customize[CONF_DEFAULT_TRANSITION]
|
||||
else:
|
||||
tr = None
|
||||
|
||||
if tr is not None:
|
||||
await self.device_send({ATTR_BRIGHTNESS: (0, tr)})
|
||||
else:
|
||||
await self.device_send({self.attr: False})
|
||||
|
||||
|
||||
# noinspection PyAbstractClass
|
||||
class XiaomiMeshBase(XiaomiLight):
|
||||
async def async_turn_on(self, **kwargs):
|
||||
kwargs[self.attr] = True
|
||||
await self.device_send(kwargs)
|
||||
|
||||
async def async_turn_off(self, **kwargs):
|
||||
kwargs[self.attr] = False
|
||||
await self.device_send(kwargs)
|
||||
|
||||
|
||||
# noinspection PyAbstractClass
|
||||
class XiaomiMeshLight(XiaomiMeshBase):
|
||||
@callback
|
||||
def async_set_state(self, data: dict):
|
||||
super().async_set_state(data)
|
||||
|
||||
if "group" not in self.device.entities:
|
||||
return
|
||||
# convert light attr to group attr
|
||||
if self.attr in data:
|
||||
data["group"] = data.pop(self.attr)
|
||||
group = self.device.entities["group"]
|
||||
group.async_set_state(data)
|
||||
group.async_write_ha_state()
|
||||
|
||||
|
||||
# noinspection PyAbstractClass
|
||||
class XiaomiMeshGroup(XiaomiMeshBase):
|
||||
def __init__(self, gateway: 'XGateway', device: XDevice, conv: Converter):
|
||||
super().__init__(gateway, device, conv)
|
||||
|
||||
if not device.extra["childs"]:
|
||||
device.available = False
|
||||
return
|
||||
|
||||
for did in device.extra["childs"]:
|
||||
child = gateway.devices[did]
|
||||
child.entities[self.attr] = self
|
||||
|
||||
async def async_will_remove_from_hass(self) -> None:
|
||||
await super().async_will_remove_from_hass()
|
||||
if not self.device.extra["childs"]:
|
||||
return
|
||||
for did in self.device.extra["childs"]:
|
||||
child = self.gw.devices[did]
|
||||
child.entities.pop(self.attr)
|
||||
|
||||
async def async_update(self):
|
||||
# To update a group - request an update of its children
|
||||
# update_ha_state for all child light entities
|
||||
try:
|
||||
childs = []
|
||||
for did in self.device.extra["childs"]:
|
||||
light = self.gw.devices[did].entities.get("light")
|
||||
childs.append(light.async_update_ha_state(True))
|
||||
if childs:
|
||||
await asyncio.gather(*childs)
|
||||
|
||||
except Exception as e:
|
||||
self.debug("Can't update child states", exc_info=e)
|
||||
Reference in New Issue
Block a user