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

Fix for 2022.5.xx #145

Merged
merged 4 commits into from
May 4, 2022
Merged
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
2 changes: 1 addition & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// See https://aka.ms/vscode-remote/devcontainer.json for format details.
{
"image": "ludeeus/container:integration-debian",
"image": "ghcr.io/ludeeus/devcontainer/integration:stable",
"name": "Nordpool integration development",
"context": "..",
"appPort": [
Expand Down
4 changes: 2 additions & 2 deletions custom_components/nordpool/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

DOMAIN = "nordpool"
_LOGGER = logging.getLogger(__name__)
RANDOM_MINUTE = randint(0, 10)
RANDOM_MINUTE = randint(10, 30)
RANDOM_SECOND = randint(0, 59)
EVENT_NEW_DATA = "nordpool_update"
_CURRENCY_LIST = ["DKK", "EUR", "NOK", "SEK"]
Expand All @@ -28,7 +28,7 @@


NAME = DOMAIN
VERSION = "0.0.5"
VERSION = "0.0.6"
ISSUEURL = "https://github.com/custom-components/nordpool/issues"

STARTUP = f"""
Expand Down
93 changes: 52 additions & 41 deletions custom_components/nordpool/events.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
from datetime import datetime, timedelta
from typing import Any, Callable, Optional
from typing import Any, Optional
from collections.abc import Awaitable, Callable

from homeassistant.const import ATTR_NOW, EVENT_TIME_CHANGED
from homeassistant.core import CALLBACK_TYPE, Event, HomeAssistant, callback
#
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback, HassJob
from homeassistant.loader import bind_hass
from homeassistant.util import dt as dt_util
from homeassistant.helpers.event import (
async_track_time_interval,
async_track_point_in_utc_time,
)
from pytz import timezone

# For targeted patching in tests
time_tracker_utcnow = dt_util.utcnow


__ALL__ = ["stock", "async_track_time_change_in_tz"]


def stock(d):
"""convert datetime to stocholm time."""
Expand All @@ -17,71 +28,71 @@ def stock(d):
@bind_hass
def async_track_utc_time_change(
hass: HomeAssistant,
action: Callable[..., None],
hour: Optional[Any] = None,
minute: Optional[Any] = None,
second: Optional[Any] = None,
action: Callable[[datetime], Awaitable[None] | None],
hour: Any | None = None,
minute: Any | None = None,
second: Any | None = None,
tz: Optional[Any] = None,
) -> CALLBACK_TYPE:
"""Add a listener that will fire if time matches a pattern."""
# This is function is modifies to support timezones.

# We do not have to wrap the function with time pattern matching logic
# if no pattern given
if all(val is None for val in (hour, minute, second)):

@callback
def time_change_listener(event: Event) -> None:
"""Fire every time event that comes in."""
hass.async_run_job(action, event.data[ATTR_NOW])

return hass.bus.async_listen(EVENT_TIME_CHANGED, time_change_listener)

# Previously this relied on EVENT_TIME_FIRED
# which meant it would not fire right away because
# the caller would always be misaligned with the call
# time vs the fire time by < 1s. To preserve this
# misalignment we use async_track_time_interval here
return async_track_time_interval(hass, action, timedelta(seconds=1))

job = HassJob(action)
matching_seconds = dt_util.parse_time_expression(second, 0, 59)
matching_minutes = dt_util.parse_time_expression(minute, 0, 59)
matching_hours = dt_util.parse_time_expression(hour, 0, 23)

next_time = None

def calculate_next(now: datetime) -> None:
def calculate_next(now: datetime) -> datetime:
"""Calculate and set the next time the trigger should fire."""
nonlocal next_time

localized_now = now.astimezone(tz) if tz else now
next_time = dt_util.find_next_time_expression_time(
localized_now, matching_seconds, matching_minutes, matching_hours
ts_now = now.astimezone(tz) if tz else now
return dt_util.find_next_time_expression_time(
ts_now, matching_seconds, matching_minutes, matching_hours
)

# Make sure rolling back the clock doesn't prevent the timer from
# triggering.
last_now: Optional[datetime] = None
time_listener: CALLBACK_TYPE | None = None

@callback
def pattern_time_change_listener(event: Event) -> None:
def pattern_time_change_listener(_: datetime) -> None:
"""Listen for matching time_changed events."""
nonlocal next_time, last_now
nonlocal time_listener

now = event.data[ATTR_NOW]
now = time_tracker_utcnow()
hass.async_run_hass_job(job, now.astimezone(tz) if tz else now)

if last_now is None or now < last_now:
# Time rolled back or next time not yet calculated
calculate_next(now)
time_listener = async_track_point_in_utc_time(
hass,
pattern_time_change_listener,
calculate_next(now + timedelta(seconds=1)),
)

last_now = now
time_listener = async_track_point_in_utc_time(
hass, pattern_time_change_listener, calculate_next(dt_util.utcnow())
)

if next_time <= now:
hass.async_run_job(action, now.astimezone(tz) if tz else now)
calculate_next(now + timedelta(seconds=1))
@callback
def unsub_pattern_time_change_listener() -> None:
"""Cancel the time listener."""
assert time_listener is not None
time_listener()

# We can't use async_track_point_in_utc_time here because it would
# break in the case that the system time abruptly jumps backwards.
# Our custom last_now logic takes care of resolving that scenario.
return hass.bus.async_listen(EVENT_TIME_CHANGED, pattern_time_change_listener)
return unsub_pattern_time_change_listener


@callback
@bind_hass
def async_track_time_change_in_tz(
hass: HomeAssistant,
action: Callable[..., None],
action: Callable[[datetime], Awaitable[None] | None],
hour: Optional[Any] = None,
minute: Optional[Any] = None,
second: Optional[Any] = None,
Expand Down
2 changes: 1 addition & 1 deletion custom_components/nordpool/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@
"requirements": [
"nordpool>=0.2"
],
"version": "0.0.5"
"version": "0.0.6"
}
4 changes: 2 additions & 2 deletions custom_components/nordpool/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import homeassistant.helpers.config_validation as cv
import voluptuous as vol
from homeassistant.components.sensor import PLATFORM_SCHEMA
from homeassistant.const import CONF_REGION, EVENT_TIME_CHANGED
from homeassistant.const import CONF_REGION
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.template import Template, attach
Expand Down Expand Up @@ -416,7 +416,7 @@ def tomorrow_valid(self):
return self._api.tomorrow_valid()

async def _update_current_price(self) -> None:
""" update the current price (price this hour)"""
"""update the current price (price this hour)"""
local_now = dt_utils.now()

data = await self._api.today(self._area, self._currency)
Expand Down