Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#263 observe deep sleep and keep sensors always available #264

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions hatasmota/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@
# Power units
POWER_WATT: Final = "W"
REACTIVE_POWER = "VAr"
POWER_FACTOR: Final = "Cos φ"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems unrelated to this PR?


# Voltage units
VOLT: Final = "V"
Expand Down
2 changes: 2 additions & 0 deletions hatasmota/device_status.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
get_topic_stat_status,
get_topic_tele_state,
get_topic_tele_will,
get_topic_sleep_state,
get_value_by_path,
)

Expand Down Expand Up @@ -98,6 +99,7 @@ def from_discovery_message(cls, config: dict) -> TasmotaDeviceStatusConfig:
poll_payload=str(STATUS_TOPICS.get(sensor)),
poll_topic=get_topic_command_status(config),
availability_topic=get_topic_tele_will(config),
sleep_state_topic=get_topic_sleep_state(config),
availability_offline=config_get_state_offline(config),
availability_online=config_get_state_online(config),
state_topic=get_topic_tele_state(config),
Expand Down
54 changes: 49 additions & 5 deletions hatasmota/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
from dataclasses import dataclass
import logging
from typing import Any
import time
import json
from .utils import (
get_value_by_path,
)

from .mqtt import ReceiveMessage, TasmotaMQTTClient

Expand Down Expand Up @@ -36,6 +41,7 @@ class TasmotaAvailabilityConfig(TasmotaEntityConfig):
availability_topic: str
availability_offline: str
availability_online: str
sleep_state_topic: str


class TasmotaEntity:
Expand Down Expand Up @@ -98,27 +104,65 @@ def __init__(self, **kwds: Any):
self._on_availability_callback: Callable[
[bool], Coroutine[Any, Any, None]
] | None = None
self.uses_deep_sleep = False
self.deep_sleep_interval = None
self.last_up=0
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please give this a better name, maybe last_up_timestamp, timestamp_last_up or something like that

self.available=None
super().__init__(**kwds)

def get_availability_topics(self) -> dict:
"""Return MQTT topics to subscribe to for availability state."""

async def availability_message_received(msg: ReceiveMessage) -> None:
"""Handle a new received MQTT availability message."""
last_up_retain=self.last_up
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This variable is never used

if msg.payload == self._cfg.availability_online:
if self.last_up and self.deep_sleep_interval is None:
self.deep_sleep_interval = int(time.time() - self.last_up)
await self.poll_status()
else:
self.last_up=time.time()
available=self.available
if msg.payload == self._cfg.availability_online:
available=True
if msg.payload == self._cfg.availability_offline and not(self.uses_deep_sleep):
if not(self.uses_deep_sleep):
available=False
else:
_LOGGER.debug("inhibit deep sleep %s", msg.topic)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we start a timer here, which if it expires sets availability to False and calls the availability callback?

You can use this timer class:

class Timer:
"""Simple timer."""
def __init__(
self, timeout: float, callback: Callable[[], Coroutine[Any, Any, None]]
):
self._timeout = timeout
self._callback = callback
self._task = asyncio.ensure_future(self._job())
async def _job(self) -> None:
await asyncio.sleep(self._timeout)
await self._callback()
def cancel(self) -> None:
"""Cancel the timer."""
self._task.cancel()

if not self._on_availability_callback:
self.available=available
return
if msg.payload == self._cfg.availability_online:
await self._on_availability_callback(True)
if msg.payload == self._cfg.availability_offline:
await self._on_availability_callback(False)

if self.available != available:
await self._on_availability_callback(available)
self.available=available

async def sleep_state_message_received(msg: ReceiveMessage) -> None:
"""Handle state messages to indicate deep sleep."""
#try:
# payload = json.loads(msg.payload)
#except json.decoder.JSONDecodeError:
# return
_LOGGER.debug("sleep state %s -> %s", msg.topic, msg.payload)
state = get_value_by_path(msg.payload, ["StatusPRM","RestartReason"])
if state is not None:
state=str(state).lower()
if state.startswith('deep sleep'):
if not(self.uses_deep_sleep):
_LOGGER.debug("switching to deep sleep mode %s", msg.topic)
self.deep_sleep_interval=None
self.uses_deep_sleep=True
Comment on lines +140 to +154
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of detecting a device's support for deep sleep when it wakes up from deep sleep, we should detect it from the device's discovery message: https://github.com/arendst/Tasmota/pull/19134/files#diff-fe970b21f48758677cb799d64f0ac866cbbe0aa48eb3e0f6aaa52c22ef73c735R189
Also


topics = {
"availability_topic": {
"event_loop_safe": True,
"msg_callback": availability_message_received,
"topic": self._cfg.availability_topic,
},
"sleep_state_topic": {
"event_loop_safe": True,
"msg_callback": sleep_state_message_received,
"topic": self._cfg.sleep_state_topic,
}
}
return topics
Expand Down
2 changes: 2 additions & 0 deletions hatasmota/fan.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
get_topic_stat_result,
get_topic_tele_state,
get_topic_tele_will,
get_topic_sleep_state,
get_value_by_path,
)

Expand Down Expand Up @@ -56,6 +57,7 @@ def from_discovery_message(cls, config: dict, platform: str) -> TasmotaFanConfig
poll_payload="",
poll_topic=get_topic_command_state(config),
availability_topic=get_topic_tele_will(config),
sleep_state_topic=get_topic_sleep_state(config),
availability_offline=config_get_state_offline(config),
availability_online=config_get_state_online(config),
command_topic=get_topic_command(config),
Expand Down
2 changes: 2 additions & 0 deletions hatasmota/light.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
get_topic_stat_result,
get_topic_tele_state,
get_topic_tele_will,
get_topic_sleep_state,
get_value_by_path,
)

Expand Down Expand Up @@ -154,6 +155,7 @@ def from_discovery_message(
poll_payload="",
poll_topic=get_topic_command_state(config),
availability_topic=get_topic_tele_will(config),
sleep_state_topic=get_topic_sleep_state(config),
availability_offline=config_get_state_offline(config),
availability_online=config_get_state_online(config),
dimmer_cmd=dimmer_cmd,
Expand Down
2 changes: 2 additions & 0 deletions hatasmota/relay.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
get_topic_command_state,
get_topic_stat_result,
get_topic_tele_state,
get_topic_sleep_state,
get_topic_tele_will,
)

Expand Down Expand Up @@ -56,6 +57,7 @@ def from_discovery_message(
poll_payload="",
poll_topic=get_topic_command_state(config),
availability_topic=get_topic_tele_will(config),
sleep_state_topic=get_topic_sleep_state(config),
availability_offline=config_get_state_offline(config),
availability_online=config_get_state_online(config),
command_topic=get_topic_command(config),
Expand Down
2 changes: 2 additions & 0 deletions hatasmota/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@
get_topic_stat_status,
get_topic_tele_sensor,
get_topic_tele_will,
get_topic_sleep_state,
get_value_by_path,
)

Expand Down Expand Up @@ -268,6 +269,7 @@ def from_discovery_message(
poll_payload="10",
poll_topic=get_topic_command_status(device_config),
availability_topic=get_topic_tele_will(device_config),
sleep_state_topic=get_topic_sleep_state(device_config),
availability_offline=config_get_state_offline(device_config),
availability_online=config_get_state_online(device_config),
quantity=quantity,
Expand Down
2 changes: 2 additions & 0 deletions hatasmota/shutter.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
get_topic_stat_status,
get_topic_tele_sensor,
get_topic_tele_will,
get_topic_sleep_state,
get_value_by_path,
)

Expand Down Expand Up @@ -75,6 +76,7 @@ def from_discovery_message(
poll_payload="10",
poll_topic=get_topic_command_status(config),
availability_topic=get_topic_tele_will(config),
sleep_state_topic=get_topic_sleep_state(config),
availability_offline=config_get_state_offline(config),
availability_online=config_get_state_online(config),
command_topic=get_topic_command(config),
Expand Down
2 changes: 2 additions & 0 deletions hatasmota/status_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
get_topic_command_status,
get_topic_stat_status,
get_topic_tele_state,
get_topic_sleep_state,
get_topic_tele_will,
get_value_by_path,
)
Expand Down Expand Up @@ -176,6 +177,7 @@ def from_discovery_message(
poll_payload=str(STATUS_TOPICS.get(sensor)),
poll_topic=get_topic_command_status(config),
availability_topic=get_topic_tele_will(config),
sleep_state_topic=get_topic_sleep_state(config),
availability_offline=config_get_state_offline(config),
availability_online=config_get_state_online(config),
sensor=sensor,
Expand Down
2 changes: 2 additions & 0 deletions hatasmota/switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
get_topic_stat_status,
get_topic_tele_sensor,
get_topic_tele_will,
get_topic_sleep_state,
get_value_by_path,
)

Expand Down Expand Up @@ -273,6 +274,7 @@ def from_discovery_message(
poll_payload="10",
poll_topic=get_topic_command_status(config),
availability_topic=get_topic_tele_will(config),
sleep_state_topic=get_topic_sleep_state(config),
availability_offline=config_get_state_offline(config),
availability_online=config_get_state_online(config),
off_delay=off_delay,
Expand Down
4 changes: 4 additions & 0 deletions hatasmota/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,10 @@ def get_topic_stat_status(config: ConfigType, idx: int | None = None) -> str:
return _get_topic_stat(config) + "STATUS"
return _get_topic_stat(config) + f"STATUS{idx}"

def get_topic_sleep_state(config: ConfigType):
topic = get_topic_stat_status(config, 1)
_LOGGER.debug("Topic for sleep state %s", topic)
return topic

def get_topic_stat_switch(config: ConfigType, idx: int) -> str:
"""Get topic for tele state."""
Expand Down
Loading