Compare commits

...

11 Commits

Author SHA1 Message Date
John Lancaster
7ba1abec84 pyright config 2025-06-20 08:50:54 -05:00
John Lancaster
475bdb9dd9 simple fixes 2025-06-20 08:50:43 -05:00
John Lancaster
ba043554dc linting 2025-06-20 08:30:46 -05:00
John Lancaster
bd5c7ee339 updated ruff rules 2025-06-20 08:29:28 -05:00
John Lancaster
ce4ede51b1 removed hello stub 2025-06-20 08:21:28 -05:00
John Lancaster
edee038a9f added ruff.toml 2025-06-20 08:21:01 -05:00
John Lancaster
7025bcde99 changed to uv 2025-06-20 08:20:50 -05:00
John Lancaster
ad34a6e919 cleanup 2025-06-20 08:20:26 -05:00
John Lancaster
99936f2c85 cleaned up simple 2025-06-20 08:20:19 -05:00
John Lancaster
4655b93b64 added malformed 2025-06-05 22:38:16 -05:00
John Lancaster
cbd91c8873 added some more examples 2025-06-05 22:37:56 -05:00
25 changed files with 201 additions and 80 deletions

8
.gitignore vendored
View File

@@ -4,7 +4,15 @@ __pycache__
.python-version
.venv
conf/compiled
conf/dashboards
conf/namespaces
conf/www
conf/appdaemon.yaml
conf/secrets.yaml
log
*.log
*.db
*.js
*cache*

View File

@@ -4,5 +4,4 @@ logger = logging.getLogger('AppDaemon.Perimeter')
logger.info('Imported statemachine')
class StateMachine:
...
class StateMachine: ...

View File

@@ -5,8 +5,8 @@ from dataclasses import dataclass
class Rule1:
state: str
@dataclass
class Rule2:
value: int
other: str = "default" # test changing this
other: str = 'default' # test changing this

View File

@@ -2,5 +2,5 @@ from appdaemon.adapi import ADAPI
class GrandParent(ADAPI):
def initialize(self):
def initialize(self) -> None:
self.log(f'{self.__class__.__name__} Initialized')

View File

@@ -2,5 +2,5 @@ from appdaemon.adapi import ADAPI
class Parent(ADAPI):
def initialize(self):
def initialize(self) -> None:
self.log(f'{self.__class__.__name__} Initialized')

View File

@@ -1,7 +1,6 @@
from child import Child
class Sibling(Child):
def initialize(self):
def initialize(self) -> None:
self.log(f'{self.__class__.__name__} Initialized')

View File

@@ -1,5 +1,4 @@
from dataclasses import dataclass, field
from typing import List
from .base import FoodItem
from .eggs import Eggs
@@ -12,7 +11,7 @@ class GreenEggs(Eggs):
@dataclass
class Menu:
dishes: List[type[FoodItem]] = field(init=False)
dishes: list[type[FoodItem]] = field(init=False)
def __post_init__(self):
self.dishes = [GreenEggs, Ham]

View File

@@ -13,4 +13,5 @@ class Utensil(Enum):
POT = auto()
PAN = auto()
GLOBAL_VAR = Utensil.FORK

View File

@@ -5,7 +5,7 @@ from food.menu import Menu
class Restaurant(ADAPI):
menu: Menu
def initialize(self):
def initialize(self) -> None:
self.log(f'{self.__class__.__name__} initialized')
self.menu = Menu()

28
conf/apps/globals.py Normal file
View File

@@ -0,0 +1,28 @@
import logging
from enum import Enum
LOGGER = logging.getLogger('AppDaemon._globals')
GLOBAL_VAR = 'Hello, World!'
def global_function() -> None:
LOGGER.info('This is a global function.')
class GlobalClass:
def __init__(self) -> None:
self.value = 'This is a global class instance.'
def display(self) -> None:
LOGGER.info(self.value)
class ModeSelect(Enum):
MODE_A = 'mode_a'
MODE_B = 'mode_b'
MODE_C = 'mode_c'
GLOBAL_MODE = ModeSelect.MODE_C

View File

@@ -3,8 +3,9 @@ from typing import Any
logger = logging.getLogger('AppDaemon.Perimeter')
class HAL:
def __init__(self, *args: Any, **kwargs: Any):
def __init__(self, *args: Any, **kwargs: Any) -> None:
self.args = args
self.kwargs = kwargs
logger.info('Logging from HAL')

View File

@@ -1,8 +1,9 @@
CONSTANTS = {
'A': 1,
'B': 2,
'C': 3
'C': 3,
}
def utility_function():
return 123, 456

View File

@@ -1,36 +0,0 @@
# scratch1:
# module: "scratch"
# class: "Scratch"
# priority: 1
# dependencies:
# - scratch2
dummy:
module: dummy
class: Dummy
dependencies:
- dev
eboon:
module: eboon
class: Eboon
dev:
module: notifications
class: NotifyDev
# scratch3:
# module: "scratch"
# class: "Scratch"
# scratch4:
# module: "scratch"
# class: "Scratch"
# scratch5:
# module: "scratch"
# class: "Scratch"
# scratch6:
# module: "scratch"
# class: "Scratch"
# scratch7:
# module: "scratch"
# class: "Scratch"

View File

@@ -0,0 +1,12 @@
from appdaemon.adapi import ADAPI
from globals import GLOBAL_MODE, GLOBAL_VAR
class AppA(ADAPI):
def initialize(self) -> None:
self.log(f'{self.__class__.__name__} Initialized')
self.log(GLOBAL_VAR)
self.log(f'Global mode is set to: {GLOBAL_MODE.value}')
def terminate(self) -> None: ...

View File

@@ -0,0 +1,11 @@
from appdaemon.adapi import ADAPI
from globals import GLOBAL_MODE, GLOBAL_VAR
class AppB(ADAPI):
def initialize(self) -> None:
self.log(f'{self.__class__.__name__} Initialized')
self.log(GLOBAL_VAR)
self.log(f'Global mode is set to: {GLOBAL_MODE.value}')
def terminate(self) -> None: ...

View File

@@ -1,10 +1,27 @@
hello_world:
module: hello
class: HelloWorld
extra: abc123
# disable: true
simple_app:
module: simple
class: SimpleApp
extra: 1234
dependencies:
- hello_world
priority: 10
# dependencies:
# - hello_world
base_app:
module: simple
class: BaseApp
# AppA:
# module: app_a
# class: AppA
# dependencies:
# - AppB # This is only set to demonstrate forcing it to load after AppB
# AppB:
# module: app_b
# class: AppB

View File

@@ -2,13 +2,23 @@ from typing import Any
from appdaemon.adapi import ADAPI
# fake_name.get()
# if:
# OUtisde&sdf'asdfasdf'<txF> asdfasdfom some html</.dstd>
# fake/
# SimpleApp
class HelloWorld(ADAPI):
def initialize(self):
def initialize(self) -> None:
self.log(f'{self.__class__.__name__} Initialized')
self.register_service("my_domain/my_exciting_service", self.my_exciting_cb)
self.log('+' * 50)
# fake
self.register_service('my_domain/my_exciting_service', self.my_exciting_cb)
def my_exciting_cb(self, *args: str, my_arg: int = 0, **kwargs: Any) -> Any:
namespace, domain, service = args
self.log(f"Service {domain}/{service} in the {namespace} namepsace called with {kwargs}")
self.log(f'Service {domain}/{service} in the {namespace} namepsace called with {kwargs}')
return 999 + my_arg

View File

@@ -0,0 +1,9 @@
from appdaemon.adapi import ADAPI
class SimpleApp(ADAPI):
def initialize(self):
self.log(f'{self.__class__.__name__} Initialized')
if:
OUtisde&sdf'asdfasdf'<txF> asdfasdfom some html</.dstd>

View File

@@ -1,6 +1,21 @@
from appdaemon.adapi import ADAPI
from appdaemon import utils
from appdaemon.adbase import ADBase
from appdaemon.plugins.hass import Hass
class SimpleApp(ADAPI):
def initialize(self):
self.log(f'{self.__class__.__name__} Initialized')
class SimpleApp(Hass):
def initialize(self) -> None:
match self.ping():
case float() as ping:
ping = utils.format_timedelta(ping)
self.log(f'{self.__class__.__name__} Initialized: {ping}')
case _:
pass
class BaseApp(ADBase):
def initialize(self) -> None:
self.adapi = self.get_ad_api()
self.log = self.adapi.log
self.hassapi = self.get_plugin_api('HASS')
assert isinstance(self.hassapi, Hass), 'HASS API not available'

View File

@@ -1,14 +0,0 @@
#
# Main arguments, all optional
#
title: Hello Panel
widget_dimensions: [120, 120]
widget_margins: [5, 5]
columns: 8
label:
widget_type: label
text: Hello World
layout:
- label(2x2)

29
conf/pyrightconfig.json Normal file
View File

@@ -0,0 +1,29 @@
{
// Specify which paths to check
"include": [
"./ad-test/conf/apps/**"
],
// "typeCheckingMode": "standard",
"typeCheckingMode": "basic",
// Be somewhat tolerant with regards to imperfect dependencies or types
"reportUnknownMemberType": "none",
"reportUnusedImport": "warning",
"reportMissingTypeStubs": "none",
"reportIncompleteStub": "none",
"reportUnknownVariableType": "none",
"reportUnknownParameterType": "warning",
"reportMissingTypeArgument": "warning",
"useLibraryCodeForTypes": true, // Homogeneous with pylance's default
// Add warnings for probable programming errors
"reportUnnecessaryTypeIgnoreComment": "warning",
"reportMissingParameterType": "warning",
"reportUnnecessaryComparison": "warning",
"reportUnnecessaryIsInstance": "warning",
"reportUnnecessaryCast": "warning",
"reportUnnecessaryContains": "warning",
"reportMatchNotExhaustive": "error",
"reportUnusedVariable": "warning",
"reportUnusedCoroutine": "warning",
"reportUnusedExpression": "warning",
"reportUnusedFunction": "warning"
}

View File

@@ -8,18 +8,15 @@ authors = [
dependencies = [
"pytest>=8.3.2",
"gitpython>=3.1.43",
"appdaemon",
]
readme = "README.md"
requires-python = ">= 3.8"
requires-python = ">= 3.10"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.rye]
managed = true
dev-dependencies = []
[tool.ruff.format]
quote-style = "single"
@@ -28,3 +25,11 @@ allow-direct-references = true
[tool.hatch.build.targets.wheel]
packages = ["src/ad_test"]
[tool.uv.sources]
appdaemon = {path = "/home/john/Documents/appdaemon" }
[dependency-groups]
dev = [
"ruff>=0.11.13",
]

29
ruff.toml Normal file
View File

@@ -0,0 +1,29 @@
line-length = 100
target-version = "py312"
[format]
quote-style = "single"
indent-style = "space"
[lint]
exclude = [
"conf/apps/simple_app/malformed.py"
]
select = ["ALL"]
extend-ignore = [
"ANN",
"BLE001",
"COM812",
"D",
"E501",
"ERA001",
"INP001",
"S101",
]
[lint.flake8-quotes]
inline-quotes = "single"
docstring-quotes = "double"
multiline-quotes = "double"

View File

@@ -1,2 +0,0 @@
def hello() -> str:
return 'Hello from ad-test!'