Skip to content

Commit

Permalink
Merge pull request #480 from krahabb/dev
Browse files Browse the repository at this point in the history
Moonlight.3.1
  • Loading branch information
krahabb authored Aug 15, 2024
2 parents f8d3b4f + d7426d8 commit b7b251c
Show file tree
Hide file tree
Showing 62 changed files with 6,469 additions and 1,410 deletions.
21 changes: 10 additions & 11 deletions custom_components/meross_lan/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
ConfigEntryType,
Loggable,
async_import_module,
schedule_async_callback,
)
from .helpers.manager import ApiProfile, ConfigEntryManager
from .meross_device import MerossDevice
Expand Down Expand Up @@ -81,8 +80,8 @@ def __init__(self, api: "MerossApi"):
if MEROSSDEBUG:
# TODO : check bug in hass shutdown
async def _async_random_disconnect():
self._unsub_random_disconnect = schedule_async_callback(
MerossApi.hass, 60, _async_random_disconnect
self._unsub_random_disconnect = api.schedule_async_callback(
60, _async_random_disconnect
)
if self._mqtt_subscribing:
return
Expand All @@ -95,8 +94,8 @@ async def _async_random_disconnect():
self.log(self.DEBUG, "random disconnect")
await self.async_mqtt_unsubscribe()

self._unsub_random_disconnect = schedule_async_callback(
MerossApi.hass, 60, _async_random_disconnect
self._unsub_random_disconnect = api.schedule_async_callback(
60, _async_random_disconnect
)
else:
self._unsub_random_disconnect = None
Expand Down Expand Up @@ -159,10 +158,10 @@ def _connection_status_callback(connected: bool):
)
except:
self._unsub_mqtt_disconnected = mqtt.async_dispatcher_connect(
hass, mqtt.MQTT_DISCONNECTED, self._mqtt_disconnected # type: ignore (removed in HA core 2024.6)
hass, mqtt.MQTT_DISCONNECTED, self._mqtt_disconnected # type: ignore (removed in HA core 2024.6)
)
self._unsub_mqtt_connected = mqtt.async_dispatcher_connect(
hass, mqtt.MQTT_CONNECTED, self._mqtt_connected # type: ignore (removed in HA core 2024.6)
hass, mqtt.MQTT_CONNECTED, self._mqtt_connected # type: ignore (removed in HA core 2024.6)
)
if mqtt.is_connected(hass):
self._mqtt_connected()
Expand Down Expand Up @@ -289,10 +288,10 @@ async def _handle_Appliance_System_Clock(


HAMQTTConnection.SESSION_HANDLERS = {
mc.NS_APPLIANCE_CONTROL_BIND: HAMQTTConnection._handle_Appliance_Control_Bind,
mc.NS_APPLIANCE_CONTROL_CONSUMPTIONCONFIG: HAMQTTConnection._handle_Appliance_Control_ConsumptionConfig,
mc.NS_APPLIANCE_SYSTEM_CLOCK: HAMQTTConnection._handle_Appliance_System_Clock,
mc.NS_APPLIANCE_SYSTEM_ONLINE: MQTTConnection._handle_Appliance_System_Online,
mn.Appliance_Control_Bind.name: HAMQTTConnection._handle_Appliance_Control_Bind,
mn.Appliance_Control_ConsumptionConfig.name: HAMQTTConnection._handle_Appliance_Control_ConsumptionConfig,
mn.Appliance_System_Clock.name: HAMQTTConnection._handle_Appliance_System_Clock,
mn.Appliance_System_Online.name: MQTTConnection._handle_Appliance_System_Online,
}


Expand Down
3 changes: 1 addition & 2 deletions custom_components/meross_lan/calendar.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,7 @@ def __init__(self, climate: MtsClimate):
# shown/available in the calendar UI.
self._schedule_entry_count_max = 0
self._schedule_entry_count_min = 0
self.name = "Schedule"
super().__init__(climate.manager, climate.channel, self.ns.key)
super().__init__(climate.manager, climate.channel, self.ns.key, name="Schedule")

# interface: MerossEntity
async def async_shutdown(self):
Expand Down
2 changes: 1 addition & 1 deletion custom_components/meross_lan/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,7 @@ async def _async_http_discovery(
)[mc.KEY_PAYLOAD][mc.KEY_ALL]
except:
# might it be the device needs encryption?
if mc.NS_APPLIANCE_ENCRYPT_ECDHE not in ability:
if mn.Appliance_Encrypt_ECDHE.name not in ability:
raise
# here we'd need the uuid and mac but we have no ns_all
# to parse so we'll try extract these info from ns_ability query
Expand Down
3 changes: 2 additions & 1 deletion custom_components/meross_lan/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,8 @@ class ProfileConfigType(
#
DND_ID: Final = "dnd"
SIGNALSTRENGTH_ID: Final = "signal_strength"
ENERGY_ESTIMATE_ID: Final = "energy_estimate"
CONSUMPTIONX_SENSOR_KEY: Final = "energy"
ELECTRICITY_SENSOR_KEY: Final = "energy_estimate"
#
# issues general consts
#
Expand Down
68 changes: 33 additions & 35 deletions custom_components/meross_lan/cover.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from . import meross_entity as me
from .const import CONF_PROTOCOL_HTTP, PARAM_ROLLERSHUTTER_TRANSITION_POLL_TIMEOUT
from .helpers import schedule_async_callback, versiontuple
from .helpers import versiontuple
from .merossclient import const as mc, namespaces as mn
from .number import MLConfigNumber

Expand All @@ -15,10 +15,6 @@
from .meross_device import MerossDevice


# rollershutter extra attributes
EXTRA_ATTR_POSITION_NATIVE = "position_native"


async def async_setup_entry(hass, config_entry, async_add_devices):
me.platform_setup_entry(hass, config_entry, async_add_devices, cover.DOMAIN)

Expand Down Expand Up @@ -89,6 +85,8 @@ class MLRollerShutter(MLCover):
MRS100 SHUTTER ENTITY
"""

ATTR_POSITION_NATIVE = "position_native"

# HA core entity attributes:
assumed_state = True
current_cover_position: int | None
Expand Down Expand Up @@ -138,12 +136,12 @@ def __init__(self, manager: "MerossDevice"):
super().__init__(manager, 0, MLCover.DeviceClass.SHUTTER)
self.number_signalOpen = MLRollerShutterConfigNumber(self, mc.KEY_SIGNALOPEN)
self.number_signalClose = MLRollerShutterConfigNumber(self, mc.KEY_SIGNALCLOSE)
if mc.NS_APPLIANCE_ROLLERSHUTTER_ADJUST in descriptor.ability:
if mn.Appliance_RollerShutter_Adjust.name in descriptor.ability:
# unknown use: actually the polling period is set on a very high timeout
manager.register_parser(mc.NS_APPLIANCE_ROLLERSHUTTER_ADJUST, self)
manager.register_parser(mc.NS_APPLIANCE_ROLLERSHUTTER_CONFIG, self)
manager.register_parser(mc.NS_APPLIANCE_ROLLERSHUTTER_POSITION, self)
manager.register_parser(mc.NS_APPLIANCE_ROLLERSHUTTER_STATE, self)
manager.register_parser(self, mn.Appliance_RollerShutter_Adjust)
manager.register_parser(self, mn.Appliance_RollerShutter_Config)
manager.register_parser(self, mn.Appliance_RollerShutter_Position)
manager.register_parser(self, mn.Appliance_RollerShutter_State)

async def async_added_to_hass(self):
await super().async_added_to_hass()
Expand All @@ -156,12 +154,12 @@ async def async_added_to_hass(self):
_attr = last_state.attributes # type: ignore
if not self._position_native_isgood:
# at this stage, the euristic on fw version doesn't say anything
if EXTRA_ATTR_POSITION_NATIVE in _attr:
if MLRollerShutter.ATTR_POSITION_NATIVE in _attr:
# this means we haven't detected (so far) a reliable 'native_position'
# so we restore the cover position (which was emulated)
self.extra_state_attributes[EXTRA_ATTR_POSITION_NATIVE] = _attr[
EXTRA_ATTR_POSITION_NATIVE
]
self.extra_state_attributes[
MLRollerShutter.ATTR_POSITION_NATIVE
] = _attr[MLRollerShutter.ATTR_POSITION_NATIVE]
if cover.ATTR_CURRENT_POSITION in _attr:
self.current_cover_position = _attr[
cover.ATTR_CURRENT_POSITION
Expand Down Expand Up @@ -209,8 +207,8 @@ async def async_set_cover_position(self, **kwargs):
else:
return # No-Op
if await self.async_request_position(position):
self._transition_end_unsub = schedule_async_callback(
self.hass, timeout, self._async_transition_end_callback
self._transition_end_unsub = self.manager.schedule_async_callback(
timeout, self._async_transition_end_callback
)

async def async_stop_cover(self, **kwargs):
Expand Down Expand Up @@ -284,10 +282,10 @@ async def async_request_position(self, position: int):
# in case the ns_multiple didn't succesfully kick-in we'll
# fallback to the legacy procedure
if await self.manager.async_request_ack(
mc.NS_APPLIANCE_ROLLERSHUTTER_POSITION,
mn.Appliance_RollerShutter_Position.name,
mc.METHOD_SET,
{
mc.KEY_POSITION: {
mn.Appliance_RollerShutter_Position.key: {
mc.KEY_CHANNEL: self.channel,
mc.KEY_POSITION: position,
}
Expand Down Expand Up @@ -342,13 +340,13 @@ def _parse_position(self, payload: dict):
self._position_native_isgood = True
self._position_native = None
self.is_closed = False
self.extra_state_attributes.pop(EXTRA_ATTR_POSITION_NATIVE, None)
self.extra_state_attributes.pop(MLRollerShutter.ATTR_POSITION_NATIVE, None)
self.supported_features |= MLCover.EntityFeature.SET_POSITION
self.current_cover_position = position
else:
self._position_native = position
self.is_closed = position == mc.ROLLERSHUTTER_POSITION_CLOSED
self.extra_state_attributes[EXTRA_ATTR_POSITION_NATIVE] = position
self.extra_state_attributes[MLRollerShutter.ATTR_POSITION_NATIVE] = position
if self.current_cover_position is None:
# only happening when we didn't restore state on devices
# which are likely not supporting native positioning
Expand Down Expand Up @@ -414,8 +412,7 @@ def _parse_state(self, payload: dict):
self.is_opening = not self.is_closing
if not self._transition_unsub:
# ensure we 'follow' cover movement
self._transition_unsub = schedule_async_callback(
self.hass,
self._transition_unsub = self.manager.schedule_async_callback(
PARAM_ROLLERSHUTTER_TRANSITION_POLL_TIMEOUT,
self._async_transition_callback,
)
Expand All @@ -431,12 +428,11 @@ async def _async_transition_callback(self):
This is a very 'gentle' polling happening only on HTTP when we're sure we're
not receiving MQTT updates. If device was configured for MQTT only we could
not setup this at all."""
self._transition_unsub = schedule_async_callback(
self.hass,
manager = self.manager
self._transition_unsub = manager.schedule_async_callback(
PARAM_ROLLERSHUTTER_TRANSITION_POLL_TIMEOUT,
self._async_transition_callback,
)
manager = self.manager
if (
manager.curr_protocol is CONF_PROTOCOL_HTTP and not manager._mqtt_active
) or (self._mrs_state == mc.ROLLERSHUTTER_STATE_IDLE):
Expand All @@ -445,28 +441,30 @@ async def _async_transition_callback(self):
await manager.async_multiple_requests_ack(
(
(
mc.NS_APPLIANCE_ROLLERSHUTTER_STATE,
mn.Appliance_RollerShutter_State.name,
mc.METHOD_GET,
{mc.KEY_STATE: p_channel_payload},
{mn.Appliance_RollerShutter_State.key: p_channel_payload},
),
(
mc.NS_APPLIANCE_ROLLERSHUTTER_POSITION,
mn.Appliance_RollerShutter_Position.name,
mc.METHOD_GET,
{mc.KEY_POSITION: p_channel_payload},
{
mn.Appliance_RollerShutter_Position.key: p_channel_payload
},
),
)
)
else:
await manager.async_request(
mc.NS_APPLIANCE_ROLLERSHUTTER_STATE,
mn.Appliance_RollerShutter_State.name,
mc.METHOD_GET,
{mc.KEY_STATE: p_channel_payload},
{mn.Appliance_RollerShutter_State.key: p_channel_payload},
)
if self._position_native_isgood:
await manager.async_request(
mc.NS_APPLIANCE_ROLLERSHUTTER_POSITION,
mn.Appliance_RollerShutter_Position.name,
mc.METHOD_GET,
{mc.KEY_POSITION: p_channel_payload},
{mn.Appliance_RollerShutter_Position.key: p_channel_payload},
)

async def _async_transition_end_callback(self):
Expand All @@ -482,7 +480,7 @@ class MLRollerShutterConfigNumber(me.MEDictChannelMixin, MLConfigNumber):

ns = mn.Appliance_RollerShutter_Config

device_scale = 1000
_attr_device_scale = 1000

# HA core entity attributes:
# these are ok for open/close durations
Expand All @@ -496,10 +494,10 @@ class MLRollerShutterConfigNumber(me.MEDictChannelMixin, MLConfigNumber):
def __init__(self, cover: "MLRollerShutter", key: str):
self._cover = cover
self.key_value = key
self.name = key
super().__init__(
cover.manager,
cover.channel,
f"config_{key}",
MLConfigNumber.DEVICE_CLASS_DURATION,
name=key,
)
10 changes: 6 additions & 4 deletions custom_components/meross_lan/devices/diffuser.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,18 +43,20 @@ def digest_init_diffuser(
"""

diffuser_light_handler = NamespaceHandler(
device, mc.NS_APPLIANCE_CONTROL_DIFFUSER_LIGHT, entity_class=MLDiffuserLight
device, mn.Appliance_Control_Diffuser_Light
)
diffuser_light_handler.register_entity_class(MLDiffuserLight)
for light_digest in digest.get(mc.KEY_LIGHT, []):
MLDiffuserLight(device, light_digest)

diffuser_spray_handler = NamespaceHandler(
device, mc.NS_APPLIANCE_CONTROL_DIFFUSER_SPRAY, entity_class=MLDiffuserSpray
device, mn.Appliance_Control_Diffuser_Spray
)
diffuser_spray_handler.register_entity_class(MLDiffuserSpray)
for spray_digest in digest.get(mc.KEY_SPRAY, []):
MLDiffuserSpray(device, spray_digest[mc.KEY_CHANNEL])

if mc.NS_APPLIANCE_CONTROL_DIFFUSER_SENSOR in device.descriptor.ability:
if mn.Appliance_Control_Diffuser_Sensor.name in device.descriptor.ability:
# former mod100 devices reported fake values for sensors, maybe the mod150 and/or a new firmware
# are supporting correct values so we implement them (#243)
def _handle_Appliance_Control_Diffuser_Sensor(header: dict, payload: dict):
Expand All @@ -79,7 +81,7 @@ def _handle_Appliance_Control_Diffuser_Sensor(header: dict, payload: dict):

NamespaceHandler(
device,
mc.NS_APPLIANCE_CONTROL_DIFFUSER_SENSOR,
mn.Appliance_Control_Diffuser_Sensor,
handler=_handle_Appliance_Control_Diffuser_Sensor,
)

Expand Down
Loading

0 comments on commit b7b251c

Please sign in to comment.