Skip to content

Commit 5a5372d

Browse files
committed
Merge branch 'dev' of https://github.com/AppDaemon/appdaemon into dev
2 parents beadc5d + d103d55 commit 5a5372d

22 files changed

+1100
-713
lines changed

.github/workflows/build-deploy.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ jobs:
5656
working-directory: docs
5757
# Save the generated documentation as an artifact in Github
5858
- name: Upload documentation
59-
uses: actions/upload-artifact@v5
59+
uses: actions/upload-artifact@v6
6060
with:
6161
name: python-doc-package
6262
path: docs/_build/
@@ -87,7 +87,7 @@ jobs:
8787
- name: Build Python package
8888
run: python -m build
8989
- name: Upload Python package
90-
uses: actions/upload-artifact@v5
90+
uses: actions/upload-artifact@v6
9191
with:
9292
name: python-package
9393
path: dist/
@@ -111,12 +111,12 @@ jobs:
111111
- name: Checkout repository
112112
uses: actions/checkout@v6
113113
- name: Download Python package
114-
uses: actions/download-artifact@v6.0.0
114+
uses: actions/download-artifact@v7.0.0
115115
with:
116116
name: python-package
117117
path: dist/
118118
- name: Setup Docker buildx
119-
uses: docker/setup-buildx-action@v3.11.1
119+
uses: docker/setup-buildx-action@v3.12.0
120120
# Login against a Docker registry (only with a tag or push on `dev` branch)
121121
# https://github.com/docker/login-action
122122
- name: Log into Docker Hub
@@ -129,7 +129,7 @@ jobs:
129129
# https://github.com/docker/metadata-action
130130
- name: Extract Docker metadata
131131
id: meta
132-
uses: docker/metadata-action@v5.9.0
132+
uses: docker/metadata-action@v5.10.0
133133
with:
134134
images: ${{ env.IMAGE_NAME }}
135135
# Customize the generation of Docker `latest` tag

appdaemon/__main__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,6 @@ def __init__(self, args: argparse.Namespace) -> None:
296296
self.dep_manager = DependencyManager.from_app_directory(
297297
self.model.appdaemon.app_dir,
298298
exclude=self.model.appdaemon.exclude_dirs,
299-
config_suffix=self.model.appdaemon.ext,
300299
)
301300

302301
except Exception as e:

appdaemon/adapi.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2975,7 +2975,9 @@ async def run_at(
29752975
_, offset = resolve_time_str(start_str, now=now, location=self.AD.sched.location)
29762976
func = functools.partial(func, *args, repeat=True, offset=offset)
29772977
case _:
2978-
start = await self.AD.sched.parse_datetime(start, aware=True)
2978+
# For run_at, always schedule for the next occurrence (today=False)
2979+
# This ensures that times in the past are scheduled for tomorrow
2980+
start = await self.AD.sched.parse_datetime(start, aware=True, today=False)
29792981
func = functools.partial(
29802982
self.AD.sched.insert_schedule,
29812983
name=self.name,

appdaemon/app_management.py

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121

2222
from pydantic import ValidationError
2323

24-
2524
from appdaemon.dependency import DependencyResolutionFail, find_all_dependents, get_full_module_name
2625
from appdaemon.dependency_manager import DependencyManager
2726
from appdaemon.models.config import AllAppConfig, AppConfig, GlobalModule
@@ -33,9 +32,9 @@
3332
from .models.internal.app_management import LoadingActions, ManagedObject, UpdateActions, UpdateMode
3433

3534
if TYPE_CHECKING:
36-
from .appdaemon import AppDaemon
37-
from .adbase import ADBase
3835
from .adapi import ADAPI
36+
from .adbase import ADBase
37+
from .appdaemon import AppDaemon
3938
from .plugin_management import PluginBase
4039

4140
T = TypeVar("T")
@@ -87,7 +86,6 @@ class AppManagement:
8786

8887
def __init__(self, ad: "AppDaemon"):
8988
self.AD = ad
90-
self.ext = self.AD.config.ext
9189
self.logger = ad.logging.get_child(self.name)
9290
self.error = ad.logging.get_error()
9391
self.diag = ad.logging.get_diag()
@@ -527,12 +525,11 @@ async def safe_create(self: "AppManagement"):
527525
module_name,
528526
)
529527

530-
if (pin := cfg.pin_thread) and pin >= self.AD.threading.total_threads:
528+
if (pin := cfg.pin_thread) is not None and pin >= self.AD.threading.total_threads:
531529
raise ade.PinOutofRange(pin_thread=pin, total_threads=self.AD.threading.total_threads)
532-
elif (obj := self.objects.get(app_name)) and obj.pin_thread is not None:
530+
if (obj := self.objects.get(app_name)) and obj.pin_thread is not None:
533531
pin = obj.pin_thread
534-
else:
535-
pin = -1
532+
# else pin is already None from cfg.pin_thread
536533

537534
# This module should already be loaded and stored in sys.modules
538535
mod_obj = await utils.run_in_executor(self, importlib.import_module, module_name)
@@ -585,7 +582,7 @@ def add_plugin_object(self, name: str, object: "PluginBase") -> None:
585582
type="plugin",
586583
object=object,
587584
pin_app=False,
588-
pin_thread=-1,
585+
pin_thread=None,
589586
running=False,
590587
)
591588

@@ -1047,8 +1044,8 @@ def get_app_config_files(self) -> set[Path]:
10471044
return set(
10481045
utils.recursive_get_files(
10491046
base=self.AD.app_dir.resolve(),
1050-
suffix=self.ext,
1051-
exclude=set(self.AD.exclude_dirs),
1047+
suffix={".yaml", ".toml"},
1048+
exclude=set(self.AD.exclude_dirs) | {"ruff.toml", "pyproject.toml", "secrets.yaml"},
10521049
)
10531050
)
10541051

@@ -1323,7 +1320,7 @@ def create_app(self, app: str = None, **kwargs):
13231320
return False
13241321

13251322
app_directory: Path = self.AD.app_dir / kwargs.pop("app_dir", "ad_apps")
1326-
app_file: Path = app_directory / kwargs.pop("app_file", f"{app}{self.ext}")
1323+
app_file: Path = app_directory / kwargs.pop("app_file", f"{app}{self.AD.config.ext}")
13271324
app_directory = app_file.parent # in case the given app_file is multi level
13281325

13291326
try:

appdaemon/callbacks.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ async def get_callback_entries(self, type="all"):
8080
)
8181
callbacks[name][str(uuid_)]["pin_thread"] = (
8282
self.callbacks[name][uuid_]["pin_thread"]
83-
if self.callbacks[name][uuid_]["pin_thread"] != -1
83+
if self.callbacks[name][uuid_]["pin_thread"] is not None
8484
else "None"
8585
)
8686
return callbacks

appdaemon/dependency_manager.py

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
from abc import ABC
22
from copy import deepcopy
33
from dataclasses import InitVar, dataclass, field
4-
from functools import partial
54
from pathlib import Path
65
from typing import Iterable
76

@@ -16,7 +15,7 @@ class Dependencies(ABC):
1615
"""Wraps an instance of ``FileCheck`` with a corresponding set of dependency graphs."""
1716

1817
files: FileCheck = field(repr=False)
19-
ext: str = field(init=False) # this has to be defined by the children classes
18+
ext: str | set[str] = field(init=False) # this has to be defined by the children classes
2019
dep_graph: dict[str, set[str]] = field(init=False)
2120
rev_graph: dict[str, set[str]] = field(init=False)
2221
bad_files: set[tuple[Path, float]] = field(default_factory=set, init=False)
@@ -39,7 +38,13 @@ def refresh_dep_graph(self):
3938

4039
@classmethod
4140
def from_path(cls, path: Path):
42-
return cls.from_paths(path.rglob(f"*{cls.ext}"))
41+
return cls.from_paths(
42+
utils.recursive_get_files(
43+
base=path,
44+
suffix={".yaml", ".toml"},
45+
exclude={"ruff.toml", "pyproject.toml", "secrets.yaml"},
46+
)
47+
)
4348

4449
@classmethod
4550
def from_paths(cls, paths: Iterable[Path]):
@@ -90,7 +95,6 @@ def modules_to_delete(self) -> list[str]:
9095
@dataclass
9196
class AppDeps(Dependencies):
9297
app_config: AllAppConfig = field(init=False, repr=False)
93-
ext: str = ".yaml"
9498

9599
def __post_init__(self):
96100
self.app_config = AllAppConfig.from_config_files(self.files)
@@ -157,7 +161,6 @@ def from_app_directory(
157161
cls,
158162
app_dir: Path,
159163
exclude: str | Iterable[str] | None = None,
160-
config_suffix: str = ".yaml",
161164
) -> "DependencyManager":
162165
"""Creates a new instance of the dependency manager from the given app directory"""
163166
match exclude:
@@ -168,12 +171,14 @@ def from_app_directory(
168171
case _:
169172
exclude_set = set(exclude)
170173

171-
get_files = partial(utils.recursive_get_files, base=app_dir, exclude=exclude_set)
172174
return cls(
173-
# python_files=get_files(suffix=".py"),
174175
python_files=list(),
175-
config_files=get_files(suffix=config_suffix)
176-
) # fmt: skip
176+
config_files=utils.recursive_get_files(
177+
base=app_dir,
178+
suffix={".yaml", ".toml"},
179+
exclude=exclude_set,
180+
)
181+
)
177182

178183
@property
179184
def app_config_files(self) -> set[Path]:

0 commit comments

Comments
 (0)