Skip to content

Commit

Permalink
Merge branches 'feature-humidifier-support' and 'master' of https://g…
Browse files Browse the repository at this point in the history
  • Loading branch information
albertogeniola committed Mar 8, 2020
2 parents ceabc66 + 08d01e4 commit 1bcb502
Show file tree
Hide file tree
Showing 4 changed files with 169 additions and 7 deletions.
3 changes: 2 additions & 1 deletion custom_components/meross_cloud/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from meross_iot.manager import MerossManager
from .common import (DOMAIN, ATTR_CONFIG, MEROSS_PLATFORMS, HA_COVER, HA_LIGHT, HA_SENSOR,
HA_SWITCH, MANAGER, SENSORS, dismiss_notification,
notify_error, CLOUD_HANDLER, MerossCloudConnectionWatchdog, HA_CLIMATE)
notify_error, CLOUD_HANDLER, MerossCloudConnectionWatchdog, HA_CLIMATE, HA_FAN)

# Unset the default stream handler for logger of the meross_iot library
ROOT_MEROSS_LOGGER.removeHandler(h)
Expand Down Expand Up @@ -61,6 +61,7 @@ async def async_setup_entry(hass: HomeAssistantType, config_entry):
hass.data[DOMAIN][HA_LIGHT] = {}
hass.data[DOMAIN][HA_SENSOR] = {}
hass.data[DOMAIN][HA_SWITCH] = {}
hass.data[DOMAIN][HA_FAN] = {}

_LOGGER.info("Starting meross manager")
manager.start()
Expand Down
17 changes: 12 additions & 5 deletions custom_components/meross_cloud/climate.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import logging
from typing import Optional, List
from typing import Optional, List, Union, Any

from homeassistant.components.climate import ClimateDevice, SUPPORT_TARGET_TEMPERATURE, SUPPORT_PRESET_MODE
from homeassistant.components.climate.const import HVAC_MODE_AUTO, HVAC_MODE_HEAT, HVAC_MODE_OFF, PRESET_NONE, \
CURRENT_HVAC_HEAT, CURRENT_HVAC_OFF, CURRENT_HVAC_IDLE
from homeassistant.const import TEMP_CELSIUS
from homeassistant.components.fan import FanEntity
from homeassistant.const import TEMP_CELSIUS, STATE_UNKNOWN, STATE_OFF
from homeassistant.helpers.entity import Entity
from meross_iot.cloud.devices.subdevices.thermostats import ValveSubDevice, ThermostatV3Mode, ThermostatMode
from meross_iot.cloud.devices.humidifier import GenericHumidifier, SprayMode
from meross_iot.manager import MerossManager
from meross_iot.meross_event import ThermostatTemperatureChange, ThermostatModeChange, DeviceSwitchStatusEvent, \
DeviceOnlineStatusEvent
Expand Down Expand Up @@ -318,14 +321,18 @@ async def async_will_remove_from_hass(self) -> None:

async def async_setup_entry(hass, config_entry, async_add_entities):
def sync_logic():
thermostat_devices = []

climante_devices = []
manager = hass.data[DOMAIN][MANAGER] # type:MerossManager

# Add smart thermostat valves
valves = manager.get_devices_by_kind(ValveSubDevice)
for valve in valves: # type: ValveSubDevice
w = ValveEntityWrapper(device=valve)
thermostat_devices.append(w)
climante_devices.append(w)
hass.data[DOMAIN][HA_CLIMATE][w.unique_id] = w
return thermostat_devices

return climante_devices

thermostat_devices = await hass.async_add_executor_job(sync_logic)
async_add_entities(thermostat_devices)
Expand Down
3 changes: 2 additions & 1 deletion custom_components/meross_cloud/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
HA_SENSOR = 'sensor'
HA_COVER = 'cover'
HA_CLIMATE = 'climate'
MEROSS_PLATFORMS = (HA_LIGHT, HA_SWITCH, HA_COVER, HA_SENSOR, HA_CLIMATE)
HA_FAN = 'fan'
MEROSS_PLATFORMS = (HA_LIGHT, HA_SWITCH, HA_COVER, HA_SENSOR, HA_CLIMATE, HA_FAN)
CONNECTION_TIMEOUT_THRESHOLD = 5


Expand Down
153 changes: 153 additions & 0 deletions custom_components/meross_cloud/fan.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import logging
from typing import Optional, Any

from homeassistant.components.fan import FanEntity, SUPPORT_SET_SPEED
from meross_iot.cloud.devices.humidifier import GenericHumidifier, SprayMode
from meross_iot.manager import MerossManager
from meross_iot.meross_event import DeviceOnlineStatusEvent, HumidifierSpryEvent

from .common import DOMAIN, MANAGER, AbstractMerossEntityWrapper, cloud_io, HA_FAN

_LOGGER = logging.getLogger(__name__)


class MerossSmartHumidifier(FanEntity, AbstractMerossEntityWrapper):
"""
At the time of writing, Homeassistant does not offer any specific device implementation that we can extend
for implementing the smart humidifier. We'll exploit the fan entity to do so
"""

def __init__(self, device: GenericHumidifier):
super().__init__(device)
self._id = device.uuid
self._device_name = device.name

# Device state
self._humidifier_mode = None
self._is_on = None
self._is_online = self._device.online

def parse_spry_mode(self, spry_mode):
if spry_mode == SprayMode.OFF:
return False, self._humidifier_mode
elif spry_mode == SprayMode.INTERMITTENT:
return True, SprayMode.INTERMITTENT
elif spry_mode == SprayMode.CONTINUOUS:
return True, SprayMode.CONTINUOUS
else:
raise ValueError("Unsupported spry mode.")

def device_event_handler(self, evt):
if isinstance(evt, DeviceOnlineStatusEvent):
_LOGGER.info("Device %s reported online status: %s" % (self._device.name, evt.status))
if evt.status not in ["online", "offline"]:
raise ValueError("Invalid online status")
self._is_online = evt.status == "online"
elif isinstance(evt, HumidifierSpryEvent):
self._is_on, self._humidifier_mode = self.parse_spry_mode(evt.spry_mode)
else:
_LOGGER.warning("Unhandled/ignored event: %s" % str(evt))

self.schedule_update_ha_state(False)

@cloud_io
def update(self):
state = self._device.get_status(True)
self._is_online = self._device.online

if self._is_online:
self._is_on, self._humidifier_mode = self.parse_spry_mode(self._device.get_spray_mode())

def force_state_update(self, ui_only=False):
if not self.enabled:
return

force_refresh = not ui_only
self.schedule_update_ha_state(force_refresh=force_refresh)

async def async_added_to_hass(self) -> None:
self._device.register_event_callback(self.device_event_handler)

async def async_will_remove_from_hass(self) -> None:
self._device.unregister_event_callback(self.device_event_handler)

@property
def available(self) -> bool:
return self._is_online

@property
def is_on(self) -> bool:
return self._is_on

@property
def speed(self) -> Optional[str]:
if self._humidifier_mode is None:
return None
return self._humidifier_mode.name

@property
def supported_features(self) -> int:
return 0 | SUPPORT_SET_SPEED

@property
def speed_list(self) -> list:
"""Get the list of available speeds."""
return [e.name for e in SprayMode if e != SprayMode.OFF]

@cloud_io
def set_speed(self, speed: str) -> None:
mode = SprayMode[speed]
self._device.set_spray_mode(mode)

@cloud_io
def set_direction(self, direction: str) -> None:
# Not supported
pass

@cloud_io
def turn_on(self, speed: Optional[str] = None, **kwargs) -> None:
# Assume the user wants to trigger the last mode
mode = self._humidifier_mode
# If a specific speed was provided, override the last mode
if speed is not None:
mode = SprayMode[speed]
# Otherwise, assume we want intermittent mode
if mode is None:
mode = SprayMode.INTERMITTENT

self._device.set_spray_mode(mode)

@cloud_io
def turn_off(self, **kwargs: Any) -> None:
self._device.set_spray_mode(SprayMode.OFF)

@property
def name(self) -> Optional[str]:
return self._device_name

@property
def should_poll(self) -> bool:
"""
This device handles stat update via push notification
:return:
"""
return False


async def async_setup_entry(hass, config_entry, async_add_entities):
def sync_logic():

fan_devices = []
manager = hass.data[DOMAIN][MANAGER] # type:MerossManager

# Add smart humidifiers
humidifiers = manager.get_devices_by_kind(GenericHumidifier)
for humidifier in humidifiers:
h = MerossSmartHumidifier(device=humidifier)
fan_devices.append(h)
hass.data[DOMAIN][HA_FAN][h.unique_id] = h

return fan_devices

devices = await hass.async_add_executor_job(sync_logic)
async_add_entities(devices)

0 comments on commit 1bcb502

Please sign in to comment.