> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/home-assistant/core/llms.txt
> Use this file to discover all available pages before exploring further.

# Service Calls

> Understanding Home Assistant's service registry and service call execution system

Services are the primary way to trigger actions in Home Assistant. The Service Registry manages registration, validation, and execution of callable services across all domains.

## ServiceRegistry Overview

The `ServiceRegistry` provides a centralized system for managing services:

```python homeassistant/core.py theme={null}
class ServiceRegistry:
    """Offer the services over the eventbus."""
    
    def __init__(self, hass: HomeAssistant) -> None:
        """Initialize a service registry."""
        self._services: dict[str, dict[str, Service]] = {}
        self._hass = hass
```

<Info>
  Services are organized in a two-level dictionary structure: `domain -> service_name -> Service`. This enables O(1) lookups for service execution.
</Info>

## Service Object Structure

Each registered service is represented by a `Service` object:

```python homeassistant/core.py theme={null}
class Service:
    """Representation of a callable service."""
    
    def __init__(
        self,
        func: Callable[[ServiceCall], Coroutine[Any, Any, ServiceResponse] | ServiceResponse | None],
        schema: VolSchemaType | None,
        domain: str,
        service: str,
        context: Context | None = None,
        supports_response: SupportsResponse = SupportsResponse.NONE,
        job_type: HassJobType | None = None,
        description_placeholders: Mapping[str, str] | None = None,
    ) -> None:
        self.job = HassJob(func, f"service {domain}.{service}", job_type=job_type)
        self.schema = schema
        self.supports_response = supports_response
        self.description_placeholders = description_placeholders
```

### Service Components

<CardGroup cols={2}>
  <Card title="job" icon="play">
    HassJob wrapping the service function for optimized execution
  </Card>

  <Card title="schema" icon="check">
    Voluptuous schema for validating service data
  </Card>

  <Card title="supports_response" icon="reply">
    Whether the service can return data to the caller
  </Card>

  <Card title="description_placeholders" icon="language">
    Translation placeholders for service descriptions
  </Card>
</CardGroup>

## ServiceCall Object

When a service is called, it receives a `ServiceCall` object:

```python homeassistant/core.py theme={null}
class ServiceCall:
    """Representation of a call to a service."""
    
    def __init__(
        self,
        hass: HomeAssistant,
        domain: str,
        service: str,
        data: dict[str, Any] | None = None,
        context: Context | None = None,
        return_response: bool = False,
    ) -> None:
        self.hass = hass
        self.domain = domain
        self.service = service
        self.data = ReadOnlyDict(data or {})
        self.context = context or Context()
        self.return_response = return_response
```

### ServiceCall Attributes

* **domain**: The domain of the service (e.g., "light")
* **service**: The service name (e.g., "turn\_on")
* **data**: Read-only dictionary of service parameters
* **context**: Context tracking who/what triggered the call
* **return\_response**: Whether the caller expects return data

## Registering Services

### Basic Registration

```python theme={null}
import voluptuous as vol
from homeassistant.core import HomeAssistant, ServiceCall

async def async_handle_my_service(call: ServiceCall) -> None:
    """Handle the my_service call."""
    entity_id = call.data.get("entity_id")
    value = call.data.get("value")
    
    _LOGGER.info("Service called with entity_id=%s, value=%s", entity_id, value)
    # Perform the service action
    await do_something(entity_id, value)

# Define validation schema
SERVICE_SCHEMA = vol.Schema({
    vol.Required("entity_id"): cv.entity_id,
    vol.Required("value"): vol.Coerce(int),
})

# Register the service
hass.services.async_register(
    "my_domain",
    "my_service",
    async_handle_my_service,
    schema=SERVICE_SCHEMA
)
```

<Steps>
  <Step title="Service Function Defined">
    Create an async function that accepts a ServiceCall parameter.
  </Step>

  <Step title="Schema Created">
    Define a voluptuous schema to validate service data.
  </Step>

  <Step title="Service Registered">
    Call `async_register` to register the service with the registry.
  </Step>

  <Step title="Event Fired">
    The registry fires `EVENT_SERVICE_REGISTERED` to notify listeners.
  </Step>
</Steps>

### Registration with Response Support

```python theme={null}
from homeassistant.core import SupportsResponse, ServiceResponse

async def async_get_data(call: ServiceCall) -> ServiceResponse:
    """Service that returns data to the caller."""
    entity_id = call.data["entity_id"]
    
    # Fetch data
    data = await fetch_entity_data(entity_id)
    
    # Return dict (must be JSON-serializable)
    return {
        "entity_id": entity_id,
        "value": data.value,
        "timestamp": data.timestamp.isoformat(),
    }

hass.services.async_register(
    "my_domain",
    "get_data",
    async_get_data,
    schema=GET_DATA_SCHEMA,
    supports_response=SupportsResponse.ONLY
)
```

### Response Support Modes

```python homeassistant/core.py theme={null}
class SupportsResponse(enum.StrEnum):
    """Service call response configuration."""
    
    NONE = "none"        # No response data (default)
    OPTIONAL = "optional" # Response data when requested
    ONLY = "only"        # Must always request response
```

<Note>
  * **NONE**: Traditional services that perform actions without returning data
  * **OPTIONAL**: Services that can optionally return data when `return_response=True`
  * **ONLY**: Read-only services that must be called with `return_response=True`
</Note>

## Calling Services

### Basic Service Call

```python theme={null}
# Async context (non-blocking)
await hass.services.async_call(
    "light",
    "turn_on",
    {"entity_id": "light.living_room", "brightness": 255},
    blocking=False
)

# Blocking call (wait for completion)
await hass.services.async_call(
    "light",
    "turn_on",
    {"entity_id": "light.living_room", "brightness": 255},
    blocking=True
)
```

### Service Call with Response

```python theme={null}
# Request response data
response = await hass.services.async_call(
    "my_domain",
    "get_data",
    {"entity_id": "sensor.temperature"},
    blocking=True,
    return_response=True
)

print(f"Temperature: {response['value']}")
```

<Warning>
  Services can only return response data when called with `blocking=True` and `return_response=True`. Attempting to use `return_response=True` with `blocking=False` will raise a `ServiceValidationError`.
</Warning>

### Service Call Flow

```python homeassistant/core.py theme={null}
async def async_call(
    self,
    domain: str,
    service: str,
    service_data: dict[str, Any] | None = None,
    blocking: bool = False,
    context: Context | None = None,
    target: dict[str, Any] | None = None,
    return_response: bool = False,
) -> ServiceResponse:
    """Call a service."""
    context = context or Context()
    service_data = service_data or {}
    
    # Lookup service handler
    try:
        handler = self._services[domain][service]
    except KeyError:
        domain = domain.lower()
        service = service.lower()
        try:
            handler = self._services[domain][service]
        except KeyError:
            raise ServiceNotFound(domain, service) from None
    
    # Validate response support
    if return_response and handler.supports_response is SupportsResponse.NONE:
        raise ServiceValidationError("Service does not support response")
    
    # Merge target into service_data
    if target:
        service_data.update(target)
    
    # Validate service data
    if handler.schema:
        processed_data = handler.schema(service_data)
    else:
        processed_data = service_data
    
    # Create ServiceCall object
    service_call = ServiceCall(
        self._hass, domain, service, processed_data, context, return_response
    )
    
    # Fire call_service event
    self._hass.bus.async_fire_internal(
        EVENT_CALL_SERVICE,
        {
            ATTR_DOMAIN: domain,
            ATTR_SERVICE: service,
            ATTR_SERVICE_DATA: service_data,
        },
        context=context,
    )
    
    # Execute service
    coro = self._execute_service(handler, service_call)
    if not blocking:
        # Run in background
        self._hass.async_create_task_internal(
            self._run_service_call_catch_exceptions(coro, service_call),
            f"service call background {service_call.domain}.{service_call.service}",
            eager_start=True,
        )
        return None
    
    # Wait for completion
    response_data = await coro
    if not return_response:
        return None
    return response_data
```

<Steps>
  <Step title="Handler Lookup">
    The service handler is retrieved from the registry.
  </Step>

  <Step title="Response Validation">
    Response support flags are validated against the request.
  </Step>

  <Step title="Data Validation">
    Service data is validated against the service schema.
  </Step>

  <Step title="Event Fired">
    EVENT\_CALL\_SERVICE is fired to notify listeners.
  </Step>

  <Step title="Service Executed">
    The service handler is executed (blocking or background).
  </Step>
</Steps>

## Service Execution

Services are executed based on their job type:

```python homeassistant/core.py theme={null}
async def _execute_service(
    self, handler: Service, service_call: ServiceCall
) -> ServiceResponse:
    """Execute a service."""
    job = handler.job
    target = job.target
    
    if job.job_type is HassJobType.Coroutinefunction:
        # Async function - await directly
        return await target(service_call)
    
    if job.job_type is HassJobType.Callback:
        # Callback - call immediately
        return target(service_call)
    
    # Executor job - run in thread pool
    return await self._hass.async_add_executor_job(target, service_call)
```

<Info>
  The job type is determined automatically when the service is registered, enabling optimal execution without runtime checks.
</Info>

## Service Schema Validation

Schemas use the Voluptuous library for validation:

```python theme={null}
import voluptuous as vol
from homeassistant.helpers import config_validation as cv

SERVICE_SCHEMA = vol.Schema({
    # Required fields
    vol.Required("entity_id"): cv.entity_ids,
    
    # Optional fields with defaults
    vol.Optional("brightness", default=255): vol.All(
        vol.Coerce(int),
        vol.Range(min=0, max=255)
    ),
    
    # Conditional fields
    vol.Optional("color_temp"): vol.All(
        vol.Coerce(int),
        vol.Range(min=153, max=500)
    ),
    
    # Custom validation
    vol.Optional("transition"): vol.All(
        vol.Coerce(float),
        vol.Range(min=0)
    ),
})
```

### Common Validators

<CodeGroup>
  ```python Entity Validators theme={null}
  import homeassistant.helpers.config_validation as cv

  # Single entity ID
  schema = vol.Schema({"entity_id": cv.entity_id})

  # List of entity IDs
  schema = vol.Schema({"entity_id": cv.entity_ids})

  # Entity ID from specific domain
  schema = vol.Schema({"entity_id": cv.entity_domain("light")})
  ```

  ```python Type Validators theme={null}
  # String
  vol.Required("name"): cv.string

  # Boolean
  vol.Optional("enabled"): cv.boolean

  # Number with range
  vol.Optional("value"): vol.All(
      vol.Coerce(int),
      vol.Range(min=0, max=100)
  )

  # Template
  vol.Optional("message"): cv.template
  ```

  ```python Complex Validators theme={null}
  # Time period
  vol.Optional("delay"): cv.positive_time_period

  # Time
  vol.Optional("at"): cv.time

  # Icon
  vol.Optional("icon"): cv.icon

  # Service
  vol.Required("service"): cv.service
  ```
</CodeGroup>

## Checking Service Availability

```python theme={null}
# Check if a service exists
if hass.services.has_service("light", "turn_on"):
    await hass.services.async_call("light", "turn_on", ...)

# Check response support
response_support = hass.services.supports_response("my_domain", "get_data")
if response_support == SupportsResponse.ONLY:
    # Must use return_response=True
    response = await hass.services.async_call(
        "my_domain",
        "get_data",
        ...,
        blocking=True,
        return_response=True
    )
```

## Removing Services

Unregister a service when your component unloads:

```python theme={null}
# Async context
hass.services.async_remove("my_domain", "my_service")

# Fires EVENT_SERVICE_REMOVED
```

<Note>
  Always remove services during component cleanup to prevent orphaned service registrations that reference unloaded code.
</Note>

## Service Events

Service operations trigger events:

### EVENT\_SERVICE\_REGISTERED

Fired when a service is registered:

```python theme={null}
{
    "domain": "my_domain",
    "service": "my_service"
}
```

### EVENT\_CALL\_SERVICE

Fired when a service is called:

```python theme={null}
{
    "domain": "light",
    "service": "turn_on",
    "service_data": {
        "entity_id": "light.living_room",
        "brightness": 255
    }
}
```

### EVENT\_SERVICE\_REMOVED

Fired when a service is unregistered:

```python theme={null}
{
    "domain": "my_domain",
    "service": "my_service"
}
```

## Error Handling

Service calls can raise several exceptions:

```python theme={null}
from homeassistant.exceptions import (
    ServiceNotFound,
    ServiceValidationError,
    Unauthorized,
)

try:
    await hass.services.async_call(
        "my_domain",
        "my_service",
        service_data,
        blocking=True,
    )
except ServiceNotFound:
    _LOGGER.error("Service not found")
except ServiceValidationError as err:
    _LOGGER.error("Invalid service data: %s", err)
except Unauthorized:
    _LOGGER.error("Unauthorized to call service")
```

<Warning>
  When `blocking=False`, exceptions are caught and logged automatically by `_run_service_call_catch_exceptions`. Only blocking calls propagate exceptions to the caller.
</Warning>

## Best Practices

<Tip>
  **Use async handlers**: Always prefer async service handlers for better performance\
  **Define schemas**: Always define validation schemas to catch errors early\
  **Return JSON-serializable data**: Response data must be JSON-serializable\
  **Handle contexts**: Propagate contexts to maintain audit trails\
  **Document services**: Use service descriptions and description\_placeholders
</Tip>

## Service Description Files

Services are documented in `services.yaml`:

```yaml theme={null}
my_service:
  name: My Service
  description: Performs a custom action
  fields:
    entity_id:
      description: Entity to control
      example: "light.living_room"
      required: true
      selector:
        entity:
          domain: light
    value:
      description: Value to set
      example: 50
      required: true
      selector:
        number:
          min: 0
          max: 100
          mode: slider
```

This enables automatic UI generation and API documentation.

## Performance Considerations

1. **Use callbacks**: Decorate simple services with `@callback` when possible
2. **Avoid blocking I/O**: Use async operations or executor jobs for I/O
3. **Validate early**: Schema validation prevents invalid data from reaching handlers
4. **Background execution**: Use `blocking=False` for fire-and-forget operations
5. **Batch operations**: Consider batching multiple entity operations in a single service
