some model scratchwork

This commit is contained in:
John Lancaster
2024-08-08 00:17:58 -05:00
parent 55a891f94c
commit 652194200b
3 changed files with 223 additions and 0 deletions

0
models/__init__.py Normal file
View File

127
models/ad_config.py Normal file
View File

@@ -0,0 +1,127 @@
from pathlib import Path
from typing import Dict, List, Literal, Optional, Union
from pydantic import BaseModel, Field
from .plugins import HASSPluginConfig, MQTTPluginConfig
class FilterConfig(BaseModel):
"""For more information see :ref:`filters`"""
command_line: str
input_ext: str
output_ext: str
class NamespaceConfig(BaseModel):
writeback: Literal['safe', 'performance', 'hybrid']
class AppDaemonConfig(BaseModel):
"""Intended to be used directly on the content of the main appdaemon.yaml"""
time_zone: str
"""Used by AppDaemon for its internal time-related operations (e.g. America/New_York).
"""
latitude: float
"""Used by AppDaemon for its internal location-related operations (decimal format).
"""
longitude: float
"""Used by AppDaemon for its internal location-related operations (decimal format).
"""
elevation: float
"""Meters above sea level. Used by AppDaemon for its internal location-related operations.
"""
plugins: Dict[str, Union[HASSPluginConfig, MQTTPluginConfig]] = Field(discriminator='type')
"""Configure the plugins used by AppDaemon to communicate with third-party systems (e.f. Home Assistant, MQTT broker).
"""
filters: Optional[List[FilterConfig]] = None
app_dir: Optional[Path] = None
exclude_dirs: Optional[List[Path]] = None
"""When loading *AppDaemon apps* in the ``apps`` directory, ignore these subdirectories.
By default AppDaemon ignores all directories with a ``.`` in their name (hidden folders).
Example:
.. code:: yaml
exclude_dirs:
- dir1
- dir2
- dir3
AppDaemon will traverse the folder hierarchy starting from the ``apps`` folder, and will exclude any directory whose name matches the configured exclude rule, as well as all its sub-folders.
.. TODO: This part is not clear. Don't we want to exclude the parent directory `somedir`?
**Note**: It is not possible to match multiple level directory names e.g., ``somedir/dir1``.
In that case, the match should be on ``dir1``, with the caveat that if you have ``dir1`` anywhere else in the hierarchy, it will also be excluded.
"""
missing_app_warnings: bool = True
"""AppDaemon by default outputs a warning if it finds a Python file that has no associated configuration in an app config file.
Set this parameter to ``0`` or ``false`` to suppress the warning. This is useful for instance to distribute Python files not strictly related to AppDaemon along with AppDaemon apps.
"""
invalid_config_warnings: bool = True
"""AppDaemon by default outputs a warning if it finds an app config file file that doesnt include ``class`` and ``module`` for an app.
Set this parameter to ``0`` or ``false`` to suppress the warning.
This is intended to ease the distribution of additional files along with apps.
"""
production_mode: bool = False
"""Defines how often AppDaemon checks for changes in the app files.
- ``false``: AppDaemon checks for changes in Apps and app config files every second.
- ``true``: AppDaemon checks for changes in Apps and app config files only on restart. This can save some processing power on busy systems.
This option can also be changed from within apps, using the ``set_production_mode`` API call.
"""
thread_duration_warning_threshold: int = 10.0
"""AppDaemon monitors the time that each tread spends in an App. If a thread is taking too long to finish a callback, it may impact other apps. AppDaemon will log a warning if any thread is over the duration specified in seconds. The default is ``10`` seconds, setting this value to ``00`` will disable this check.
"""
log_thread_actions: bool = False
"""If set to ``1``, AppDaemon will log all callbacks on entry and exit for the scheduler, events, and state changes.
This can be useful for troubleshooting thread starvation issues.
"""
import_paths: Optional[List[Path]] = None
"""Use this directive to add additional arbitary directories to the python interpreter's search path. Directories must be fully qualified.
"""
### Advanced Options
total_threads: int = 10
"""The number of dedicated worker threads to create for running the apps. Normally, AppDaemon will create enough threads to provide one per app, or default to 10 if app pinning is turned off.
Setting this to a specific value will turn off automatic thread management.
"""
pin_apps: bool = True
"""If ``true``, AppDaemon apps will be pinned to a particular thread. This should avoids complications around re-entrant code and locking of instance variables.
"""
pin_threads: int = 0
"""Number of threads to use for pinned apps, allowing the user to section off a sub-pool just for pinned apps. By default all threads are used for pinned apps.
"""
threadpool_workers: int = 10
"""Maximum number of worker threads to be internally used by AppDaemon to execute the calls asynchronously.
"""
load_distribution: Literal['round-robin', 'random', 'load'] = 'round-robin'
"""Algorithm to use for load balancing between unpinned apps.
"""
qsize_warning_threshold: int = 50
"""Total number of items on thread queues before a warning is issued. Defaults to ``50``.
"""
qsize_warning_step: int = 60
"""If total queue size is over ``qsize_warning_threshold``, issue a warning every ``<qsize_warning_step>`` times the utility loop executes (normally this is once every second).
"""
qsize_warning_iterations: int = 5
"""If set to a value greater than ``0``, when total queue size is over ``qsize_warning_threshold``, issue a warning every ``<qsize_warning_step>`` times the utility loop executes, but not until the queue size has been exceeded for a minimum of ``<qsize_warning_iterations>`` iterations.
This allows you to tune out brief expected spikes in queue size.
"""
uvloop: bool = False
"""If ``true``, AppDaemon will use `uvloop`_ instead of the default Python ``asyncio`` loop. It is said to improve the speed of the loop. For more information about ``uvloop`` see `this`_.
.. _uvloop: https://github.com/MagicStack/uvloop
.. _this: https://magic.io/blog/uvloop-blazing-fast-python-networking
"""
namespaces: Dict[str, NamespaceConfig] = Field(default_factory=dict)

96
models/plugins.py Normal file
View File

@@ -0,0 +1,96 @@
import ssl
import sys
from ipaddress import IPv4Address
from pathlib import Path
from typing import Any, List, Literal, Optional, Union
from pydantic import BaseModel, SecretStr, validator
class PluginConfig(BaseModel):
""""""
type: str
class HASSPluginConfig(PluginConfig):
type: Literal['hass'] = 'hass'
token: SecretStr
ha_url: str
class MQTTPluginConfig(PluginConfig):
version: str = '1.0'
type: Literal['mqtt'] = 'mqtt'
name: Optional[str] = None
namespace: str = 'default'
client_host: Union[str, IPv4Address] = '127.0.0.1'
client_port: int = 1883
client_timeout: int = 60
client_transport: Literal['tcp', 'websockets'] = 'tcp'
client_clean_session: bool = True
client_id: Optional[str] = None
client_user: Optional[str] = None
client_password: Optional[SecretStr] = None
client_cert: Optional[Path] = None
client_key: Optional[Path] = None
verify_cert: bool = True
tls_version: ssl._SSLMethod = 'auto'
ca_cert: Optional[str] = None
event_name: str = 'MQTT_MESSAGE'
client_topics: Union[List[str] | Literal['NONE']] = ['#']
client_qos: Any = 0
status_topic: str = None
birth_topic: Optional[str] = None
birth_payload: str = 'online'
birth_retain: bool = True
will_topic: Optional[str] = None
will_payload: str = 'offline'
will_retain: bool = True
shutdown_payload: Optional[str] = None
force_start: bool = False
def model_post_init(self, context: Any):
if self.client_topics == 'NONE':
self.client_topics = []
if self.will_topic is None:
self.will_topic = self.status_topic
if self.birth_topic is None:
self.birth_topic = self.status_topic
@validator('status_topic', pre=True, always=True)
@classmethod
def set_status_topic(cls, v, values):
if v is None:
client_id = values['client_id'] if values['client_id'] else f'{values["name"]}-client'
status_topic = f'{client_id}/status'
return status_topic
else:
return v
@validator('tls_version', pre=True, always=True)
@classmethod
def convert_tls_version(cls, v, values):
if v.lower() == 'auto':
if sys.hexversion >= 0x03060000:
return ssl.PROTOCOL_TLS
else:
return ssl.PROTOCOL_TLSv1
val_map = {
'1.0': ssl.PROTOCOL_TLSv1,
'1.1': ssl.PROTOCOL_TLSv1_1,
'1.2': ssl.PROTOCOL_TLSv1_2,
}
return val_map[v]
@validator('client_id', pre=True, always=True)
@classmethod
def validate_client_id(cls, v, values):
if v is None:
return f"appdaemon_{values['name']}_client"
else:
return v