Skip to content

Commit

Permalink
fix hub sensor adjust commands
Browse files Browse the repository at this point in the history
  • Loading branch information
krahabb committed Dec 7, 2023
1 parent 4f74f4e commit 69b6c24
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 81 deletions.
73 changes: 49 additions & 24 deletions custom_components/meross_lan/devices/mts100.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,43 +6,45 @@
from ..climate import MtsClimate, MtsSetPointNumber
from ..helpers import reverse_lookup
from ..merossclient import const as mc
from ..number import MLHubAdjustNumber
from ..number import MLConfigNumber

if typing.TYPE_CHECKING:
from ..meross_device_hub import MTS100SubDevice


class Mts100AdjustNumber(MLHubAdjustNumber):
class Mts100AdjustNumber(MLConfigNumber):
namespace = mc.NS_APPLIANCE_HUB_MTS100_ADJUST
key_namespace = mc.KEY_ADJUST
key_channel = mc.KEY_ID
key_value = mc.KEY_TEMPERATURE

__slots__ = ("climate",)

def __init__(self, manager: MTS100SubDevice, climate: Mts100Climate):
self.climate = climate # climate not initialized yet
self._attr_name = "Adjust temperature"
super().__init__(
manager,
mc.KEY_TEMPERATURE,
mc.NS_APPLIANCE_HUB_MTS100_ADJUST,
MLHubAdjustNumber.DeviceClass.TEMPERATURE,
-5,
5,
0.1,
manager.id,
f"config_{self.key_namespace}_{self.key_value}",
MLConfigNumber.DeviceClass.TEMPERATURE,
)

def update_native_value(self, device_value):
super().update_native_value(device_value)
# hub adjust has a scale of 100 while the other climate temperature
# numbers have a scale of 10 (MTS_SCALE)
adjust_offset = round(device_value / 10) % 5
# since adjust have a resolution of 0.1 °C while temp setpoints have a 0.5 °C
# stepping, when the adjust is not a multiple of 0.5 the MTS looses the
# correct setpoints and starts to round down their values.
# it looks like it is not able to represent correctly the offsets when
# these are not in multiple of 0.5. We therefore try to 'patch'
# these readings before sending them to HA
self.climate._mts_adjust_offset = adjust_offset if adjust_offset < 3 else adjust_offset - 5
# _mts_adjust_offset will then be used to offset the T setpoints and will be 0 when
# the adjust value is a 0.5 multiple or the corresponding remainder when it is not.
# the offset is set so it 'down-rounds' when it is 0.1 or 0.2. Instead it will 'up-rounds'
# when it is 0.3 or 0.4
@property
def native_max_value(self):
return 5

@property
def native_min_value(self):
return -5

@property
def native_step(self):
return 0.1

@property
def native_unit_of_measurement(self):
return MtsClimate.TEMP_CELSIUS

async def async_set_native_value(self, value: float):
# when sending the 'adjust' to the valve, the device also modifies
Expand Down Expand Up @@ -90,6 +92,29 @@ async def async_set_native_value(self, value: float):
response[mc.KEY_PAYLOAD][mc.KEY_TEMPERATURE][0]
)

@property
def device_scale(self):
return 100

def update_native_value(self, device_value):
super().update_native_value(device_value)
# hub adjust has a scale of 100 while the other climate temperature
# numbers have a scale of 10 (MTS_SCALE)
adjust_offset = round(device_value / 10) % 5
# since adjust have a resolution of 0.1 °C while temp setpoints have a 0.5 °C
# stepping, when the adjust is not a multiple of 0.5 the MTS looses the
# correct setpoints and starts to round down their values.
# it looks like it is not able to represent correctly the offsets when
# these are not in multiple of 0.5. We therefore try to 'patch'
# these readings before sending them to HA
self.climate._mts_adjust_offset = (
adjust_offset if adjust_offset < 3 else adjust_offset - 5
)
# _mts_adjust_offset will then be used to offset the T setpoints and will be 0 when
# the adjust value is a 0.5 multiple or the corresponding remainder when it is not.
# the offset is set so it 'down-rounds' when it is 0.1 or 0.2. Instead it will 'up-rounds'
# when it is 0.3 or 0.4


class Mts100Climate(MtsClimate):
"""Climate entity for hub paired devices MTS100, MTS100V3, MTS150"""
Expand Down
74 changes: 63 additions & 11 deletions custom_components/meross_lan/meross_device_hub.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
get_productnameuuid,
is_device_online,
)
from .number import MLHubAdjustNumber
from .number import MLConfigNumber
from .select import MtsTrackedSensor
from .sensor import MLSensor
from .switch import MLSwitch
Expand All @@ -43,6 +43,60 @@
TRICK = False


class MLHubSensorAdjustNumber(MLConfigNumber):
namespace = mc.NS_APPLIANCE_HUB_SENSOR_ADJUST
key_namespace = mc.KEY_ADJUST
key_channel = mc.KEY_ID

def __init__(
self,
manager: MerossSubDevice,
key: str,
device_class: MLConfigNumber.DeviceClass,
min_value: float,
max_value: float,
step: float,
):
self.key_value = key
self._attr_native_min_value = min_value
self._attr_native_max_value = max_value
self._attr_native_step = step
self._attr_native_unit_of_measurement = (
MLConfigNumber.DEVICECLASS_TO_UNIT_MAP.get(device_class)
)
self._attr_name = f"Adjust {device_class}"
super().__init__(
manager,
manager.id,
f"config_{self.key_namespace}_{self.key_value}",
device_class,
)

async def async_set_native_value(self, value: float):
# the SET command on NS_APPLIANCE_HUB_SENSOR_ADJUST works by applying
# the issued value as a 'delta' to the current configured value i.e.
# 'new adjust value' = 'current adjust value' + 'issued adjust value'
# Since the native HA interface async_set_native_value wants to set
# the 'new adjust value' we have to issue the difference against the
# currently configured one
device_value = round(value * self.device_scale) + self.device_offset
if adjust_value := device_value - self.device_value:
if await self.manager.async_request_ack(
self.namespace,
mc.METHOD_SET,
{
self.key_namespace: [
{self.key_channel: self.channel, self.key_value: adjust_value}
]
},
):
self.update_native_value(device_value)

@property
def device_scale(self):
return 10


class SubDevicePollingStrategy(PollingStrategy):
"""
This is a strategy for polling (general) subdevices state with special care for messages
Expand Down Expand Up @@ -117,7 +171,7 @@ def __init__(self, descriptor, entry):
self.platforms[MLBinarySensor.PLATFORM] = None
self.platforms[MtsClimate.PLATFORM] = None
self.platforms[MtsTrackedSensor.PLATFORM] = None
self.platforms[MLHubAdjustNumber.PLATFORM] = None
self.platforms[MLHubSensorAdjustNumber.PLATFORM] = None
self.platforms[MLSwitch.PLATFORM] = None
self.platforms[MLCalendar.PLATFORM] = None

Expand Down Expand Up @@ -612,7 +666,7 @@ def _parse_adjust(self, p_adjust: dict):
for p_key, p_value in p_adjust.items():
if p_key == mc.KEY_ID:
continue
number: MLHubAdjustNumber
number: MLHubSensorAdjustNumber
if number := getattr(self, f"number_adjust_{p_key}", None): # type: ignore
number.update_native_value(p_value)

Expand Down Expand Up @@ -655,20 +709,18 @@ def __init__(self, hub: MerossDeviceHub, p_digest: dict):
super().__init__(hub, p_digest, mc.TYPE_MS100)
self.sensor_temperature = self.build_sensor_c(MLSensor.DeviceClass.TEMPERATURE)
self.sensor_humidity = self.build_sensor_c(MLSensor.DeviceClass.HUMIDITY)
self.number_adjust_temperature = MLHubAdjustNumber(
self.number_adjust_temperature = MLHubSensorAdjustNumber(
self,
mc.KEY_TEMPERATURE,
mc.NS_APPLIANCE_HUB_SENSOR_ADJUST,
MLHubAdjustNumber.DeviceClass.TEMPERATURE,
MLHubSensorAdjustNumber.DeviceClass.TEMPERATURE,
-5,
5,
0.1,
)
self.number_adjust_humidity = MLHubAdjustNumber(
self.number_adjust_humidity = MLHubSensorAdjustNumber(
self,
mc.KEY_HUMIDITY,
mc.NS_APPLIANCE_HUB_SENSOR_ADJUST,
MLHubAdjustNumber.DeviceClass.HUMIDITY,
MLHubSensorAdjustNumber.DeviceClass.HUMIDITY,
-20,
20,
1,
Expand All @@ -678,8 +730,8 @@ async def async_shutdown(self):
await super().async_shutdown()
self.sensor_temperature: MLSensor = None # type: ignore
self.sensor_humidity: MLSensor = None # type: ignore
self.number_adjust_temperature: MLHubAdjustNumber = None # type: ignore
self.number_adjust_humidity: MLHubAdjustNumber = None # type: ignore
self.number_adjust_temperature: MLHubSensorAdjustNumber = None # type: ignore
self.number_adjust_humidity: MLHubSensorAdjustNumber = None # type: ignore

def _parse_humidity(self, p_humidity: dict):
if isinstance(p_latest := p_humidity.get(mc.KEY_LATEST), int):
Expand Down
56 changes: 10 additions & 46 deletions custom_components/meross_lan/number.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,13 @@
from homeassistant.const import PERCENTAGE, TEMP_CELSIUS

from . import meross_entity as me
from .merossclient import const as mc, get_namespacekey # mEROSS cONST
from .merossclient import const as mc

if typing.TYPE_CHECKING:
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant

from .meross_device import MerossDevice
from .meross_device_hub import MerossSubDevice


try:
Expand Down Expand Up @@ -42,15 +41,13 @@ async def async_setup_entry(
me.platform_setup_entry(hass, config_entry, async_add_devices, number.DOMAIN)


DEVICECLASS_TO_UNIT_MAP = {
NumberDeviceClass.HUMIDITY: PERCENTAGE,
NumberDeviceClass.TEMPERATURE: TEMP_CELSIUS,
}


class MLConfigNumber(me.MerossEntity, number.NumberEntity):
PLATFORM = number.DOMAIN
DeviceClass = NumberDeviceClass
DEVICECLASS_TO_UNIT_MAP = {
NumberDeviceClass.HUMIDITY: PERCENTAGE,
NumberDeviceClass.TEMPERATURE: TEMP_CELSIUS,
}

manager: MerossDevice

Expand All @@ -76,6 +73,7 @@ class MLConfigNumber(me.MerossEntity, number.NumberEntity):
"_device_value",
)

# interface: number.NumberEntity
@property
def mode(self) -> number.NumberMode:
"""Return the mode of the entity."""
Expand All @@ -101,10 +99,6 @@ def native_unit_of_measurement(self):
def native_value(self):
return self._attr_state

def update_native_value(self, device_value):
self._device_value = device_value
self.update_state((device_value - self.device_offset) / self.device_scale)

async def async_set_native_value(self, value: float):
device_value = round(value * self.device_scale) + self.device_offset
if await self.manager.async_request_ack(
Expand All @@ -118,6 +112,7 @@ async def async_set_native_value(self, value: float):
):
self.update_native_value(device_value)

# interface: self
@property
def device_offset(self):
"""used to offset the device value when converting to/from native value"""
Expand All @@ -133,37 +128,6 @@ def device_value(self):
"""the 'native' device value carried in protocol messages"""
return self._device_value


class MLHubAdjustNumber(MLConfigNumber):
key_channel = mc.KEY_ID

def __init__(
self,
manager: "MerossSubDevice",
key: str,
namespace: str,
device_class: NumberDeviceClass,
min_value: float,
max_value: float,
step: float,
):
self.namespace = namespace
self.key_namespace = get_namespacekey(namespace)
self.key_value = key
self._attr_native_min_value = min_value
self._attr_native_max_value = max_value
self._attr_native_step = step
self._attr_native_unit_of_measurement = DEVICECLASS_TO_UNIT_MAP.get(
device_class
)
self._attr_name = f"Adjust {device_class}"
super().__init__(
manager,
manager.id,
f"config_{self.key_namespace}_{key}",
device_class,
)

@property
def device_scale(self):
return 100
def update_native_value(self, device_value):
self._device_value = device_value
self.update_state((device_value - self.device_offset) / self.device_scale)

0 comments on commit 69b6c24

Please sign in to comment.