adaptive-lighting/custom_components/adaptive_lighting/_docs_helpers.py
Adam DeMuri 623dd65aef
Register service actions in async_setup for Bronze tier compliance (#1403)
* Register service actions in async_setup for Bronze tier compliance

- Move 'apply' and 'set_manual_control' service registration from async_setup_entry to async_setup.
- Move service handlers to module-level functions in switch.py.
- Update apply_service_schema to support dynamic defaults for transition duration.
- Clean up related unused imports and fix Python 3.10 syntax compatibility.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Clean up and add tests

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Fix lint errors

* Automated update of generated docs

* Re-add types

* Make transition not required again

* fix: validate global service targets

* fix: document optional apply transition

* fix: derive service docs from schema markers

* docs: clarify service target options

* fix: preserve entity service target handling

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Bas Nijholt <bas@nijho.lt>
2026-09-06 13:04:18 +02:00

116 lines
3.5 KiB
Python

from typing import Any
import homeassistant.helpers.config_validation as cv
import pandas as pd
import voluptuous as vol
from homeassistant.helpers import selector
from .const import (
DOCS,
DOCS_APPLY,
DOCS_MANUAL_CONTROL,
SET_MANUAL_CONTROL_SCHEMA,
VALIDATION_TUPLES,
apply_service_schema,
)
def _format_voluptuous_instance(instance: vol.All) -> str:
coerce_type = None
min_val = None
max_val = None
for validator in instance.validators:
if isinstance(validator, vol.Coerce):
coerce_type = validator.type.__name__
elif isinstance(validator, vol.Clamp | vol.Range):
min_val = validator.min
max_val = validator.max
if min_val is not None and max_val is not None:
return f"`{coerce_type}` {min_val}-{max_val}"
if min_val is not None:
return f"`{coerce_type} > {min_val}`"
if max_val is not None:
return f"`{coerce_type} < {max_val}`"
return f"`{coerce_type}`"
def _type_to_str(type_: Any) -> str: # noqa: PLR0911
"""Convert a (voluptuous) type to a string."""
if type_ == cv.entity_ids:
return "list of `entity_id`s"
if type_ in (bool, int, float, str):
return f"`{type_.__name__}`"
if type_ == cv.boolean:
return "bool"
if isinstance(type_, vol.All):
return _format_voluptuous_instance(type_)
if isinstance(type_, vol.Any):
return " or ".join(_type_to_str(t) for t in type_.validators)
if isinstance(type_, vol.In):
return f"one of `{type_.container}`"
if isinstance(type_, selector.SelectSelector):
return f"one of `{type_.config['options']}`"
if isinstance(type_, selector.ColorRGBSelector):
return "RGB color"
msg = f"Unknown type: {type_}"
raise ValueError(msg)
def generate_config_markdown_table() -> str:
rows: list[dict[str, str]] = []
for k, default, type_ in VALIDATION_TUPLES:
description = DOCS[k]
row = {
"Variable name": f"`{k}`",
"Description": description,
"Default": f"`{default}`",
"Type": _type_to_str(type_),
}
rows.append(row)
df = pd.DataFrame(rows)
return df.to_markdown(index=False)
def _schema_to_dict(schema: vol.Schema) -> dict[str, tuple[bool, Any]]:
result: dict[str, tuple[bool, Any]] = {}
for key, value in schema.schema.items():
if isinstance(key, vol.Required | vol.Optional):
required = isinstance(key, vol.Required) and key.default == vol.UNDEFINED
result[key.schema] = (required, value)
return result
def _generate_service_markdown_table(
schema: vol.Schema,
alternative_docs: dict[str, str] | None = None,
) -> str:
rows: list[dict[str, str]] = []
for k, (required, type_) in _schema_to_dict(schema).items():
if alternative_docs is not None and k in alternative_docs:
description = alternative_docs[k]
else:
description = DOCS[k]
row = {
"Service data attribute": f"`{k}`",
"Description": description,
"Required": "" if required else "",
"Type": _type_to_str(type_),
}
rows.append(row)
df = pd.DataFrame(rows)
return df.to_markdown(index=False)
def generate_apply_markdown_table() -> str:
return _generate_service_markdown_table(apply_service_schema(), DOCS_APPLY)
def generate_set_manual_control_markdown_table() -> str:
return _generate_service_markdown_table(
SET_MANUAL_CONTROL_SCHEMA,
DOCS_MANUAL_CONTROL,
)