Initial Home Assistant Configuration
This commit is contained in:
@@ -0,0 +1,181 @@
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from typing import Optional, Dict
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
from meross_iot.controller.device import BaseDevice
|
||||
from meross_iot.controller.mixins.consumption import ConsumptionXMixin
|
||||
from meross_iot.controller.mixins.electricity import ElectricityMixin
|
||||
from meross_iot.controller.mixins.garage import GarageOpenerMixin
|
||||
from meross_iot.controller.mixins.light import LightMixin
|
||||
from meross_iot.controller.mixins.dnd import SystemDndMixin
|
||||
from meross_iot.controller.mixins.toggle import ToggleXMixin, ToggleMixin
|
||||
from meross_iot.manager import MerossManager
|
||||
from meross_iot.model.http.device import HttpDeviceInfo
|
||||
from meross_iot.model.enums import DNDMode
|
||||
|
||||
# Conditional import for switch device
|
||||
from homeassistant.components.switch import SwitchEntity
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||
from . import MerossDevice
|
||||
from .common import (DOMAIN, MANAGER, DEVICE_LIST_COORDINATOR, HA_SWITCH)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class MerossSwitchDevice(ToggleXMixin, BaseDevice):
|
||||
"""
|
||||
Type hints helper
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class MerossDndDevice(SystemDndMixin, BaseDevice):
|
||||
"""
|
||||
Type hints helper
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class SwitchEntityWrapper(MerossDevice, SwitchEntity):
|
||||
"""Wrapper class to adapt the Meross switches into the Homeassistant platform"""
|
||||
_device: MerossSwitchDevice
|
||||
|
||||
def __init__(self,
|
||||
channel: int,
|
||||
device: MerossSwitchDevice,
|
||||
device_list_coordinator: DataUpdateCoordinator[Dict[str, HttpDeviceInfo]]):
|
||||
super().__init__(
|
||||
device=device,
|
||||
channel=channel,
|
||||
device_list_coordinator=device_list_coordinator,
|
||||
platform=HA_SWITCH)
|
||||
|
||||
# Device properties
|
||||
self._last_power_sample = None
|
||||
self._daily_consumption = None
|
||||
|
||||
async def async_update(self):
|
||||
if self.online:
|
||||
await super().async_update()
|
||||
|
||||
# If the device supports power reading, update it
|
||||
if isinstance(self._device, ElectricityMixin):
|
||||
self._last_power_sample = await self._device.async_get_instant_metrics(channel=self._channel_id)
|
||||
|
||||
if isinstance(self._device, ConsumptionXMixin):
|
||||
self._daily_consumption = await self._device.async_get_daily_power_consumption(channel=self._channel_id)
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool:
|
||||
dev = self._device
|
||||
return dev.is_on(channel=self._channel_id)
|
||||
|
||||
async def async_turn_off(self, **kwargs) -> None:
|
||||
dev = self._device
|
||||
await dev.async_turn_off(channel=self._channel_id, skip_rate_limits=True)
|
||||
|
||||
async def async_turn_on(self, **kwargs) -> None:
|
||||
dev = self._device
|
||||
await dev.async_turn_on(channel=self._channel_id, skip_rate_limits=True)
|
||||
|
||||
@property
|
||||
def current_power_w(self) -> Optional[float]:
|
||||
if self._last_power_sample is not None:
|
||||
return self._last_power_sample.power
|
||||
|
||||
@property
|
||||
def today_energy_kwh(self) -> Optional[float]:
|
||||
if self._daily_consumption is not None:
|
||||
today = datetime.today()
|
||||
total = 0
|
||||
daystart = datetime(year=today.year, month=today.month, day=today.day, hour=0, second=0)
|
||||
for x in self._daily_consumption:
|
||||
if x['date'] == daystart:
|
||||
total = x['total_consumption_kwh']
|
||||
return total
|
||||
|
||||
|
||||
class DndEntityWrapper(MerossDevice, SwitchEntity):
|
||||
"""Wrapper class to adapt the Meross switches into the Homeassistant platform"""
|
||||
_device: MerossDndDevice
|
||||
|
||||
# The DNDMode change does not trigger any push notification, so we cannot we
|
||||
_attr_should_poll = True
|
||||
_dnd_mode: Optional[DNDMode] = None
|
||||
|
||||
def __init__(self,
|
||||
device: MerossDndDevice,
|
||||
device_list_coordinator: DataUpdateCoordinator[Dict[str, HttpDeviceInfo]]):
|
||||
super().__init__(
|
||||
device=device,
|
||||
channel=-1, # DND devices do not relate to channels
|
||||
device_list_coordinator=device_list_coordinator,
|
||||
platform=HA_SWITCH,
|
||||
override_channel_name="Do Not Disturb")
|
||||
|
||||
async def async_update(self):
|
||||
if self.online:
|
||||
await super().async_update()
|
||||
self._dnd_mode = await self._device.async_get_dnd_mode()
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool | None:
|
||||
if self._dnd_mode is None:
|
||||
return None
|
||||
return self._dnd_mode == DNDMode.DND_DISABLED
|
||||
|
||||
async def async_turn_off(self, **kwargs) -> None:
|
||||
dev = self._device
|
||||
await dev.set_dnd_mode(mode=DNDMode.DND_ENABLED, skip_rate_limits=True)
|
||||
self._dnd_mode = DNDMode.DND_ENABLED
|
||||
|
||||
async def async_turn_on(self, **kwargs) -> None:
|
||||
dev = self._device
|
||||
await dev.set_dnd_mode(mode=DNDMode.DND_DISABLED, skip_rate_limits=True)
|
||||
self._dnd_mode = DNDMode.DND_DISABLED
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, config_entry, async_add_entities):
|
||||
def entity_adder_callback():
|
||||
"""Discover and adds new Meross entities"""
|
||||
manager: MerossManager = hass.data[DOMAIN][MANAGER] # type
|
||||
coordinator = hass.data[DOMAIN][DEVICE_LIST_COORDINATOR]
|
||||
devices = manager.find_devices()
|
||||
|
||||
new_entities = []
|
||||
|
||||
# Identify all the devices that expose the Toggle or ToggleX capabilities
|
||||
devs = filter(lambda d: isinstance(d, ToggleXMixin) or isinstance(d, ToggleMixin), devices)
|
||||
|
||||
# Exclude garage openers, lights.
|
||||
devs = filter(lambda d: not (isinstance(d, GarageOpenerMixin) or isinstance(d, LightMixin)), devs)
|
||||
|
||||
for d in devs:
|
||||
channels = [c.index for c in d.channels] if len(d.channels) > 0 else [0]
|
||||
for channel_index in channels:
|
||||
w = SwitchEntityWrapper(device=d, channel=channel_index,
|
||||
device_list_coordinator=coordinator)
|
||||
if w.unique_id not in hass.data[DOMAIN]["ADDED_ENTITIES_IDS"]:
|
||||
new_entities.append(w)
|
||||
|
||||
dnd_switches = filter(lambda d: isinstance(d, SystemDndMixin), devices)
|
||||
for d in dnd_switches:
|
||||
w = DndEntityWrapper(device=d, device_list_coordinator=coordinator)
|
||||
if w.unique_id not in hass.data[DOMAIN]["ADDED_ENTITIES_IDS"]:
|
||||
new_entities.append(w)
|
||||
|
||||
async_add_entities(new_entities, True)
|
||||
|
||||
coordinator = hass.data[DOMAIN][DEVICE_LIST_COORDINATOR]
|
||||
coordinator.async_add_listener(entity_adder_callback)
|
||||
# Run the entity adder a first time during setup
|
||||
entity_adder_callback()
|
||||
|
||||
# TODO: Implement entry unload
|
||||
# TODO: Unload entry
|
||||
# TODO: Remove entry
|
||||
|
||||
|
||||
def setup_platform(hass, config, async_add_entities, discovery_info=None):
|
||||
pass
|
||||
Reference in New Issue
Block a user