Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 24 additions & 12 deletions docs/core/integration-quality-scale/rules/action-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,24 +24,36 @@ This is used to first fetch the configuration entry, and then check if it is loa
If the configuration entry does not exist or the configuration entry that we found is not loaded, we raise a relevant error which is shown to the user.
Supply description placeholders to enable translation of service parameters, for example, to reference external resources like documentation URLs that need to be localized or updated independently of the service description.

`__init__py`:
```python {13-20} showLineNumbers
`__init__.py`:
```python {6} showLineNumbers
from .services import async_setup_services

async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up my integration."""

async def async_get_schedule(call: ServiceCall) -> ServiceResponse:
"""Get the schedule for a specific range."""
if not (entry := hass.config_entries.async_get_entry(call.data[ATTR_CONFIG_ENTRY_ID])):
raise ServiceValidationError("Entry not found")
if entry.state is not ConfigEntryState.LOADED:
raise ServiceValidationError("Entry not loaded")
client = cast(MyConfigEntry, entry).runtime_data
...
async_setup_services(hass)
return True
```

`services.py`:
```python {14-21} showLineNumbers
async def _async_get_schedule(call: ServiceCall) -> ServiceResponse:
"""Get the schedule for a specific range."""
if not (entry := hass.config_entries.async_get_entry(call.data[ATTR_CONFIG_ENTRY_ID])):
raise ServiceValidationError("Entry not found")
if entry.state is not ConfigEntryState.LOADED:
raise ServiceValidationError("Entry not loaded")
client = cast(MyConfigEntry, entry).runtime_data
...

@callback
def async_setup_services(hass: HomeAssistant) -> None:
"""Register the services."""

hass.services.async_register(
DOMAIN,
SERVICE_GET_SCHEDULE,
async_get_schedule,
_async_get_schedule,
schema=SERVICE_GET_SCHEDULE_SCHEMA,
supports_response=SupportsResponse.ONLY,
description_placeholders={"example_url": "https://schedule.example.com"}
Expand All @@ -58,4 +70,4 @@ There are no exceptions to this rule.

## Related rules

<RelatedRules relatedRules={frontMatter.related_rules}></RelatedRules>
<RelatedRules relatedRules={frontMatter.related_rules}></RelatedRules>
30 changes: 30 additions & 0 deletions docs/core/integration-quality-scale/rules/common-modules.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ The second common pattern is the base entity.
Since a lot of integrations provide more types of entities, a base entity can prove useful to reduce code duplication.
The base entity should be placed in `entity.py`.

The third common pattern relates to action definition and registration.
Placing these in a separate module helps improve code clarity and separation of concern.
The action definition and registration should be placed in `services.py`.

The efforts done to increase consistency between integrations have a positive impact on the quality of the codebase and the developer experience.

## Example implementation
Expand Down Expand Up @@ -48,6 +52,32 @@ class MyEntity(CoordinatorEntity[MyCoordinator]):
self._attr_device_info = ...
```

`services.py`
```python showLineNumbers
async def _async_get_schedule(call: ServiceCall) -> ServiceResponse:
"""Get the schedule for a specific range."""
if not (entry := hass.config_entries.async_get_entry(call.data[ATTR_CONFIG_ENTRY_ID])):
raise ServiceValidationError("Entry not found")
if entry.state is not ConfigEntryState.LOADED:
raise ServiceValidationError("Entry not loaded")
client = cast(MyConfigEntry, entry).runtime_data
...

@callback
def async_setup_services(hass: HomeAssistant) -> None:
"""Register the services."""

hass.services.async_register(
DOMAIN,
SERVICE_GET_SCHEDULE,
async_get_schedule,
_async_get_schedule,
schema=SERVICE_GET_SCHEDULE_SCHEMA,
supports_response=SupportsResponse.ONLY,
description_placeholders={"example_url": "https://schedule.example.com"}
)
```

## Exceptions

There are no exceptions to this rule.
Expand Down