Compare commits

..

7 Commits

Author SHA1 Message Date
John Lancaster
f6e7069449 added to launch.json example 2024-08-09 17:56:34 -05:00
John Lancaster
24dd2bfd3a more complexity in the test cases 2024-08-09 17:54:20 -05:00
John Lancaster
03ebbcc1f0 added variant 2024-07-08 17:55:32 -05:00
John Lancaster
9210cbf8a1 added parameter 2024-07-08 17:55:01 -05:00
John Lancaster
87ed3c81ae app definition organization 2024-07-07 23:39:02 -05:00
John Lancaster
4abd6ce5d5 formatting 2024-07-07 22:27:13 -05:00
John Lancaster
870424baa0 some renames and other changes 2024-07-07 22:07:02 -05:00
36 changed files with 209 additions and 554 deletions

View File

@@ -1,312 +0,0 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"import sys\n",
"import json\n",
"from pathlib import Path\n",
"\n",
"import yaml\n",
"from rich import print"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"class EmptyClass:\n",
" pass\n",
"\n",
"self = EmptyClass()\n",
"self.AD = EmptyClass()\n",
"self.AD.app_dir = '/mnt/ad_dev_conf/apps'"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\">Python parents:\n",
"</pre>\n"
],
"text/plain": [
"Python parents:\n"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"> - <span style=\"color: #800080; text-decoration-color: #800080\">/mnt/ad_dev_conf/apps/my_repo/</span><span style=\"color: #ff00ff; text-decoration-color: #ff00ff\">my_pkg</span>\n",
" - <span style=\"color: #800080; text-decoration-color: #800080\">/mnt/ad_dev_conf/</span><span style=\"color: #ff00ff; text-decoration-color: #ff00ff\">apps</span>\n",
" - <span style=\"color: #800080; text-decoration-color: #800080\">/mnt/ad_dev_conf/apps/my_repo/my_pkg/</span><span style=\"color: #ff00ff; text-decoration-color: #ff00ff\">my_sub_pkg</span>\n",
"</pre>\n"
],
"text/plain": [
" - \u001b[35m/mnt/ad_dev_conf/apps/my_repo/\u001b[0m\u001b[95mmy_pkg\u001b[0m\n",
" - \u001b[35m/mnt/ad_dev_conf/\u001b[0m\u001b[95mapps\u001b[0m\n",
" - \u001b[35m/mnt/ad_dev_conf/apps/my_repo/my_pkg/\u001b[0m\u001b[95mmy_sub_pkg\u001b[0m\n"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\">Module parents:\n",
"</pre>\n"
],
"text/plain": [
"Module parents:\n"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"> - <span style=\"color: #800080; text-decoration-color: #800080\">/mnt/ad_dev_conf/</span><span style=\"color: #ff00ff; text-decoration-color: #ff00ff\">apps</span>\n",
"</pre>\n"
],
"text/plain": [
" - \u001b[35m/mnt/ad_dev_conf/\u001b[0m\u001b[95mapps\u001b[0m\n"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\">Package parents:\n",
"</pre>\n"
],
"text/plain": [
"Package parents:\n"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"> - <span style=\"color: #800080; text-decoration-color: #800080\">/mnt/ad_dev_conf/apps/my_repo/</span><span style=\"color: #ff00ff; text-decoration-color: #ff00ff\">my_pkg</span>\n",
" - <span style=\"color: #800080; text-decoration-color: #800080\">/mnt/ad_dev_conf/apps/my_repo/my_pkg/</span><span style=\"color: #ff00ff; text-decoration-color: #ff00ff\">my_sub_pkg</span>\n",
"</pre>\n"
],
"text/plain": [
" - \u001b[35m/mnt/ad_dev_conf/apps/my_repo/\u001b[0m\u001b[95mmy_pkg\u001b[0m\n",
" - \u001b[35m/mnt/ad_dev_conf/apps/my_repo/my_pkg/\u001b[0m\u001b[95mmy_sub_pkg\u001b[0m\n"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\">Top package parents:\n",
"</pre>\n"
],
"text/plain": [
"Top package parents:\n"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"> - <span style=\"color: #800080; text-decoration-color: #800080\">/mnt/ad_dev_conf/apps/my_repo/</span><span style=\"color: #ff00ff; text-decoration-color: #ff00ff\">my_pkg</span>\n",
"</pre>\n"
],
"text/plain": [
" - \u001b[35m/mnt/ad_dev_conf/apps/my_repo/\u001b[0m\u001b[95mmy_pkg\u001b[0m\n"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\">Imports\n",
"</pre>\n"
],
"text/plain": [
"Imports\n"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"> - <span style=\"color: #800080; text-decoration-color: #800080\">/mnt/ad_dev_conf/</span><span style=\"color: #ff00ff; text-decoration-color: #ff00ff\">apps</span>\n",
" - <span style=\"color: #800080; text-decoration-color: #800080\">/mnt/ad_dev_conf/apps/</span><span style=\"color: #ff00ff; text-decoration-color: #ff00ff\">my_repo</span>\n",
"</pre>\n"
],
"text/plain": [
" - \u001b[35m/mnt/ad_dev_conf/\u001b[0m\u001b[95mapps\u001b[0m\n",
" - \u001b[35m/mnt/ad_dev_conf/apps/\u001b[0m\u001b[95mmy_repo\u001b[0m\n"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"base_path = Path(self.AD.app_dir)\n",
"\n",
"python_file_parents = set(\n",
" f.parent.resolve()\n",
" for f in base_path.rglob('*.py')\n",
")\n",
"print('Python parents:')\n",
"print('\\n'.join(f' - {p.as_posix()}' for p in python_file_parents))\n",
"\n",
"module_parents = set(\n",
" p for p in python_file_parents\n",
" if not (p / '__init__.py').exists()\n",
")\n",
"print('Module parents:')\n",
"print('\\n'.join(f' - {p.as_posix()}' for p in module_parents))\n",
"\n",
"package_parents = set(\n",
" p for p in python_file_parents\n",
" if (p / '__init__.py').exists()\n",
")\n",
"print('Package parents:')\n",
"print('\\n'.join(f' - {p.as_posix()}' for p in package_parents))\n",
"\n",
"top_packages_dirs = set(\n",
" p for p in package_parents\n",
" if not (p.parent / '__init__.py').exists()\n",
")\n",
"print('Top package parents:')\n",
"print('\\n'.join(f' - {p.as_posix()}' for p in top_packages_dirs))\n",
"\n",
"import_dirs = module_parents | set(p.parent for p in top_packages_dirs)\n",
"print('Imports')\n",
"print('\\n'.join(f' - {p.as_posix()}' for p in import_dirs))"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-weight: bold\">{</span>\n",
" <span style=\"color: #800080; text-decoration-color: #800080; font-weight: bold\">PosixPath</span><span style=\"font-weight: bold\">(</span><span style=\"color: #008000; text-decoration-color: #008000\">'/mnt/ad_dev_conf/apps/my_repo/my_pkg/__init__.py'</span><span style=\"font-weight: bold\">)</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'my_pkg'</span>,\n",
" <span style=\"color: #800080; text-decoration-color: #800080; font-weight: bold\">PosixPath</span><span style=\"font-weight: bold\">(</span><span style=\"color: #008000; text-decoration-color: #008000\">'/mnt/ad_dev_conf/apps/my_repo/my_pkg/motion.py'</span><span style=\"font-weight: bold\">)</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'my_pkg'</span>,\n",
" <span style=\"color: #800080; text-decoration-color: #800080; font-weight: bold\">PosixPath</span><span style=\"font-weight: bold\">(</span><span style=\"color: #008000; text-decoration-color: #008000\">'/mnt/ad_dev_conf/apps/my_repo/my_pkg/my_sub_pkg/__init__.py'</span><span style=\"font-weight: bold\">)</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'my_pkg'</span>,\n",
" <span style=\"color: #800080; text-decoration-color: #800080; font-weight: bold\">PosixPath</span><span style=\"font-weight: bold\">(</span><span style=\"color: #008000; text-decoration-color: #008000\">'/mnt/ad_dev_conf/apps/my_repo/my_pkg/my_sub_pkg/hello.py'</span><span style=\"font-weight: bold\">)</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'my_pkg'</span>\n",
"<span style=\"font-weight: bold\">}</span>\n",
"</pre>\n"
],
"text/plain": [
"\u001b[1m{\u001b[0m\n",
" \u001b[1;35mPosixPath\u001b[0m\u001b[1m(\u001b[0m\u001b[32m'/mnt/ad_dev_conf/apps/my_repo/my_pkg/__init__.py'\u001b[0m\u001b[1m)\u001b[0m: \u001b[32m'my_pkg'\u001b[0m,\n",
" \u001b[1;35mPosixPath\u001b[0m\u001b[1m(\u001b[0m\u001b[32m'/mnt/ad_dev_conf/apps/my_repo/my_pkg/motion.py'\u001b[0m\u001b[1m)\u001b[0m: \u001b[32m'my_pkg'\u001b[0m,\n",
" \u001b[1;35mPosixPath\u001b[0m\u001b[1m(\u001b[0m\u001b[32m'/mnt/ad_dev_conf/apps/my_repo/my_pkg/my_sub_pkg/__init__.py'\u001b[0m\u001b[1m)\u001b[0m: \u001b[32m'my_pkg'\u001b[0m,\n",
" \u001b[1;35mPosixPath\u001b[0m\u001b[1m(\u001b[0m\u001b[32m'/mnt/ad_dev_conf/apps/my_repo/my_pkg/my_sub_pkg/hello.py'\u001b[0m\u001b[1m)\u001b[0m: \u001b[32m'my_pkg'\u001b[0m\n",
"\u001b[1m}\u001b[0m\n"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"association = {}\n",
"for dir in top_packages_dirs:\n",
" associated_modules = list(dir.rglob('*.py'))\n",
" # print('\\n'.join(f' - {p.as_posix()}' for p in associated_modules))\n",
" for module_file in associated_modules:\n",
" association[module_file] = dir.stem\n",
"\n",
"print(association)"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"['hello_sub', 'motion']"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"module_name = 'my_pkg'\n",
"\n",
"with Path('apps/apps.yaml').open('r') as f:\n",
" self.app_config = yaml.load(f, Loader=yaml.SafeLoader)\n",
"\n",
"self.non_apps = []\n",
"\n",
"[\n",
" app_name\n",
" for app_name, app_cfg in self.app_config.items()\n",
" if app_cfg['module'].split('.')[0] == module_name\n",
"]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "ad_dev",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.7"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

View File

@@ -1,42 +0,0 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import importlib\n",
"from pathlib import Path"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "ad_dev",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.7"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

View File

@@ -23,8 +23,20 @@ Use with VSCode's debugger using a `launch.json` file.
"request": "launch", "request": "launch",
"module": "appdaemon", "module": "appdaemon",
"justMyCode": true, "justMyCode": true,
"args": "-c ../conf" "args": "-c ../conf" // Expects the conf directory to be next to the appdaemon one
}, },
{
"name": "Pytest",
"type": "debugpy",
"request": "launch",
"module": "pytest",
"justMyCode": true,
"args": [
"${workspaceFolder}/tests",
"--maxfail=1", // Stop after the first failure
"-s" // Allow for interactive debugging (pdb)
],
}
] ]
} }
``` ```

View File

@@ -1,4 +1,6 @@
appdaemon: appdaemon:
# ffake_ketys: asdfasdf
# starttime: '2024-07-14 12:00:00' # YYYY-MM-DD HH:MM:SS
uvloop: True uvloop: True
use_dictionary_unpacking: True use_dictionary_unpacking: True
# import_method: expert # import_method: expert
@@ -8,9 +10,15 @@ appdaemon:
longitude: 0 longitude: 0
elevation: 30 elevation: 30
time_zone: America/Chicago time_zone: America/Chicago
check_app_updates_profile: True # check_app_updates_profile: True
module_debug:
_app_management: DEBUG
_callbacks: DEBUG
# _events: DEBUG
# _threading: DEBUG
# mqtt: DEBUG
plugins: plugins:
HASS: hass:
type: hass type: hass
ha_url: http://192.168.1.82:8123 ha_url: http://192.168.1.82:8123
token: !secret long_lived_token token: !secret long_lived_token
@@ -19,6 +27,7 @@ appdaemon:
# namespace: mqtt # namespace: mqtt
# client_host: zigbee.localdomain # client_host: zigbee.localdomain
# client_user: homeassistant # client_user: homeassistant
# client_id: test_dev_client
# client_password: !secret mqtt_password # client_password: !secret mqtt_password
# client_topics: # client_topics:
# - zigbee2mqtt/# # - zigbee2mqtt/#

29
apps/app1/button.py Normal file
View File

@@ -0,0 +1,29 @@
import json
from appdaemon.adapi import ADAPI
class Button(ADAPI):
def initialize(self):
name = self.args['button']
self.handle = self.listen_event(
self.handle_button,
'MQTT_MESSAGE',
topic=f'zigbee2mqtt/{name}',
payload=self.payload_filter,
)
self.log(f"Started MQTT app in namespace '{self._namespace}'")
# raise ValueError
@staticmethod
def payload_filter(payload: str):
try:
return json.loads(payload)['action'] != ''
except Exception:
return False
def handle_button(self, event_name, data, **kwargs):
data['payload'] = json.loads(data['payload'])
# if data['payload']['action'] != '':
json_str = json.dumps(data, indent=4)
self.logger.info(f'{event_name} callback with\n{json_str}\n{kwargs}')

View File

@@ -1,8 +1,12 @@
from appdaemon.adapi import ADAPI from appdaemon.adapi import ADAPI
class Database: class Database(ADAPI):
def initialize(self):
self.comp = DatabaseComponent(self)
self.log('Database with component')
class DatabaseComponent:
def __init__(self, ad: ADAPI) -> None: def __init__(self, ad: ADAPI) -> None:
self.ad = ad self.ad = ad
# self.ad.logger.info(' NEW LOG LINE Database '.center(50, '='))
# self.ad.call_service()

View File

@@ -2,9 +2,11 @@ from appdaemon.adapi import ADAPI
class Notification: class Notification:
def __init__(self, ad: ADAPI) -> None: def __init__(self, ad: ADAPI, name: str) -> None:
self.ad = ad self.ad = ad
# self.ad.logger.info(' NEW LOG LINE '.center(50, '=')) self.log = self.ad.logger.info
self.log(f'Notification initialized for {name}')
# self.ad.logger.info(' NOTIFICATION '.center(50, '-'))
def send(self, message: str = "message not specified") -> None: def send(self, message: str = 'message not specified') -> None:
self.ad.log(message, level="DEBUG") self.ad.log(f'NOTIFICATION: {message}', level='DEBUG')

View File

View File

@@ -1,6 +1,13 @@
from pathlib import Path
from appdaemon.adapi import ADAPI from appdaemon.adapi import ADAPI
from ..notification import Notification
class Foo(ADAPI): class Foo(ADAPI):
def initialize(self): def initialize(self):
self.log(f'Initialized from {__file__}') # self.log('FOO')
self.log(f'Initialized app from {Path(__file__).relative_to(self.AD.app_dir.parent)}')
self.notification = Notification(self.AD, self.name)
# self.log(' FOO '.center(50, '*'))

View File

@@ -1,9 +1,8 @@
from appdaemon.adapi import ADAPI
from app1.subdir.foo import Foo from app1.subdir.foo import Foo
class OtherDatabaseApp(Foo): class OtherDatabaseApp(Foo):
def initialize(self): def initialize(self):
self.log(f'Initialized from {__file__}') super().initialize()
self.log(' CHANGE '.center(50, '=')) self.log('This app inherits from a custom class')
# self.log(' CHANGE '.center(50, '-'))

View File

@@ -0,0 +1,13 @@
import arrow
from appdaemon.adapi import ADAPI
class AutomationLib:
"""This is the documentation for AutomationLib"""
def __init__(self, ad: ADAPI) -> None:
self.ad = ad
self.ad.log(f"AutomationLib initialised for app '{self.ad.name}'", level='DEBUG')
def dow(self):
return arrow.now().isoweekday()

View File

@@ -0,0 +1,10 @@
automationlib:
module: automationlib
global: true
my_test_app:
module: test
class: Test
log_level: DEBUG
dependencies:
- automationlib

View File

@@ -0,0 +1 @@
arrow

View File

@@ -0,0 +1,14 @@
from appdaemon.plugins.hass.hassapi import Hass
from automationlib import AutomationLib
class Test(Hass):
"""This is the documentation for Test"""
def initialize(self):
"""."""
self.lib = AutomationLib(self)
self.log('-' * 72)
self.log(f'Day of the week: {self.lib.dow()}')
self.log('-' * 72)

6
apps/button.yaml Normal file
View File

@@ -0,0 +1,6 @@
button_test:
module: app1.button
class: Button
button: Living Room Button
dependencies:
- hello_world

View File

@@ -1,9 +1,15 @@
App1: App1:
module: aadfasdf module: app1.database
class: Database class: Database
other_kwargs: value other_kwargs: value
dependencies: Sound dependencies: Sound
App1Foo:
module: app1.subdir.foo
class: Foo
# dependencies:
# - companion_app
App2: App2:
module: app2.database module: app2.database
class: OtherDatabaseApp class: OtherDatabaseApp
@@ -12,8 +18,8 @@ App2:
- App1Foo - App1Foo
- globals - globals
App1Foo: App3:
module: app1.subdir.foo module: app2.database
class: Foo class: OtherDatabaseApp
globals: dependencies:
- hello - App2

0
apps/family/__init__.py Normal file
View File

10
apps/family/child.py Normal file
View File

@@ -0,0 +1,10 @@
from appdaemon.adapi import ADAPI
from .utils import UTILS_VALS
raise ImportError('Fake import error')
class Child(ADAPI):
def initialize(self):
self.log(f'Initialized child with: {UTILS_VALS}')

7
apps/family/family.yaml Normal file
View File

@@ -0,0 +1,7 @@
parent:
module: family.parent
class: Parent
child:
module: family.child
class: Child

8
apps/family/parent.py Normal file
View File

@@ -0,0 +1,8 @@
from appdaemon.adapi import ADAPI
from .utils import UTILS_VALS
class Parent(ADAPI):
def initialize(self):
self.log(f'Initialized parent with: {UTILS_VALS}')

View File

View File

@@ -0,0 +1 @@
OTHER_CITY = 'NYC again'

3
apps/family/utils.py Normal file
View File

@@ -0,0 +1,3 @@
from .relatives.city import OTHER_CITY
UTILS_VALS = 100, 600

View File

@@ -2,9 +2,9 @@ globals:
module: globals module: globals
global: true global: true
companion_app: # companion_app:
module: app1.notification # module: app1.notification
global: true # global: true
global_modules: global_modules:
- hello - hello

View File

@@ -5,4 +5,4 @@ from appdaemon.adapi import ADAPI
class HelloWorld(ADAPI): class HelloWorld(ADAPI):
def initialize(self): def initialize(self):
self.log(f'Initialized app from {Path(__file__).relative_to(self.AD.app_dir.parent)}') self.log(f'Initialized app from {Path(__file__).relative_to(Path(self.AD.app_dir).parent)}')

View File

@@ -2,17 +2,11 @@ from pathlib import Path
from appdaemon.adbase import ADBase from appdaemon.adbase import ADBase
filename = Path(__file__).name
# print(f' Importing {filename} '.center(50, '-'))
class Foo(ADBase): class Foo(ADBase):
changes: bool = True changes: bool = True
def initialize(self): def initialize(self):
self.adapi = self.get_ad_api() self.adapi = self.get_ad_api()
self.adapi.log(f'Initialized app from {Path(__file__).relative_to(self.AD.app_dir.parent)}')
self.adapi.log(f'Initialized app from {filename} new line changes: {self.changes}') self.adapi.log(f'new line changes: {self.changes}')
# self.adapi.log(f'New log line: {self.changes}')
# self.adapi.log(f'Even newer log line: {self.changes}')
# print(f' Foo.changes = {Foo.changes} '.center(50, '-'))

View File

@@ -1,15 +1,11 @@
Foo: Repo-Foo:
module: my_pkg module: my_pkg
class: Foo class: Foo
Bar: Repo-Bar:
module: my_pkg.my_sub_pkg module: my_pkg.my_sub_pkg
class: Bar class: Bar
Baz: Repo-Baz:
module: my_pkg.my_sub_pkg.baz module: my_pkg.my_sub_pkg.baz
class: Baz class: Baz
Hello:
module: hello
class: HelloWorld

View File

@@ -1,3 +1,4 @@
rich rich
anyio anyio
pydantic pydantic
# arrow

View File

@@ -1,3 +1,7 @@
Hello:
module: hello
class: HelloWorld
Hello2: Hello2:
module: hello module: hello
class: HelloWorld class: HelloWorld

View File

@@ -1,9 +0,0 @@
from app1.notification import Notification
from appdaemon.adbase import ADBase
class Test(ADBase):
def initialize(self):
self.adapi = self.get_ad_api()
self.notify = Notification(self.adapi)
self.notify.send("Test notification from Test2 app.")

View File

@@ -1,5 +1,11 @@
test: test_notifier:
module: test # module: test_notify
class: Test # class: TestNotifier
module: variant
class: Variant
log_level: DEBUG log_level: DEBUG
dependencies: companion_app # dependencies: companion_app
TestApp:
module: test_notify
class: TestNotifier

11
apps/test/test_notify.py Normal file
View File

@@ -0,0 +1,11 @@
from app1.notification import Notification
from appdaemon.adbase import ADBase
class TestNotifier(ADBase):
def initialize(self):
self.adapi = self.get_ad_api()
self.log = self.adapi.log
self.log(f'Initialized test notifier class: {self.__class__.__name__}')
self.notify = Notification(self.adapi, self.name)
self.notify.send('Some notifications')

8
apps/test/variant.py Normal file
View File

@@ -0,0 +1,8 @@
from test_notify import TestNotifier
from appdaemon.adapi import ADAPI
class Variant(TestNotifier, ADAPI):
def initialize(self):
super().initialize()
self.log('Variant of the TestNotifier')
# self.log(' VARIANT '.center(50, '*'))

View File

@@ -2,6 +2,7 @@ services:
appdaemon: appdaemon:
container_name: appdaemon_dev container_name: appdaemon_dev
image: appdaemon:local-dev image: appdaemon:local-dev
# image: acockburn/appdaemon:dev
volumes: volumes:
- /etc/localtime:/etc/localtime:ro - /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro - /etc/timezone:/etc/timezone:ro

View File

@@ -1,144 +0,0 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import importlib\n",
"\n",
"import pathlib\n",
"from types import ModuleType\n",
"from typing import List\n",
"from pathlib import Path\n",
"\n",
"from IPython.lib import deepreload"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"=============== Importing my_pkg ===============\n",
"-------------- Importing motion.py ---------------\n",
"===================== Done =====================\n"
]
}
],
"source": [
"import sys\n",
"\n",
"sys.path.insert(0, Path('apps/my_repo').resolve().as_posix())\n",
"\n",
"import my_pkg"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"['importlib._abc',\n",
" 'importlib._bootstrap',\n",
" 'importlib._bootstrap_external',\n",
" 'importlib._imp',\n",
" 'importlib.abc',\n",
" 'importlib.machinery',\n",
" 'importlib.metadata',\n",
" 'importlib.resources',\n",
" 'importlib.sys',\n",
" 'importlib.util',\n",
" 'importlib.warnings']"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def recursive_modules(module_obj: ModuleType) -> List[str]:\n",
" sub_modules = [\n",
" f'{module_obj.__name__}.{name}' for name in dir(module_obj)\n",
" if isinstance(getattr(module_obj, name), ModuleType)\n",
" ]\n",
" return sub_modules\n",
"\n",
"recursive_modules(importlib)"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[<module 'my_pkg' from '/home/john/conf/apps/my_repo/my_pkg/__init__.py'>, <module 'my_pkg.motion' from '/home/john/conf/apps/my_repo/my_pkg/motion.py'>, <module 'my_pkg.my_sub_pkg' from '/home/john/conf/apps/my_repo/my_pkg/my_sub_pkg/__init__.py'>, <module 'my_pkg.my_sub_pkg.hello' from '/home/john/conf/apps/my_repo/my_pkg/my_sub_pkg/hello.py'>]\n",
"=============== Importing my_pkg ===============\n",
"===================== Done =====================\n",
"-------------- Importing motion.py ---------------\n"
]
}
],
"source": [
"def recursive_modules(module: ModuleType):\n",
" for name in dir(module):\n",
" sub_mod = getattr(module, name)\n",
" if isinstance(sub_mod, ModuleType) and sub_mod.__name__.startswith(module.__name__):\n",
" yield from recursive_modules(sub_mod)\n",
" yield module\n",
"\n",
"\n",
"def reload_package(pkg: ModuleType):\n",
" modules = sorted(recursive_modules(pkg), key=lambda m: m.__name__)\n",
" for mod in modules:\n",
" importlib.reload(mod)\n",
"\n",
"\n",
"reload_package(my_pkg)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"importlib.import_module(name)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "appdaemon",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.8"
}
},
"nbformat": 4,
"nbformat_minor": 2
}