You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/APPGUIDE.rst
+93-30Lines changed: 93 additions & 30 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -569,50 +569,113 @@ By declaring the above, each time the function ``self.log()`` is used within the
569
569
Global Module Dependencies
570
570
--------------------------
571
571
572
-
The previously described dependencies and load order have all been at the App level.
573
-
It is however, sometimes convenient to have global modules that have no apps in them that nonetheless
574
-
require dependency tracking. For instance, a global module might have a number of useful
575
-
variables or functions in it. When they change, a number of apps may need to be restarted.
576
-
as of AppDaemon 4.5 these dependencies are tracked autmatically and should just work.
577
-
It is neccesarry to take some care about how apps are structured, especially if multiple subdirectories are used.
572
+
.. admonition:: Deprecation warning
573
+
:class: warning
574
+
575
+
Global modules are deprecated and will be removed in a future release. AppDaemon now automatically tracks and
576
+
resolves dependencies using the :py:mod:`ast <ast>` package from the standard library.
578
577
579
578
AppDir Structure
580
579
----------------
581
580
582
-
So far, we have assumed that all apps and their configuration files are placed in a single directory. This works fine for simple setups
583
-
but as the number of apps grows, it can be useful to organize them into subdirectories. AppDaemon will automatically search all subdirectories of the apps directory for apps and configuration files. This means that you can have a directory structure like this:
581
+
So far, we have assumed that all apps and their configuration files are placed in a single directory. This works fine
582
+
for simple setups but as the number of apps grows, it can be useful to organize them into subdirectories. AppDaemon will
583
+
automatically search all subdirectories of the `apps` directory for apps and configuration files. This means that you
584
+
can have a directory structure like this:
584
585
585
586
.. code:: text
586
587
587
-
apps/
588
-
app1/
589
-
app1.py
590
-
app1.yaml
591
-
app2/
592
-
app2.py
593
-
app2.yaml
594
-
app3/
595
-
app3.py
596
-
app3.yaml
597
-
common/
598
-
common.py
599
-
common.yaml
588
+
conf/apps
589
+
├── app1
590
+
│ ├── app1.py
591
+
│ └── app1.yaml
592
+
├── app2
593
+
│ ├── app2.py
594
+
│ └── app2.yaml
595
+
├── common
596
+
│ ├── my_globals.py
597
+
│ └── utils.py
598
+
└── some
599
+
└── deep
600
+
└── path
601
+
├── app3.py
602
+
└── app3.yaml
603
+
604
+
In this example, AppDaemon will find all the apps defined in `app1.yaml`, `app2.yaml`, and even `app3.yaml`, despite it
605
+
being deep in a subdirectory. Each of those files would define apps using ``module: app1`` or ``module: app2`` etc. to
606
+
refer to their respective python modules.
607
+
608
+
Addtionally, apps in `app1.py`, `app2.py`, and `app3.py` can import things directly from `my_globals.py` and `utils.py`
609
+
like this:
600
610
611
+
.. code:: python
601
612
602
-
In this example, AppDaemon will find all the apps in the app1, app2, and app3 directories, as well as the common.py and common.yaml files in the common directory.
603
-
The apps can be configured in their respective YAML files, and they can also import functions or classes from the common module if needed, as long as some simple rules are adhered to.
613
+
#app1/app1.py
614
+
from appdaemon.adapi importADAPI
604
615
605
-
- If app1 wants to import a function called `common_funtion` from common.py, it can do so using the following import statement:
616
+
from my_globals importMY_GLOBAL_VAR
617
+
from utils import my_util_function
606
618
607
-
.. code:: python
619
+
classMyApp(ADAPI):
620
+
definitialize(self):
621
+
...# app code would go here
622
+
623
+
.. admonition:: Note text
624
+
:class: note
625
+
626
+
Note that there are no relative paths here. AppDaemon handles adding all the relevant subdirectories to the import path,
627
+
which allows them to be directly imported, as if the files were next to each other. Furthermore, AppDaemon understands
628
+
that `app1.py` depends on both `my_globals.py` and `utils.py`, so if either of those files change, AppDaemon will reload
629
+
`app1.py` automatically.
630
+
631
+
App Packages
632
+
~~~~~~~~~~~~
633
+
634
+
As app complexity increases, it's often useful to break the logic apart into multiple files, and sometimes these modules
635
+
have the same name as modules in other directories. For example, what if an app needed its own set of utils? The module
636
+
names can be managed by using ``__init__.py`` files.
637
+
638
+
.. code:: text
639
+
640
+
conf/apps
641
+
├── my_app
642
+
│ ├── __init__.py
643
+
│ ├── foo.py
644
+
│ ├── apps.yaml
645
+
│ └── utils.py
646
+
├── common
647
+
│ ├── ... # other common modules
648
+
│ └── utils.py
649
+
... # more apps down here
650
+
651
+
In this example `foo.py` can import from both `utils.py` modules like this, which uses
652
+
:py:ref:`package relative imports <relativeimports>` to reference the `utils.py` next to it as distinct from the one in
653
+
the `common` directory
654
+
655
+
.. code-block:: python
656
+
:emphasize-lines: 4,6
657
+
658
+
# my_app/foo.py
659
+
from appdaemon.adapi importADAPI
608
660
609
-
fromcommonimportcommon_function
661
+
fromutilsimportglobal_util_function
610
662
611
-
Note that there are no relative paths here - the AppDaemon system in combination with standard python rules will reslove this correctly,
612
-
and importantly, will understand that app1 now relies on common.py, and any changes to common.py will result it common.py being reloaded,
613
-
but this will also result in a reload of app1.py to pick up the changes
663
+
from .utils import specific_util_function
614
664
615
-
- if app2 is a package in it's own right (e.g. it has an __init__.py at the top level) #### John, what happens here???
665
+
classMyApp(ADAPI):
666
+
definitialize(self):
667
+
...# app code would go here
668
+
669
+
Using the ``__init__.py`` file indicates to Python/AppDaemon that the directory containing it is a package, and as such
670
+
the its import name changes slightly. The `apps.yaml` file needs to be updated to reflect this.
0 commit comments