Skip to content

Commit 3e71ce0

Browse files
committed
fixes for typing issues
1 parent 0cc7ccc commit 3e71ce0

File tree

8 files changed

+100
-54
lines changed

8 files changed

+100
-54
lines changed

appdaemon/adapi.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
from appdaemon.logging import Logging
2424
from appdaemon.models.config.app import AppConfig
2525
from appdaemon.parse import resolve_time_str
26-
from appdaemon.state import StateCallback
26+
from appdaemon.state import StateCallbackType
2727

2828
T = TypeVar("T")
2929

@@ -787,7 +787,7 @@ def get_ad_version() -> str:
787787
async def add_entity(
788788
self,
789789
entity_id: str,
790-
state: str | dict[str, Any],
790+
state: Any,
791791
attributes: dict | None = None,
792792
namespace: str | None = None,
793793
) -> None:
@@ -1402,7 +1402,7 @@ async def deregister_route(self, handle: str) -> None:
14021402
@utils.sync_decorator
14031403
async def listen_state(
14041404
self,
1405-
callback: StateCallback,
1405+
callback: StateCallbackType,
14061406
entity_id: str | None,
14071407
namespace: str | None = None,
14081408
new: str | Callable[[Any], bool] | None = None,
@@ -1421,7 +1421,7 @@ async def listen_state(
14211421
@utils.sync_decorator
14221422
async def listen_state(
14231423
self,
1424-
callback: StateCallback,
1424+
callback: StateCallbackType,
14251425
entity_id: Iterable[str],
14261426
namespace: str | None = None,
14271427
new: str | Callable[[Any], bool] | None = None,
@@ -1439,7 +1439,7 @@ async def listen_state(
14391439
@utils.sync_decorator
14401440
async def listen_state(
14411441
self,
1442-
callback: StateCallback,
1442+
callback: StateCallbackType,
14431443
entity_id: str | Iterable[str] | None = None,
14441444
namespace: str | None = None,
14451445
new: str | Callable[[Any], bool] | None = None,

appdaemon/entity.py

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33
from collections import defaultdict
44
from collections.abc import Callable, Iterable
55
from dataclasses import dataclass, field
6-
from datetime import timedelta
6+
from datetime import datetime, timedelta
77
from logging import Logger
88
from typing import TYPE_CHECKING, Any, overload
99

1010
from appdaemon import utils
1111

1212
from .exceptions import TimeOutException
13-
from .state import StateCallback
13+
from .state import StateCallbackType
1414

1515
if TYPE_CHECKING:
1616
from appdaemon import ADAPI
@@ -28,8 +28,6 @@ class Entity:
2828
name: str = field(init=False)
2929

3030
adapi: "ADAPI"
31-
namespace: str
32-
entity_id: str | None
3331
_async_events: dict[str, asyncio.Event] = field(default_factory=lambda: defaultdict(asyncio.Event))
3432
# states_attrs = EntityAttrs()
3533

@@ -115,7 +113,7 @@ async def set_state(
115113
116114
"""
117115
self.logger.debug("set state: %s, %s from %s", self.entity_id, kwargs, self.name)
118-
return await self.adapi.set_state(
116+
return await self.adapi.set_state( # pyright: ignore[reportGeneralTypeIssues]
119117
entity_id=self.entity_id,
120118
namespace=self.namespace,
121119
state=state,
@@ -175,7 +173,7 @@ async def get_state(
175173
176174
"""
177175
self.logger.debug("get state: %s, %s from %s", self.entity_id, self.namespace, self.name)
178-
return await self.adapi.get_state(
176+
return await self.adapi.get_state( # pyright: ignore[reportGeneralTypeIssues]
179177
namespace=self.namespace,
180178
entity_id=self.entity_id,
181179
attribute=attribute,
@@ -187,7 +185,7 @@ async def get_state(
187185
@utils.sync_decorator
188186
async def listen_state(
189187
self,
190-
callback: StateCallback,
188+
callback: StateCallbackType,
191189
new: str | Callable[[Any], bool] | None = None,
192190
old: str | Callable[[Any], bool] | None = None,
193191
duration: str | int | float | timedelta | None = None,
@@ -197,10 +195,11 @@ async def listen_state(
197195
oneshot: bool = False,
198196
pin: bool | None = None,
199197
pin_thread: int | None = None,
198+
**kwargs: Any,
200199
) -> str: ...
201200

202201
@utils.sync_decorator
203-
async def listen_state(self, callback: StateCallback, **kwargs: Any) -> str:
202+
async def listen_state(self, callback: StateCallbackType, **kwargs: Any) -> str:
204203
"""Registers a callback to react to state changes.
205204
206205
This function allows the user to register a callback for a wide variety of state changes.
@@ -301,15 +300,15 @@ async def listen_state(self, callback: StateCallback, **kwargs: Any) -> str:
301300
302301
>>> self.handle = self.my_entity.listen_state(self.my_callback, new="on", duration=60, immediate=True)
303302
"""
304-
return await self.adapi.listen_state(
303+
return await self.adapi.listen_state( # pyright: ignore[reportGeneralTypeIssues]
305304
callback,
306305
entity_id=self.entity_id,
307306
namespace=self.namespace,
308307
**kwargs,
309308
)
310309

311310
@utils.sync_decorator
312-
async def add(self, state: str | int | float = None, attributes: dict = None) -> None:
311+
async def add(self, state: str | int | float | None = None, attributes: dict[str, Any] | None = None) -> None:
313312
"""Adds a non-existent entity, by creating it within a namespaces.
314313
315314
It should be noted that this api call, is mainly for creating AD internal entities.
@@ -383,9 +382,9 @@ async def call_service(
383382
async def wait_state(
384383
self,
385384
state: Any,
386-
attribute: str | int = None,
385+
attribute: str | None = None,
387386
duration: int | float = 0,
388-
timeout: int | float = None,
387+
timeout: int | float | None = None,
389388
) -> None:
390389
"""Used to wait for the state of an entity's attribute
391390
@@ -419,7 +418,7 @@ async def wait_state(
419418
async_event = self._async_events[wait_id]
420419

421420
try:
422-
handle = await self.listen_state(
421+
handle = await self.listen_state( # pyright: ignore[reportGeneralTypeIssues]
423422
self.entity_state_changed,
424423
new=state,
425424
attribute=attribute,
@@ -556,7 +555,7 @@ def entity_id(self) -> str:
556555
return self._entity_id
557556

558557
@entity_id.setter
559-
def entity_id(self, new: str) -> str:
558+
def entity_id(self, new: str) -> None:
560559
"""Get the entity's entity_id"""
561560
self._entity_id = new
562561
try:
@@ -574,7 +573,6 @@ def state(self) -> Any:
574573
@property
575574
def namespace(self) -> str:
576575
"""Get the entity's namespace name"""
577-
578576
return self._namespace
579577

580578
@namespace.setter
@@ -592,15 +590,15 @@ def friendly_name(self) -> str:
592590
return self.attributes.get("friendly_name", self.entity_id)
593591

594592
@property
595-
def last_changed(self) -> str:
593+
def last_changed(self) -> str | None:
596594
"""Get the entity's last changed time in iso format"""
597595
return self._simple_state.get("last_changed")
598596

599597
@property
600598
def last_changed_delta(self) -> timedelta | None:
601599
"""A timedelta object representing the time since the entity was last changed"""
602600
if time_str := self.last_changed:
603-
compare = self.adapi.parse_datetime(time_str, aware=True)
601+
compare = datetime.fromisoformat(time_str)
604602
now = self.AD.sched.get_now_sync()
605603
return (now - compare)
606604

appdaemon/exceptions.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
44
"""
55
import asyncio
6-
from contextlib import contextmanager
76
import functools
87
import inspect
98
import json
@@ -12,6 +11,7 @@
1211
import traceback
1312
from abc import ABC
1413
from collections.abc import Iterable
14+
from contextlib import contextmanager
1515
from dataclasses import dataclass
1616
from logging import Logger
1717
from pathlib import Path
@@ -344,8 +344,9 @@ class BadAppConfigFile(AppDaemonException):
344344
path: Path
345345

346346

347+
@dataclass
347348
class TimeOutException(AppDaemonException):
348-
pass
349+
msg: str
349350

350351

351352
class StartupAbortedException(AppDaemonException):

appdaemon/parse.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ def parse_datetime(
313313
ISO 8601 datetime strings, which helps with testing.
314314
location (Location, optional): Location used for sunrise/sunset parsing. This is needed in order to parse
315315
sunset/sunrise times from the input.
316-
today (bool, optional): If `True`, forces the result to have the same date as the `now` datetime. `False` is
316+
today (bool, optional): If `True`, forces the result to have the same date as the `now` datetime. `False` is
317317
effectively equivalent to `next`. The default value is `None`, which doesn't try to coerce the output at
318318
all. This results in slightly different date results for different input types. For example, a time string
319319
will be given the same date as the one in the `now` datetime, but a sun event string will be the datetime

appdaemon/scheduler.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ class Scheduler:
4141
diag: Logger
4242

4343
schedule: dict[str, dict[str, Any]]
44+
location: Location
4445

4546
name: str = "_scheduler"
4647
active_event: asyncio.Event
@@ -54,7 +55,6 @@ def __init__(self, ad: "AppDaemon"):
5455
self.last_fired = None
5556
self.sleep_task = None
5657
self.timer_resetted = False
57-
self.location = None
5858
self.schedule = {}
5959

6060
self.active_event = asyncio.Event()
@@ -854,10 +854,25 @@ async def parse_datetime(
854854
today: bool | None = None,
855855
days_offset: int = 0
856856
) -> datetime: # fmt: skip
857-
now = await self.get_now()
857+
"""Parse a variety of inputs into a datetime object.
858+
859+
Args:
860+
input_ (str | time | datetime): The input to parse. Can be a string, time, or datetime object.
861+
aware (bool, optional): If `False`, the resulting datetime will be naive (without timezone). Defaults to
862+
`True`.
863+
today (bool, optional): If `True`, forces the result to have the same date as the `now` datetime. `False` is
864+
effectively equivalent to `next`. The default value is `None`, which doesn't try to coerce the output at
865+
all. This results in slightly different date results for different input types. For example, a time string
866+
will be given the same date as the one in the `now` datetime, but a sun event string will be the datetime
867+
of the next one.
868+
days_offset (int, optional): Number of days to offset from the current date for sunrise/sunset parsing. If
869+
this is negative, this will unset the `today` argument, which allows the result to be in the past.
870+
"""
871+
# Need to force timezone during time-travel mode
872+
now = (await self.get_now()).astimezone(self.AD.tz)
858873
return parse.parse_datetime(
859874
input_=input_,
860-
now=now.astimezone(self.AD.tz), # Need to force timezone during time-travel mode
875+
now=now,
861876
location=self.location,
862877
today=today,
863878
days_offset=days_offset,

0 commit comments

Comments
 (0)