Skip to content

Commit

Permalink
Merge pull request #122 from pyalarmdotcom/state-updates
Browse files Browse the repository at this point in the history
State updates
  • Loading branch information
elahd authored Jun 10, 2023
2 parents 6d5af6a + 98accf4 commit 45478e3
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 40 deletions.
67 changes: 38 additions & 29 deletions pyalarmdotcomajax/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
)
from pyalarmdotcomajax.websockets.client import WebSocketClient, WebSocketState

__version__ = "0.5.3"
__version__ = "0.5.4-alpha.1"

log = logging.getLogger(__name__)

Expand Down Expand Up @@ -386,6 +386,15 @@ async def async_send_command(
)
raise UnexpectedResponse

#
# SESSION FUNCTIONS
#

async def close_websession(self) -> None:
"""Close websession."""

await self._websession.close()

async def start_session_nudger(self) -> None:
"""Start task to nudge user sessions to keep from timing out."""

Expand All @@ -398,6 +407,34 @@ async def stop_session_nudger(self) -> None:
if self._session_timer:
await self._session_timer.stop()

async def keep_alive(self) -> None:
"""Keep session alive. Handle if not (optionally).
Should be called once per minute to keep session alive.
"""

reload_context_now = (
self._last_session_refresh + timedelta(milliseconds=self._session_refresh_interval_ms)
) < datetime.now()

seconds_remaining = (
self._last_session_refresh
+ (timedelta(milliseconds=self._session_refresh_interval_ms))
- datetime.now()
).total_seconds()

debug_message = "Sending keep alive signal. Time until session context refresh: {}".format(
"imminent" if reload_context_now else f"~ {round((seconds_remaining % 3600) // 60)} minutes."
)
log.debug(debug_message)

try:
if await self.is_logged_in(throw=True) and reload_context_now:
await self._reload_session_context()
except SessionTimeout:
log.info("User session expired. Logging back in.")
await self.async_login()

#
# WEBSOCKET FUNCTIONS
#
Expand Down Expand Up @@ -719,34 +756,6 @@ async def is_logged_in(self, throw: bool = False) -> bool:

return True

async def keep_alive(self) -> None:
"""Keep session alive. Handle if not (optionally).
Should be called once per minute to keep session alive.
"""

reload_context_now = (
self._last_session_refresh + timedelta(milliseconds=self._session_refresh_interval_ms)
) < datetime.now()

seconds_remaining = (
self._last_session_refresh
+ (timedelta(milliseconds=self._session_refresh_interval_ms))
- datetime.now()
).total_seconds()

debug_message = "Sending keep alive signal. Time until session context refresh: {}".format(
"imminent" if reload_context_now else f"~ {round((seconds_remaining % 3600) // 60)} minutes."
)
log.debug(debug_message)

try:
if await self.is_logged_in(throw=True) and reload_context_now:
await self._reload_session_context()
except SessionTimeout:
log.info("User session expired. Logging back in.")
await self.async_login()

#
#
#####################
Expand Down
43 changes: 32 additions & 11 deletions pyalarmdotcomajax/devices/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from collections.abc import Callable
from dataclasses import dataclass
from enum import Enum
from typing import Any, Final, TypedDict
from typing import Any, Final, Optional, TypedDict

from pyalarmdotcomajax.const import ATTR_DESIRED_STATE, ATTR_STATE
from pyalarmdotcomajax.exceptions import (
Expand Down Expand Up @@ -107,7 +107,7 @@ def __init__(
self._send_action_callback = send_action_callback
self._config_change_callback: Callable | None = config_change_callback

self.external_update_callback: list[Callable] = []
self.external_update_callback: list[tuple[Callable, Optional[str]]] = []

self.process_device_type_specific_data()

Expand Down Expand Up @@ -304,29 +304,50 @@ async def async_handle_external_attribute_change(
"""Update device attribute when notified of externally-triggered change."""

log.info(
f"{__name__} Got async update for {self.name} ({self.id_}){' from ' + source if source else ''} with"
f"{__name__} Got update for {self.name} ({self.id_}){' from ' + source if source else ''} with"
f" new {new_attributes}."
)

self.raw_attributes.update(new_attributes)

new_attrib_key = list(new_attributes.keys())[0]
new_attrib_value = list(new_attributes.values())[0]
if log.level == logging.DEBUG:
log_str = ""
for key, value in new_attributes.items():
if (current_value := self.raw_attributes.get(key)) != value:
log_str += f" | {str(key).upper()}:: [{current_value}] -> [{value}]"
if log_str:
log.debug(f"ATTRIBUTE NAME:: Current_Value -> Desired_Value{log_str}")

log.debug(f"Desired: [{new_attrib_value}] | Current: [{self.raw_attributes.get(new_attrib_key)}]")
# Trace logging for @catellie
log.debug(
f"{__name__} {self.name} ({self.id_}) has {len(self.external_update_callback)} external update"
" callbacks.)"
)

for external_callback, listener_name in self.external_update_callback:
# Trace logging for @catellie
log.debug(
f"{__name__} Calling external update callback for listener {listener_name} by"
f" {self.name} ({self.id_})"
)

for external_callback in self.external_update_callback:
external_callback()

def register_external_update_callback(self, callback: Callable) -> None:
def register_external_update_callback(self, callback: Callable, listener_name: str | None = None) -> None:
"""Register callback to be called when device state changes."""

self.external_update_callback.append(callback)
# Trace logging for @catellie
log.debug(f"Registering external update callback for {listener_name} with {self.name} ({self.id_})")

self.external_update_callback.append((callback, listener_name))

def unregister_external_update_callback(self, callback: Callable) -> None:
def unregister_external_update_callback(self, callback: Callable, listener_name: str | None = None) -> None:
"""Unregister callback to be called when device state changes."""

self.external_update_callback.remove(callback)
# Trace logging for @catellie
log.debug(f"Unregistering external update callback for {listener_name} with {self.name} ({self.id_})")

self.external_update_callback.remove((callback, listener_name))

# #
# PLACEHOLDERS
Expand Down

0 comments on commit 45478e3

Please sign in to comment.