Skip to content

Commit

Permalink
added more config entities
Browse files Browse the repository at this point in the history
  • Loading branch information
krahabb committed Feb 16, 2022
1 parent 85616f8 commit 68f8b67
Show file tree
Hide file tree
Showing 7 changed files with 166 additions and 44 deletions.
4 changes: 2 additions & 2 deletions custom_components/meross_lan/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
platform_setup_entry, platform_unload_entry
)
from .number import (
MLNumber,
MLConfigNumber,
)


Expand Down Expand Up @@ -177,7 +177,7 @@ async def _async_turn_onoff(self, onoff) -> None:



class MtsSetPointNumber(MLNumber):
class MtsSetPointNumber(MLConfigNumber):
"""
Helper entity to configure MTS (thermostats) setpoints
AKA: Heat(comfort) - Cool(sleep) - Eco(away)
Expand Down
145 changes: 118 additions & 27 deletions custom_components/meross_lan/cover.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@

from .merossclient import const as mc
from .meross_entity import _MerossEntity, platform_setup_entry, platform_unload_entry
from .number import MLNumber
from .number import MLConfigNumber
from .switch import MLConfigSwitch
from .const import (
PARAM_GARAGEDOOR_TRANSITION_MAXDURATION,
PARAM_GARAGEDOOR_TRANSITION_MINDURATION,
Expand Down Expand Up @@ -124,6 +125,7 @@ def set_unavailable(self) -> None:


def _parse_state(self, payload: dict) -> None:
#{"channel": 0, "open": 1, "lmTime": 0, "execute": 1}
now = time()
open = payload.get(mc.KEY_OPEN)
execute = payload.get(mc.KEY_EXECUTE)
Expand Down Expand Up @@ -262,9 +264,86 @@ def _transition_end_callback(self, _now: datetime) -> None:
self._open_pending = None


class MLGarageConfigNumber(MLConfigNumber):
"""
Helper entity to configure MRS open/close duration
"""
multiplier = 1000

# these are ok for 2 of the 3 config numbers
# customize those when instantiating
_attr_min_value = 1
_attr_max_value = 60
_attr_step = 1

def __init__(self, device, key: str):
self._key = key
super().__init__(device, None, f"config_{key}")


@property
def name(self) -> str:
return f"{super().name} - {self._key}"


async def async_set_value(self, value: float) -> None:
config = dict(self.device.garageDoor_config)
config[self._key] = int(value * self.multiplier)

def _ack_callback():
self.device.garageDoor_config[self._key] = config[self._key]

self.device.request(
mc.NS_APPLIANCE_GARAGEDOOR_CONFIG,
mc.METHOD_SET,
{ mc.KEY_CONFIG: config },
_ack_callback
)


class MLGarageConfigSwitch(MLConfigSwitch):


def __init__(self, device, key: str):
self._key = key
super().__init__(device, None, f"config_{key}", None, None)


@property
def name(self) -> str:
return f"{super().name} - {self._key}"


def request_onoff(self, onoff):
config = dict(self.device.garageDoor_config)
config[self._key] = onoff

def _ack_callback():
self.device.garageDoor_config[self._key] = config[self._key]

self.device.request(
mc.NS_APPLIANCE_GARAGEDOOR_CONFIG,
mc.METHOD_SET,
{ mc.KEY_CONFIG: config },
_ack_callback
)



class GarageMixin:

def __init__(self, api, descriptor, entry) -> None:
super().__init__(api, descriptor, entry)
if mc.NS_APPLIANCE_GARAGEDOOR_CONFIG in descriptor.ability:
self.garageDoor_config = {}
self.config_signalDuration = MLGarageConfigNumber(self, mc.KEY_SIGNALDURATION)
self.config_signalDuration._attr_step = 0.1 # 100 msec step in UI
self.config_signalDuration._attr_min_value = 0.1 # 100 msec minimum duration
self.config_buzzerEnable = MLGarageConfigSwitch(self, mc.KEY_BUZZERENABLE)
self.config_doorOpenDuration = MLGarageConfigNumber(self, mc.KEY_DOOROPENDURATION)
self.config_doorCloseDuration = MLGarageConfigNumber(self, mc.KEY_DOORCLOSEDURATION)
self.polling_dictionary.add(mc.NS_APPLIANCE_GARAGEDOOR_CONFIG)


def _init_garageDoor(self, payload: dict):
MLGarage(self, payload[mc.KEY_CHANNEL])
Expand All @@ -275,6 +354,24 @@ def _handle_Appliance_GarageDoor_State(self,
self._parse__generic(mc.KEY_STATE, payload.get(mc.KEY_STATE))


def _handle_Appliance_GarageDoor_Config(self,
namespace: str, method: str, payload: dict, header: dict):
#{"config": {"signalDuration": 1000, "buzzerEnable": 0, "doorOpenDuration": 30000, "doorCloseDuration": 30000}}
# no channel here ?!..need to parse the manual way
payload = payload.get(mc.KEY_CONFIG)
if isinstance(payload, dict):
self.garageDoor_config.update(payload)
if mc.KEY_SIGNALDURATION in payload:
self.config_signalDuration.update_value(payload[mc.KEY_SIGNALDURATION])
if mc.KEY_BUZZERENABLE in payload:
self.config_buzzerEnable.update_onoff(payload[mc.KEY_BUZZERENABLE])
if mc.KEY_DOOROPENDURATION in payload:
self.config_doorOpenDuration.update_value(payload[mc.KEY_DOOROPENDURATION])
if mc.KEY_DOORCLOSEDURATION in payload:
self.config_doorCloseDuration.update_value(payload[mc.KEY_DOORCLOSEDURATION])
return


def _parse_garageDoor(self, payload):
self._parse__generic(mc.KEY_STATE, payload)

Expand Down Expand Up @@ -339,37 +436,37 @@ def is_position_native(self) -> bool:


async def async_open_cover(self, **kwargs) -> None:
self._request(100)
self.request_position(100)


async def async_close_cover(self, **kwargs) -> None:
self._request(0)
self.request_position(0)


async def async_set_cover_position(self, **kwargs):
if ATTR_POSITION in kwargs:
position = kwargs[ATTR_POSITION]
if self.is_position_native:
self._request(position)
self.request_position(position)
else:
if position > self._position_timed:
self._request(
self.request_position(
100,
((position - self._position_timed) * self._signalOpen) / 100000
)
elif position < self._position_timed:
self._request(
self.request_position(
0,
((self._position_timed - position) * self._signalClose) / 100000
)


async def async_stop_cover(self, **kwargs):
self._request(-1)
self.request_position(-1)


def _request(self, command, timeout = None):
self.device.log(DEBUG, 0, "MLRollerShutter(0): _request(%s, %s)", str(command), str(timeout))
def request_position(self, command, timeout = None):
self.device.log(DEBUG, 0, "MLRollerShutter(0): request_position(%s, %s)", str(command), str(timeout))
def _ack_callback():
self.device.log(DEBUG, 0, "MLRollerShutter(0): _ack_callback")
if timeout is not None:
Expand Down Expand Up @@ -450,7 +547,7 @@ def _parse_state(self, payload: dict):
if self._position_endtime is not None:
# in case our _close_calback has not been called or failed
if epoch >= self._position_endtime:
self._request(-1)
self.request_position(-1)

if self._transition_unsub is None:
# ensure we 'follow' cover movement
Expand All @@ -461,11 +558,11 @@ def _parse_config(self, payload: dict) -> None:
# payload = {"channel": 0, "signalOpen": 50000, "signalClose": 50000}
if mc.KEY_SIGNALOPEN in payload:
self._signalOpen = payload[mc.KEY_SIGNALOPEN] # time to fully open cover in msec
self._number_signalOpen.update_state(self._signalOpen / 1000)
self._number_signalOpen.update_value(self._signalOpen)
self._attr_extra_state_attributes[EXTRA_ATTR_DURATION_OPEN] = self._signalOpen
if mc.KEY_SIGNALCLOSE in payload:
self._signalClose = payload[mc.KEY_SIGNALCLOSE] # time to fully close cover in msec
self._number_signalClose.update_state(self._signalClose / 1000)
self._number_signalClose.update_value(self._signalClose)
self._attr_extra_state_attributes[EXTRA_ATTR_DURATION_CLOSE] = self._signalClose


Expand All @@ -491,7 +588,7 @@ def _transition_cancel(self):
def _stop_callback(self, _now: datetime) -> None:
self.device.log(DEBUG, 0, "MLRollerShutter(0): _stop_callback")
self._stop_unsub = None
self._request(-1)
self.request_position(-1)


def _stop_cancel(self):
Expand All @@ -503,10 +600,16 @@ def _stop_cancel(self):



class MLRollerShutterConfigNumber(MLNumber):
class MLRollerShutterConfigNumber(MLConfigNumber):
"""
Helper entity to configure MRS open/close duration
"""
multiplier = 1000

_attr_min_value = 1
_attr_max_value = 60
_attr_step = 1

def __init__(self, cover: MLRollerShutter, key: str):
self._cover = cover
self._key = key
Expand All @@ -518,19 +621,6 @@ def name(self) -> str:
return f"{self._cover.name} - {self._key}"


@property
def step(self) -> float:
return 1

@property
def min_value(self) -> float:
return 1

@property
def max_value(self) -> float:
return 60


async def async_set_value(self, value: float) -> None:
config = {
mc.KEY_CHANNEL: self.channel,
Expand All @@ -550,6 +640,7 @@ def _ack_callback():
)



class RollerShutterMixin:


Expand Down
6 changes: 3 additions & 3 deletions custom_components/meross_lan/meross_device_hub.py
Original file line number Diff line number Diff line change
Expand Up @@ -369,10 +369,10 @@ def __init__(self, hub: MerossDeviceHub, p_digest: dict) -> None:
self.sensor_humidity = MLSensor.build_for_subdevice(self, DEVICE_CLASS_HUMIDITY)
self.number_adjust_temperature = MLHubAdjustNumber(
self, mc.KEY_TEMPERATURE, mc.NS_APPLIANCE_HUB_SENSOR_ADJUST,
'', DEVICE_CLASS_TEMPERATURE, 100, -5, 5, 0.1)
'', DEVICE_CLASS_TEMPERATURE, -5, 5, 0.1)
self.number_adjust_humidity = MLHubAdjustNumber(
self, mc.KEY_HUMIDITY, mc.NS_APPLIANCE_HUB_SENSOR_ADJUST,
'', DEVICE_CLASS_HUMIDITY, 100, -20, 20, 1)
'', DEVICE_CLASS_HUMIDITY, -20, 20, 1)


def _setonline(self) -> None:
Expand Down Expand Up @@ -415,7 +415,7 @@ def __init__(self, hub: MerossDeviceHub, p_digest: dict, _type: str = mc.TYPE_MT
self, DEVICE_CLASS_TEMPERATURE)
self.number_adjust_temperature = MLHubAdjustNumber(
self, mc.KEY_TEMPERATURE, mc.NS_APPLIANCE_HUB_MTS100_ADJUST,
'', DEVICE_CLASS_TEMPERATURE, 100, -5, 5, 0.1)
'', DEVICE_CLASS_TEMPERATURE, -5, 5, 0.1)


def _setonline(self) -> None:
Expand Down
9 changes: 6 additions & 3 deletions custom_components/meross_lan/meross_entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,14 +203,17 @@ def __init__(


async def async_turn_on(self, **kwargs) -> None:
self._request(1)
self.request_onoff(1)


async def async_turn_off(self, **kwargs) -> None:
self._request(0)
self.request_onoff(0)


def _request(self, onoff):
def request_onoff(self, onoff):
# this is the meross executor code
# override for switches not implemented
# by a toggle like api
def _ack_callback():
self.update_onoff(onoff)

Expand Down
9 changes: 7 additions & 2 deletions custom_components/meross_lan/merossclient/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@

# Garage door opener
NS_APPLIANCE_GARAGEDOOR_STATE = "Appliance.GarageDoor.State"
NS_APPLIANCE_GARAGEDOOR_CONFIG = "Appliance.GarageDoor.Config"
# Roller shutter
NS_APPLIANCE_ROLLERSHUTTER_STATE = 'Appliance.RollerShutter.State'
NS_APPLIANCE_ROLLERSHUTTER_POSITION = 'Appliance.RollerShutter.Position'
Expand Down Expand Up @@ -188,8 +189,12 @@
KEY_STATE = 'state'
KEY_POSITION = 'position'
KEY_CONFIG = 'config'
KEY_SIGNALOPEN = 'signalOpen'
KEY_SIGNALCLOSE = 'signalClose'
KEY_SIGNALOPEN = 'signalOpen'# rollershutter config
KEY_SIGNALCLOSE = 'signalClose'# rollershutter config
KEY_SIGNALDURATION = 'signalDuration'# garageDoor config
KEY_BUZZERENABLE = 'buzzerEnable'# garageDoor config
KEY_DOOROPENDURATION = 'doorOpenDuration'# garageDoor config
KEY_DOORCLOSEDURATION = 'doorCloseDuration'# garageDoor config
KEY_OPEN = 'open'
KEY_EXECUTE = 'execute'
KEY_MODE = 'mode'
Expand Down
Loading

0 comments on commit 68f8b67

Please sign in to comment.