mirror of
https://github.com/home-assistant/core.git
synced 2026-03-03 22:37:03 +01:00
Compare commits
1 Commits
dev
...
config-yam
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f37a58798 |
@@ -1,46 +0,0 @@
|
||||
---
|
||||
name: github-pr-reviewer
|
||||
description: Review a GitHub pull request and provide feedback comments. Use when the user says "review the current PR" or asks to review a specific PR.
|
||||
---
|
||||
|
||||
# Review GitHub Pull Request
|
||||
|
||||
## Preparation:
|
||||
- Check if the local commit matches the last one in the PR. If not, checkout the PR locally using 'gh pr checkout'.
|
||||
- CRITICAL: If 'gh pr checkout' fails for ANY reason, you MUST immediately STOP.
|
||||
- Do NOT attempt any workarounds.
|
||||
- Do NOT proceed with the review.
|
||||
- ALERT about the failure and WAIT for instructions.
|
||||
- This is a hard requirement - no exceptions.
|
||||
|
||||
## Follow these steps:
|
||||
1. Use 'gh pr view' to get the PR details and description.
|
||||
2. Use 'gh pr diff' to see all the changes in the PR.
|
||||
3. Analyze the code changes for:
|
||||
- Code quality and style consistency
|
||||
- Potential bugs or issues
|
||||
- Performance implications
|
||||
- Security concerns
|
||||
- Test coverage
|
||||
- Documentation updates if needed
|
||||
4. Ensure any existing review comments have been addressed.
|
||||
5. Generate constructive review comments in the CONSOLE. DO NOT POST TO GITHUB YOURSELF.
|
||||
|
||||
## IMPORTANT:
|
||||
- Just review. DO NOT make any changes
|
||||
- Be constructive and specific in your comments
|
||||
- Suggest improvements where appropriate
|
||||
- Only provide review feedback in the CONSOLE. DO NOT ACT ON GITHUB.
|
||||
- No need to run tests or linters, just review the code changes.
|
||||
- No need to highlight things that are already good.
|
||||
|
||||
## Output format:
|
||||
- List specific comments for each file/line that needs attention
|
||||
- In the end, summarize with an overall assessment (approve, request changes, or comment) and bullet point list of changes suggested, if any.
|
||||
- Example output:
|
||||
```
|
||||
Overall assessment: request changes.
|
||||
- [CRITICAL] Memory leak in homeassistant/components/sensor/my_sensor.py:143
|
||||
- [PROBLEM] Inefficient algorithm in homeassistant/helpers/data_processing.py:87
|
||||
- [SUGGESTION] Improve variable naming in homeassistant/helpers/config_validation.py:45
|
||||
```
|
||||
862
.github/copilot-instructions.md
vendored
862
.github/copilot-instructions.md
vendored
@@ -331,6 +331,864 @@ class MyCoordinator(DataUpdateCoordinator[MyData]):
|
||||
```
|
||||
|
||||
|
||||
# Skills
|
||||
# Skill: Home Assistant Integration knowledge
|
||||
|
||||
- Home Assistant Integration knowledge: .claude/skills/integrations/SKILL.md
|
||||
### File Locations
|
||||
- **Integration code**: `./homeassistant/components/<integration_domain>/`
|
||||
- **Integration tests**: `./tests/components/<integration_domain>/`
|
||||
|
||||
## Integration Templates
|
||||
|
||||
### Standard Integration Structure
|
||||
```
|
||||
homeassistant/components/my_integration/
|
||||
├── __init__.py # Entry point with async_setup_entry
|
||||
├── manifest.json # Integration metadata and dependencies
|
||||
├── const.py # Domain and constants
|
||||
├── config_flow.py # UI configuration flow
|
||||
├── coordinator.py # Data update coordinator (if needed)
|
||||
├── entity.py # Base entity class (if shared patterns)
|
||||
├── sensor.py # Sensor platform
|
||||
├── strings.json # User-facing text and translations
|
||||
├── services.yaml # Service definitions (if applicable)
|
||||
└── quality_scale.yaml # Quality scale rule status
|
||||
```
|
||||
|
||||
An integration can have platforms as needed (e.g., `sensor.py`, `switch.py`, etc.). The following platforms have extra guidelines:
|
||||
- **Diagnostics**: [`platform-diagnostics.md`](platform-diagnostics.md) for diagnostic data collection
|
||||
<REFERENCE platform-diagnostics.md>
|
||||
# Integration Diagnostics
|
||||
|
||||
Platform exists as `homeassistant/components/<domain>/diagnostics.py`.
|
||||
|
||||
- **Required**: Implement diagnostic data collection
|
||||
- **Implementation**:
|
||||
```python
|
||||
TO_REDACT = [CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE]
|
||||
|
||||
async def async_get_config_entry_diagnostics(
|
||||
hass: HomeAssistant, entry: MyConfigEntry
|
||||
) -> dict[str, Any]:
|
||||
"""Return diagnostics for a config entry."""
|
||||
return {
|
||||
"entry_data": async_redact_data(entry.data, TO_REDACT),
|
||||
"data": entry.runtime_data.data,
|
||||
}
|
||||
```
|
||||
- **Security**: Never expose passwords, tokens, or sensitive coordinates
|
||||
<END REFERENCE platform-diagnostics.md>
|
||||
|
||||
- **Repairs**: [`platform-repairs.md`](platform-repairs.md) for user-actionable repair issues
|
||||
<REFERENCE platform-repairs.md>
|
||||
# Repairs platform
|
||||
|
||||
Platform exists as `homeassistant/components/<domain>/repairs.py`.
|
||||
|
||||
- **Actionable Issues Required**: All repair issues must be actionable for end users
|
||||
- **Issue Content Requirements**:
|
||||
- Clearly explain what is happening
|
||||
- Provide specific steps users need to take to resolve the issue
|
||||
- Use friendly, helpful language
|
||||
- Include relevant context (device names, error details, etc.)
|
||||
- **Implementation**:
|
||||
```python
|
||||
ir.async_create_issue(
|
||||
hass,
|
||||
DOMAIN,
|
||||
"outdated_version",
|
||||
is_fixable=False,
|
||||
issue_domain=DOMAIN,
|
||||
severity=ir.IssueSeverity.ERROR,
|
||||
translation_key="outdated_version",
|
||||
)
|
||||
```
|
||||
- **Translation Strings Requirements**: Must contain user-actionable text in `strings.json`:
|
||||
```json
|
||||
{
|
||||
"issues": {
|
||||
"outdated_version": {
|
||||
"title": "Device firmware is outdated",
|
||||
"description": "Your device firmware version {current_version} is below the minimum required version {min_version}. To fix this issue: 1) Open the manufacturer's mobile app, 2) Navigate to device settings, 3) Select 'Update Firmware', 4) Wait for the update to complete, then 5) Restart Home Assistant."
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
- **String Content Must Include**:
|
||||
- What the problem is
|
||||
- Why it matters
|
||||
- Exact steps to resolve (numbered list when multiple steps)
|
||||
- What to expect after following the steps
|
||||
- **Avoid Vague Instructions**: Don't just say "update firmware" - provide specific steps
|
||||
- **Severity Guidelines**:
|
||||
- `CRITICAL`: Reserved for extreme scenarios only
|
||||
- `ERROR`: Requires immediate user attention
|
||||
- `WARNING`: Indicates future potential breakage
|
||||
- **Additional Attributes**:
|
||||
```python
|
||||
ir.async_create_issue(
|
||||
hass, DOMAIN, "issue_id",
|
||||
breaks_in_ha_version="2024.1.0",
|
||||
is_fixable=True,
|
||||
is_persistent=True,
|
||||
severity=ir.IssueSeverity.ERROR,
|
||||
translation_key="issue_description",
|
||||
)
|
||||
```
|
||||
- Only create issues for problems users can potentially resolve
|
||||
<END REFERENCE platform-repairs.md>
|
||||
|
||||
|
||||
### Minimal Integration Checklist
|
||||
- [ ] `manifest.json` with required fields (domain, name, codeowners, etc.)
|
||||
- [ ] `__init__.py` with `async_setup_entry` and `async_unload_entry`
|
||||
- [ ] `config_flow.py` with UI configuration support
|
||||
- [ ] `const.py` with `DOMAIN` constant
|
||||
- [ ] `strings.json` with at least config flow text
|
||||
- [ ] Platform files (`sensor.py`, etc.) as needed
|
||||
- [ ] `quality_scale.yaml` with rule status tracking
|
||||
|
||||
## Integration Quality Scale
|
||||
|
||||
Home Assistant uses an Integration Quality Scale to ensure code quality and consistency. The quality level determines which rules apply:
|
||||
|
||||
### Quality Scale Levels
|
||||
- **Bronze**: Basic requirements (ALL Bronze rules are mandatory)
|
||||
- **Silver**: Enhanced functionality
|
||||
- **Gold**: Advanced features
|
||||
- **Platinum**: Highest quality standards
|
||||
|
||||
### Quality Scale Progression
|
||||
- **Bronze → Silver**: Add entity unavailability, parallel updates, auth flows
|
||||
- **Silver → Gold**: Add device management, diagnostics, translations
|
||||
- **Gold → Platinum**: Add strict typing, async dependencies, websession injection
|
||||
|
||||
### How Rules Apply
|
||||
1. **Check `manifest.json`**: Look for `"quality_scale"` key to determine integration level
|
||||
2. **Bronze Rules**: Always required for any integration with quality scale
|
||||
3. **Higher Tier Rules**: Only apply if integration targets that tier or higher
|
||||
4. **Rule Status**: Check `quality_scale.yaml` in integration folder for:
|
||||
- `done`: Rule implemented
|
||||
- `exempt`: Rule doesn't apply (with reason in comment)
|
||||
- `todo`: Rule needs implementation
|
||||
|
||||
### Example `quality_scale.yaml` Structure
|
||||
```yaml
|
||||
rules:
|
||||
# Bronze (mandatory)
|
||||
config-flow: done
|
||||
entity-unique-id: done
|
||||
action-setup:
|
||||
status: exempt
|
||||
comment: Integration does not register custom actions.
|
||||
|
||||
# Silver (if targeting Silver+)
|
||||
entity-unavailable: done
|
||||
parallel-updates: done
|
||||
|
||||
# Gold (if targeting Gold+)
|
||||
devices: done
|
||||
diagnostics: done
|
||||
|
||||
# Platinum (if targeting Platinum)
|
||||
strict-typing: done
|
||||
```
|
||||
|
||||
**When Reviewing/Creating Code**: Always check the integration's quality scale level and exemption status before applying rules.
|
||||
|
||||
## Code Organization
|
||||
|
||||
### Core Locations
|
||||
- Shared constants: `homeassistant/const.py` (use these instead of hardcoding)
|
||||
- Integration structure:
|
||||
- `homeassistant/components/{domain}/const.py` - Constants
|
||||
- `homeassistant/components/{domain}/models.py` - Data models
|
||||
- `homeassistant/components/{domain}/coordinator.py` - Update coordinator
|
||||
- `homeassistant/components/{domain}/config_flow.py` - Configuration flow
|
||||
- `homeassistant/components/{domain}/{platform}.py` - Platform implementations
|
||||
|
||||
### Common Modules
|
||||
- **coordinator.py**: Centralize data fetching logic
|
||||
```python
|
||||
class MyCoordinator(DataUpdateCoordinator[MyData]):
|
||||
def __init__(self, hass: HomeAssistant, client: MyClient, config_entry: ConfigEntry) -> None:
|
||||
super().__init__(
|
||||
hass,
|
||||
logger=LOGGER,
|
||||
name=DOMAIN,
|
||||
update_interval=timedelta(minutes=1),
|
||||
config_entry=config_entry, # ✅ Pass config_entry - it's accepted and recommended
|
||||
)
|
||||
```
|
||||
- **entity.py**: Base entity definitions to reduce duplication
|
||||
```python
|
||||
class MyEntity(CoordinatorEntity[MyCoordinator]):
|
||||
_attr_has_entity_name = True
|
||||
```
|
||||
|
||||
### Runtime Data Storage
|
||||
- **Use ConfigEntry.runtime_data**: Store non-persistent runtime data
|
||||
```python
|
||||
type MyIntegrationConfigEntry = ConfigEntry[MyClient]
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: MyIntegrationConfigEntry) -> bool:
|
||||
client = MyClient(entry.data[CONF_HOST])
|
||||
entry.runtime_data = client
|
||||
```
|
||||
|
||||
### Manifest Requirements
|
||||
- **Required Fields**: `domain`, `name`, `codeowners`, `integration_type`, `documentation`, `requirements`
|
||||
- **Integration Types**: `device`, `hub`, `service`, `system`, `helper`
|
||||
- **IoT Class**: Always specify connectivity method (e.g., `cloud_polling`, `local_polling`, `local_push`)
|
||||
- **Discovery Methods**: Add when applicable: `zeroconf`, `dhcp`, `bluetooth`, `ssdp`, `usb`
|
||||
- **Dependencies**: Include platform dependencies (e.g., `application_credentials`, `bluetooth_adapters`)
|
||||
|
||||
### Config Flow Patterns
|
||||
- **Version Control**: Always set `VERSION = 1` and `MINOR_VERSION = 1`
|
||||
- **Unique ID Management**:
|
||||
```python
|
||||
await self.async_set_unique_id(device_unique_id)
|
||||
self._abort_if_unique_id_configured()
|
||||
```
|
||||
- **Error Handling**: Define errors in `strings.json` under `config.error`
|
||||
- **Step Methods**: Use standard naming (`async_step_user`, `async_step_discovery`, etc.)
|
||||
|
||||
### Integration Ownership
|
||||
- **manifest.json**: Add GitHub usernames to `codeowners`:
|
||||
```json
|
||||
{
|
||||
"domain": "my_integration",
|
||||
"name": "My Integration",
|
||||
"codeowners": ["@me"]
|
||||
}
|
||||
```
|
||||
|
||||
### Async Dependencies (Platinum)
|
||||
- **Requirement**: All dependencies must use asyncio
|
||||
- Ensures efficient task handling without thread context switching
|
||||
|
||||
### WebSession Injection (Platinum)
|
||||
- **Pass WebSession**: Support passing web sessions to dependencies
|
||||
```python
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: MyConfigEntry) -> bool:
|
||||
"""Set up integration from config entry."""
|
||||
client = MyClient(entry.data[CONF_HOST], async_get_clientsession(hass))
|
||||
```
|
||||
- For cookies: Use `async_create_clientsession` (aiohttp) or `create_async_httpx_client` (httpx)
|
||||
|
||||
### Data Update Coordinator
|
||||
- **Standard Pattern**: Use for efficient data management
|
||||
```python
|
||||
class MyCoordinator(DataUpdateCoordinator):
|
||||
def __init__(self, hass: HomeAssistant, client: MyClient, config_entry: ConfigEntry) -> None:
|
||||
super().__init__(
|
||||
hass,
|
||||
logger=LOGGER,
|
||||
name=DOMAIN,
|
||||
update_interval=timedelta(minutes=5),
|
||||
config_entry=config_entry, # ✅ Pass config_entry - it's accepted and recommended
|
||||
)
|
||||
self.client = client
|
||||
|
||||
async def _async_update_data(self):
|
||||
try:
|
||||
return await self.client.fetch_data()
|
||||
except ApiError as err:
|
||||
raise UpdateFailed(f"API communication error: {err}")
|
||||
```
|
||||
- **Error Types**: Use `UpdateFailed` for API errors, `ConfigEntryAuthFailed` for auth issues
|
||||
- **Config Entry**: Always pass `config_entry` parameter to coordinator - it's accepted and recommended
|
||||
|
||||
## Integration Guidelines
|
||||
|
||||
### Configuration Flow
|
||||
- **UI Setup Required**: All integrations must support configuration via UI
|
||||
- **Manifest**: Set `"config_flow": true` in `manifest.json`
|
||||
- **Data Storage**:
|
||||
- Connection-critical config: Store in `ConfigEntry.data`
|
||||
- Non-critical settings: Store in `ConfigEntry.options`
|
||||
- **Validation**: Always validate user input before creating entries
|
||||
- **Config Entry Naming**:
|
||||
- ❌ Do NOT allow users to set config entry names in config flows
|
||||
- Names are automatically generated or can be customized later in UI
|
||||
- ✅ Exception: Helper integrations MAY allow custom names in config flow
|
||||
- **Connection Testing**: Test device/service connection during config flow:
|
||||
```python
|
||||
try:
|
||||
await client.get_data()
|
||||
except MyException:
|
||||
errors["base"] = "cannot_connect"
|
||||
```
|
||||
- **Duplicate Prevention**: Prevent duplicate configurations:
|
||||
```python
|
||||
# Using unique ID
|
||||
await self.async_set_unique_id(identifier)
|
||||
self._abort_if_unique_id_configured()
|
||||
|
||||
# Using unique data
|
||||
self._async_abort_entries_match({CONF_HOST: user_input[CONF_HOST]})
|
||||
```
|
||||
|
||||
### Reauthentication Support
|
||||
- **Required Method**: Implement `async_step_reauth` in config flow
|
||||
- **Credential Updates**: Allow users to update credentials without re-adding
|
||||
- **Validation**: Verify account matches existing unique ID:
|
||||
```python
|
||||
await self.async_set_unique_id(user_id)
|
||||
self._abort_if_unique_id_mismatch(reason="wrong_account")
|
||||
return self.async_update_reload_and_abort(
|
||||
self._get_reauth_entry(),
|
||||
data_updates={CONF_API_TOKEN: user_input[CONF_API_TOKEN]}
|
||||
)
|
||||
```
|
||||
|
||||
### Reconfiguration Flow
|
||||
- **Purpose**: Allow configuration updates without removing device
|
||||
- **Implementation**: Add `async_step_reconfigure` method
|
||||
- **Validation**: Prevent changing underlying account with `_abort_if_unique_id_mismatch`
|
||||
|
||||
### Device Discovery
|
||||
- **Manifest Configuration**: Add discovery method (zeroconf, dhcp, etc.)
|
||||
```json
|
||||
{
|
||||
"zeroconf": ["_mydevice._tcp.local."]
|
||||
}
|
||||
```
|
||||
- **Discovery Handler**: Implement appropriate `async_step_*` method:
|
||||
```python
|
||||
async def async_step_zeroconf(self, discovery_info):
|
||||
"""Handle zeroconf discovery."""
|
||||
await self.async_set_unique_id(discovery_info.properties["serialno"])
|
||||
self._abort_if_unique_id_configured(updates={CONF_HOST: discovery_info.host})
|
||||
```
|
||||
- **Network Updates**: Use discovery to update dynamic IP addresses
|
||||
|
||||
### Network Discovery Implementation
|
||||
- **Zeroconf/mDNS**: Use async instances
|
||||
```python
|
||||
aiozc = await zeroconf.async_get_async_instance(hass)
|
||||
```
|
||||
- **SSDP Discovery**: Register callbacks with cleanup
|
||||
```python
|
||||
entry.async_on_unload(
|
||||
ssdp.async_register_callback(
|
||||
hass, _async_discovered_device,
|
||||
{"st": "urn:schemas-upnp-org:device:ZonePlayer:1"}
|
||||
)
|
||||
)
|
||||
```
|
||||
|
||||
### Bluetooth Integration
|
||||
- **Manifest Dependencies**: Add `bluetooth_adapters` to dependencies
|
||||
- **Connectable**: Set `"connectable": true` for connection-required devices
|
||||
- **Scanner Usage**: Always use shared scanner instance
|
||||
```python
|
||||
scanner = bluetooth.async_get_scanner()
|
||||
entry.async_on_unload(
|
||||
bluetooth.async_register_callback(
|
||||
hass, _async_discovered_device,
|
||||
{"service_uuid": "example_uuid"},
|
||||
bluetooth.BluetoothScanningMode.ACTIVE
|
||||
)
|
||||
)
|
||||
```
|
||||
- **Connection Handling**: Never reuse `BleakClient` instances, use 10+ second timeouts
|
||||
|
||||
### Setup Validation
|
||||
- **Test Before Setup**: Verify integration can be set up in `async_setup_entry`
|
||||
- **Exception Handling**:
|
||||
- `ConfigEntryNotReady`: Device offline or temporary failure
|
||||
- `ConfigEntryAuthFailed`: Authentication issues
|
||||
- `ConfigEntryError`: Unresolvable setup problems
|
||||
|
||||
### Config Entry Unloading
|
||||
- **Required**: Implement `async_unload_entry` for runtime removal/reload
|
||||
- **Platform Unloading**: Use `hass.config_entries.async_unload_platforms`
|
||||
- **Cleanup**: Register callbacks with `entry.async_on_unload`:
|
||||
```python
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: MyConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||
entry.runtime_data.listener() # Clean up resources
|
||||
return unload_ok
|
||||
```
|
||||
|
||||
### Service Actions
|
||||
- **Registration**: Register all service actions in `async_setup`, NOT in `async_setup_entry`
|
||||
- **Validation**: Check config entry existence and loaded state:
|
||||
```python
|
||||
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
async def service_action(call: ServiceCall) -> ServiceResponse:
|
||||
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")
|
||||
```
|
||||
- **Exception Handling**: Raise appropriate exceptions:
|
||||
```python
|
||||
# For invalid input
|
||||
if end_date < start_date:
|
||||
raise ServiceValidationError("End date must be after start date")
|
||||
|
||||
# For service errors
|
||||
try:
|
||||
await client.set_schedule(start_date, end_date)
|
||||
except MyConnectionError as err:
|
||||
raise HomeAssistantError("Could not connect to the schedule") from err
|
||||
```
|
||||
|
||||
### Service Registration Patterns
|
||||
- **Entity Services**: Register on platform setup
|
||||
```python
|
||||
platform.async_register_entity_service(
|
||||
"my_entity_service",
|
||||
{vol.Required("parameter"): cv.string},
|
||||
"handle_service_method"
|
||||
)
|
||||
```
|
||||
- **Service Schema**: Always validate input
|
||||
```python
|
||||
SERVICE_SCHEMA = vol.Schema({
|
||||
vol.Required("entity_id"): cv.entity_ids,
|
||||
vol.Required("parameter"): cv.string,
|
||||
vol.Optional("timeout", default=30): cv.positive_int,
|
||||
})
|
||||
```
|
||||
- **Services File**: Create `services.yaml` with descriptions and field definitions
|
||||
|
||||
### Polling
|
||||
- Use update coordinator pattern when possible
|
||||
- **Polling intervals are NOT user-configurable**: Never add scan_interval, update_interval, or polling frequency options to config flows or config entries
|
||||
- **Integration determines intervals**: Set `update_interval` programmatically based on integration logic, not user input
|
||||
- **Minimum Intervals**:
|
||||
- Local network: 5 seconds
|
||||
- Cloud services: 60 seconds
|
||||
- **Parallel Updates**: Specify number of concurrent updates:
|
||||
```python
|
||||
PARALLEL_UPDATES = 1 # Serialize updates to prevent overwhelming device
|
||||
# OR
|
||||
PARALLEL_UPDATES = 0 # Unlimited (for coordinator-based or read-only)
|
||||
```
|
||||
|
||||
## Entity Development
|
||||
|
||||
### Unique IDs
|
||||
- **Required**: Every entity must have a unique ID for registry tracking
|
||||
- Must be unique per platform (not per integration)
|
||||
- Don't include integration domain or platform in ID
|
||||
- **Implementation**:
|
||||
```python
|
||||
class MySensor(SensorEntity):
|
||||
def __init__(self, device_id: str) -> None:
|
||||
self._attr_unique_id = f"{device_id}_temperature"
|
||||
```
|
||||
|
||||
**Acceptable ID Sources**:
|
||||
- Device serial numbers
|
||||
- MAC addresses (formatted using `format_mac` from device registry)
|
||||
- Physical identifiers (printed/EEPROM)
|
||||
- Config entry ID as last resort: `f"{entry.entry_id}-battery"`
|
||||
|
||||
**Never Use**:
|
||||
- IP addresses, hostnames, URLs
|
||||
- Device names
|
||||
- Email addresses, usernames
|
||||
|
||||
### Entity Descriptions
|
||||
- **Lambda/Anonymous Functions**: Often used in EntityDescription for value transformation
|
||||
- **Multiline Lambdas**: When lambdas exceed line length, wrap in parentheses for readability
|
||||
- **Bad pattern**:
|
||||
```python
|
||||
SensorEntityDescription(
|
||||
key="temperature",
|
||||
name="Temperature",
|
||||
value_fn=lambda data: round(data["temp_value"] * 1.8 + 32, 1) if data.get("temp_value") is not None else None, # ❌ Too long
|
||||
)
|
||||
```
|
||||
- **Good pattern**:
|
||||
```python
|
||||
SensorEntityDescription(
|
||||
key="temperature",
|
||||
name="Temperature",
|
||||
value_fn=lambda data: ( # ✅ Parenthesis on same line as lambda
|
||||
round(data["temp_value"] * 1.8 + 32, 1)
|
||||
if data.get("temp_value") is not None
|
||||
else None
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
### Entity Naming
|
||||
- **Use has_entity_name**: Set `_attr_has_entity_name = True`
|
||||
- **For specific fields**:
|
||||
```python
|
||||
class MySensor(SensorEntity):
|
||||
_attr_has_entity_name = True
|
||||
def __init__(self, device: Device, field: str) -> None:
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, device.id)},
|
||||
name=device.name,
|
||||
)
|
||||
self._attr_name = field # e.g., "temperature", "humidity"
|
||||
```
|
||||
- **For device itself**: Set `_attr_name = None`
|
||||
|
||||
### Event Lifecycle Management
|
||||
- **Subscribe in `async_added_to_hass`**:
|
||||
```python
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Subscribe to events."""
|
||||
self.async_on_remove(
|
||||
self.client.events.subscribe("my_event", self._handle_event)
|
||||
)
|
||||
```
|
||||
- **Unsubscribe in `async_will_remove_from_hass`** if not using `async_on_remove`
|
||||
- Never subscribe in `__init__` or other methods
|
||||
|
||||
### State Handling
|
||||
- Unknown values: Use `None` (not "unknown" or "unavailable")
|
||||
- Availability: Implement `available()` property instead of using "unavailable" state
|
||||
|
||||
### Entity Availability
|
||||
- **Mark Unavailable**: When data cannot be fetched from device/service
|
||||
- **Coordinator Pattern**:
|
||||
```python
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
"""Return if entity is available."""
|
||||
return super().available and self.identifier in self.coordinator.data
|
||||
```
|
||||
- **Direct Update Pattern**:
|
||||
```python
|
||||
async def async_update(self) -> None:
|
||||
"""Update entity."""
|
||||
try:
|
||||
data = await self.client.get_data()
|
||||
except MyException:
|
||||
self._attr_available = False
|
||||
else:
|
||||
self._attr_available = True
|
||||
self._attr_native_value = data.value
|
||||
```
|
||||
|
||||
### Extra State Attributes
|
||||
- All attribute keys must always be present
|
||||
- Unknown values: Use `None`
|
||||
- Provide descriptive attributes
|
||||
|
||||
## Device Management
|
||||
|
||||
### Device Registry
|
||||
- **Create Devices**: Group related entities under devices
|
||||
- **Device Info**: Provide comprehensive metadata:
|
||||
```python
|
||||
_attr_device_info = DeviceInfo(
|
||||
connections={(CONNECTION_NETWORK_MAC, device.mac)},
|
||||
identifiers={(DOMAIN, device.id)},
|
||||
name=device.name,
|
||||
manufacturer="My Company",
|
||||
model="My Sensor",
|
||||
sw_version=device.version,
|
||||
)
|
||||
```
|
||||
- For services: Add `entry_type=DeviceEntryType.SERVICE`
|
||||
|
||||
### Dynamic Device Addition
|
||||
- **Auto-detect New Devices**: After initial setup
|
||||
- **Implementation Pattern**:
|
||||
```python
|
||||
def _check_device() -> None:
|
||||
current_devices = set(coordinator.data)
|
||||
new_devices = current_devices - known_devices
|
||||
if new_devices:
|
||||
known_devices.update(new_devices)
|
||||
async_add_entities([MySensor(coordinator, device_id) for device_id in new_devices])
|
||||
|
||||
entry.async_on_unload(coordinator.async_add_listener(_check_device))
|
||||
```
|
||||
|
||||
### Stale Device Removal
|
||||
- **Auto-remove**: When devices disappear from hub/account
|
||||
- **Device Registry Update**:
|
||||
```python
|
||||
device_registry.async_update_device(
|
||||
device_id=device.id,
|
||||
remove_config_entry_id=self.config_entry.entry_id,
|
||||
)
|
||||
```
|
||||
- **Manual Deletion**: Implement `async_remove_config_entry_device` when needed
|
||||
|
||||
### Entity Categories
|
||||
- **Required**: Assign appropriate category to entities
|
||||
- **Implementation**: Set `_attr_entity_category`
|
||||
```python
|
||||
class MySensor(SensorEntity):
|
||||
_attr_entity_category = EntityCategory.DIAGNOSTIC
|
||||
```
|
||||
- Categories include: `DIAGNOSTIC` for system/technical information
|
||||
|
||||
### Device Classes
|
||||
- **Use When Available**: Set appropriate device class for entity type
|
||||
```python
|
||||
class MyTemperatureSensor(SensorEntity):
|
||||
_attr_device_class = SensorDeviceClass.TEMPERATURE
|
||||
```
|
||||
- Provides context for: unit conversion, voice control, UI representation
|
||||
|
||||
### Disabled by Default
|
||||
- **Disable Noisy/Less Popular Entities**: Reduce resource usage
|
||||
```python
|
||||
class MySignalStrengthSensor(SensorEntity):
|
||||
_attr_entity_registry_enabled_default = False
|
||||
```
|
||||
- Target: frequently changing states, technical diagnostics
|
||||
|
||||
### Entity Translations
|
||||
- **Required with has_entity_name**: Support international users
|
||||
- **Implementation**:
|
||||
```python
|
||||
class MySensor(SensorEntity):
|
||||
_attr_has_entity_name = True
|
||||
_attr_translation_key = "phase_voltage"
|
||||
```
|
||||
- Create `strings.json` with translations:
|
||||
```json
|
||||
{
|
||||
"entity": {
|
||||
"sensor": {
|
||||
"phase_voltage": {
|
||||
"name": "Phase voltage"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Exception Translations (Gold)
|
||||
- **Translatable Errors**: Use translation keys for user-facing exceptions
|
||||
- **Implementation**:
|
||||
```python
|
||||
raise ServiceValidationError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="end_date_before_start_date",
|
||||
)
|
||||
```
|
||||
- Add to `strings.json`:
|
||||
```json
|
||||
{
|
||||
"exceptions": {
|
||||
"end_date_before_start_date": {
|
||||
"message": "The end date cannot be before the start date."
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Icon Translations (Gold)
|
||||
- **Dynamic Icons**: Support state and range-based icon selection
|
||||
- **State-based Icons**:
|
||||
```json
|
||||
{
|
||||
"entity": {
|
||||
"sensor": {
|
||||
"tree_pollen": {
|
||||
"default": "mdi:tree",
|
||||
"state": {
|
||||
"high": "mdi:tree-outline"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
- **Range-based Icons** (for numeric values):
|
||||
```json
|
||||
{
|
||||
"entity": {
|
||||
"sensor": {
|
||||
"battery_level": {
|
||||
"default": "mdi:battery-unknown",
|
||||
"range": {
|
||||
"0": "mdi:battery-outline",
|
||||
"90": "mdi:battery-90",
|
||||
"100": "mdi:battery"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Testing Requirements
|
||||
|
||||
- **Location**: `tests/components/{domain}/`
|
||||
- **Coverage Requirement**: Above 95% test coverage for all modules
|
||||
- **Best Practices**:
|
||||
- Use pytest fixtures from `tests.common`
|
||||
- Mock all external dependencies
|
||||
- Use snapshots for complex data structures
|
||||
- Follow existing test patterns
|
||||
|
||||
### Config Flow Testing
|
||||
- **100% Coverage Required**: All config flow paths must be tested
|
||||
- **Test Scenarios**:
|
||||
- All flow initiation methods (user, discovery, import)
|
||||
- Successful configuration paths
|
||||
- Error recovery scenarios
|
||||
- Prevention of duplicate entries
|
||||
- Flow completion after errors
|
||||
|
||||
### Testing
|
||||
- **Integration-specific tests** (recommended):
|
||||
```bash
|
||||
pytest ./tests/components/<integration_domain> \
|
||||
--cov=homeassistant.components.<integration_domain> \
|
||||
--cov-report term-missing \
|
||||
--durations-min=1 \
|
||||
--durations=0 \
|
||||
--numprocesses=auto
|
||||
```
|
||||
|
||||
### Testing Best Practices
|
||||
- **Never access `hass.data` directly** - Use fixtures and proper integration setup instead
|
||||
- **Use snapshot testing** - For verifying entity states and attributes
|
||||
- **Test through integration setup** - Don't test entities in isolation
|
||||
- **Mock external APIs** - Use fixtures with realistic JSON data
|
||||
- **Verify registries** - Ensure entities are properly registered with devices
|
||||
|
||||
### Config Flow Testing Template
|
||||
```python
|
||||
async def test_user_flow_success(hass, mock_api):
|
||||
"""Test successful user flow."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
# Test form submission
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input=TEST_USER_INPUT
|
||||
)
|
||||
assert result["type"] == FlowResultType.CREATE_ENTRY
|
||||
assert result["title"] == "My Device"
|
||||
assert result["data"] == TEST_USER_INPUT
|
||||
|
||||
async def test_flow_connection_error(hass, mock_api_error):
|
||||
"""Test connection error handling."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input=TEST_USER_INPUT
|
||||
)
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["errors"] == {"base": "cannot_connect"}
|
||||
```
|
||||
|
||||
### Entity Testing Patterns
|
||||
```python
|
||||
@pytest.fixture
|
||||
def platforms() -> list[Platform]:
|
||||
"""Overridden fixture to specify platforms to test."""
|
||||
return [Platform.SENSOR] # Or another specific platform as needed.
|
||||
|
||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default", "init_integration")
|
||||
async def test_entities(
|
||||
hass: HomeAssistant,
|
||||
snapshot: SnapshotAssertion,
|
||||
entity_registry: er.EntityRegistry,
|
||||
device_registry: dr.DeviceRegistry,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test the sensor entities."""
|
||||
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
|
||||
|
||||
# Ensure entities are correctly assigned to device
|
||||
device_entry = device_registry.async_get_device(
|
||||
identifiers={(DOMAIN, "device_unique_id")}
|
||||
)
|
||||
assert device_entry
|
||||
entity_entries = er.async_entries_for_config_entry(
|
||||
entity_registry, mock_config_entry.entry_id
|
||||
)
|
||||
for entity_entry in entity_entries:
|
||||
assert entity_entry.device_id == device_entry.id
|
||||
```
|
||||
|
||||
### Mock Patterns
|
||||
```python
|
||||
# Modern integration fixture setup
|
||||
@pytest.fixture
|
||||
def mock_config_entry() -> MockConfigEntry:
|
||||
"""Return the default mocked config entry."""
|
||||
return MockConfigEntry(
|
||||
title="My Integration",
|
||||
domain=DOMAIN,
|
||||
data={CONF_HOST: "127.0.0.1", CONF_API_KEY: "test_key"},
|
||||
unique_id="device_unique_id",
|
||||
)
|
||||
|
||||
@pytest.fixture
|
||||
def mock_device_api() -> Generator[MagicMock]:
|
||||
"""Return a mocked device API."""
|
||||
with patch("homeassistant.components.my_integration.MyDeviceAPI", autospec=True) as api_mock:
|
||||
api = api_mock.return_value
|
||||
api.get_data.return_value = MyDeviceData.from_json(
|
||||
load_fixture("device_data.json", DOMAIN)
|
||||
)
|
||||
yield api
|
||||
|
||||
@pytest.fixture
|
||||
def platforms() -> list[Platform]:
|
||||
"""Fixture to specify platforms to test."""
|
||||
return PLATFORMS
|
||||
|
||||
@pytest.fixture
|
||||
async def init_integration(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_device_api: MagicMock,
|
||||
platforms: list[Platform],
|
||||
) -> MockConfigEntry:
|
||||
"""Set up the integration for testing."""
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
|
||||
with patch("homeassistant.components.my_integration.PLATFORMS", platforms):
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
return mock_config_entry
|
||||
```
|
||||
|
||||
## Debugging & Troubleshooting
|
||||
|
||||
### Common Issues & Solutions
|
||||
- **Integration won't load**: Check `manifest.json` syntax and required fields
|
||||
- **Entities not appearing**: Verify `unique_id` and `has_entity_name` implementation
|
||||
- **Config flow errors**: Check `strings.json` entries and error handling
|
||||
- **Discovery not working**: Verify manifest discovery configuration and callbacks
|
||||
- **Tests failing**: Check mock setup and async context
|
||||
|
||||
### Debug Logging Setup
|
||||
```python
|
||||
# Enable debug logging in tests
|
||||
caplog.set_level(logging.DEBUG, logger="my_integration")
|
||||
|
||||
# In integration code - use proper logging
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER.debug("Processing data: %s", data) # Use lazy logging
|
||||
```
|
||||
|
||||
### Validation Commands
|
||||
```bash
|
||||
# Check specific integration
|
||||
python -m script.hassfest --integration-path homeassistant/components/my_integration
|
||||
|
||||
# Validate quality scale
|
||||
# Check quality_scale.yaml against current rules
|
||||
|
||||
# Run integration tests with coverage
|
||||
pytest ./tests/components/my_integration \
|
||||
--cov=homeassistant.components.my_integration \
|
||||
--cov-report term-missing
|
||||
```
|
||||
|
||||
4
.github/workflows/builder.yml
vendored
4
.github/workflows/builder.yml
vendored
@@ -182,7 +182,7 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Download translations
|
||||
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
with:
|
||||
name: translations
|
||||
|
||||
@@ -544,7 +544,7 @@ jobs:
|
||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||
|
||||
- name: Download translations
|
||||
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
with:
|
||||
name: translations
|
||||
|
||||
|
||||
8
.github/workflows/ci.yaml
vendored
8
.github/workflows/ci.yaml
vendored
@@ -978,7 +978,7 @@ jobs:
|
||||
run: |
|
||||
echo "::add-matcher::.github/workflows/matchers/pytest-slow.json"
|
||||
- name: Download pytest_buckets
|
||||
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
with:
|
||||
name: pytest_buckets
|
||||
- name: Compile English translations
|
||||
@@ -1387,7 +1387,7 @@ jobs:
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Download all coverage artifacts
|
||||
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
with:
|
||||
pattern: coverage-*
|
||||
- name: Upload coverage to Codecov
|
||||
@@ -1558,7 +1558,7 @@ jobs:
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Download all coverage artifacts
|
||||
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
with:
|
||||
pattern: coverage-*
|
||||
- name: Upload coverage to Codecov
|
||||
@@ -1587,7 +1587,7 @@ jobs:
|
||||
&& needs.info.outputs.skip_coverage != 'true' && !cancelled()
|
||||
steps:
|
||||
- name: Download all coverage artifacts
|
||||
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
with:
|
||||
pattern: test-results-*
|
||||
- name: Upload test results to Codecov
|
||||
|
||||
4
.github/workflows/codeql.yml
vendored
4
.github/workflows/codeql.yml
vendored
@@ -28,11 +28,11 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4
|
||||
uses: github/codeql-action/init@9e907b5e64f6b83e7804b09294d44122997950d6 # v4.32.3
|
||||
with:
|
||||
languages: python
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4
|
||||
uses: github/codeql-action/analyze@9e907b5e64f6b83e7804b09294d44122997950d6 # v4.32.3
|
||||
with:
|
||||
category: "/language:python"
|
||||
|
||||
12
.github/workflows/wheels.yml
vendored
12
.github/workflows/wheels.yml
vendored
@@ -124,12 +124,12 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Download env_file
|
||||
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
with:
|
||||
name: env_file
|
||||
|
||||
- name: Download requirements_diff
|
||||
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
with:
|
||||
name: requirements_diff
|
||||
|
||||
@@ -175,17 +175,17 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Download env_file
|
||||
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
with:
|
||||
name: env_file
|
||||
|
||||
- name: Download requirements_diff
|
||||
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
with:
|
||||
name: requirements_diff
|
||||
|
||||
- name: Download requirements_all_wheels
|
||||
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
with:
|
||||
name: requirements_all_wheels
|
||||
|
||||
@@ -209,4 +209,4 @@ jobs:
|
||||
skip-binary: aiohttp;charset-normalizer;grpcio;multidict;SQLAlchemy;propcache;protobuf;pymicro-vad;yarl
|
||||
constraints: "homeassistant/package_constraints.txt"
|
||||
requirements-diff: "requirements_diff.txt"
|
||||
requirements: "requirements_all_wheels_${{ matrix.arch }}.txt"
|
||||
requirements: "requirements_all.txt"
|
||||
|
||||
126
CREATE_CONFIG_YAML.md
Normal file
126
CREATE_CONFIG_YAML.md
Normal file
@@ -0,0 +1,126 @@
|
||||
# Create `config.yaml` For Config Flows
|
||||
|
||||
## Goal
|
||||
Document the persisted config entry and subentry payloads in each integration's `config.yaml` under `config_entry`, using selector-based field metadata that is consistent with Home Assistant selectors.
|
||||
|
||||
The output must describe what is **actually stored** in config entries (`data`, `options`, and `subentries`), not just what is shown in forms.
|
||||
|
||||
## Required Files Per Integration
|
||||
For each integration with `"config_flow": true` in `manifest.json`, inspect:
|
||||
|
||||
1. `config_flow.py`
|
||||
2. `__init__.py` (for migration and runtime usage confirmation)
|
||||
3. `const.py` (for `CONF_*`, version constants, and aliases)
|
||||
4. `strings.json` / translations only as fallback for field names not inferable from code
|
||||
5. Existing `config.yaml` (target file)
|
||||
|
||||
## Version Rules
|
||||
1. Default version is `major: 1`, `minor: 1` when no explicit version is defined.
|
||||
2. Read `VERSION` and `MINOR_VERSION` from the config flow class.
|
||||
3. If the class uses constants (for example `CONFIG_FLOW_VERSION`), resolve them from `const.py`.
|
||||
4. Document all known config-entry versions when code clearly supports multiple versions:
|
||||
- Current version from config flow class.
|
||||
- Historical versions from explicit migration branches (for example `async_migrate_entry` checks in `__init__.py`).
|
||||
5. Apply the same version logic to subentries (default `1.1` when unspecified).
|
||||
|
||||
## Storage Target Rules (Critical)
|
||||
Always determine where values are persisted:
|
||||
|
||||
1. `ConfigFlow.async_create_entry(data=...)` -> persisted in config entry `data`.
|
||||
2. `ConfigFlow.async_create_entry(..., options=...)` -> persisted in config entry `options`.
|
||||
3. `OptionsFlow.async_create_entry(data=...)` -> persisted in config entry `options`.
|
||||
4. `SchemaConfigFlowHandler` (default implementation):
|
||||
- Config flow values are stored in `options`.
|
||||
- Config entry `data` is empty.
|
||||
- Exception: class overrides `async_create_entry` (then follow override).
|
||||
5. `async_update_reload_and_abort(..., data=..., options=...)` updates existing entry payloads and must align with documented fields.
|
||||
|
||||
## Form-To-Storage Mapping Rules
|
||||
When `user_input` is stored directly, form schema must be mirrored in `config.yaml`.
|
||||
|
||||
### Config Flow
|
||||
If step logic returns `async_create_entry(data=user_input)`:
|
||||
1. Find the matching `async_show_form(..., data_schema=...)` for that step.
|
||||
2. Extract all schema keys.
|
||||
3. Add those keys to `config_entry.versions[*].data.fields`.
|
||||
|
||||
### Options Flow
|
||||
If options step returns `async_create_entry(data=user_input)`:
|
||||
1. Extract step schema keys.
|
||||
2. Add those keys to `config_entry.versions[*].options.fields`.
|
||||
|
||||
### Dict Payloads
|
||||
If `async_create_entry(data={...})` (or via a local dict variable/function that clearly returns a dict):
|
||||
1. Extract literal keys.
|
||||
2. Add keys to the relevant persisted section (`data` or `options`).
|
||||
|
||||
## Helper Flow Rules
|
||||
### `register_discovery_flow(...)`
|
||||
Creates entry with `data={}` by default. Keep data empty unless integration overrides flow behavior elsewhere.
|
||||
|
||||
### `register_webhook_flow(...)`
|
||||
Creates entry with:
|
||||
1. `webhook_id`
|
||||
2. `cloudhook`
|
||||
|
||||
These must be documented in `config_entry.versions[*].data.fields`.
|
||||
|
||||
### `AbstractOAuth2FlowHandler`
|
||||
Default OAuth payload includes:
|
||||
1. `auth_implementation`
|
||||
2. `token`
|
||||
|
||||
If integration overrides `async_oauth_create_entry` and adds additional stored keys, include those too.
|
||||
|
||||
## Subentry Rules
|
||||
1. Find `async_get_supported_subentry_types(...)` mapping and subentry flow classes (`ConfigSubentryFlow`).
|
||||
2. For each `subentry_type`, document under:
|
||||
- `config_entry.subentries.<subentry_type>.versions`
|
||||
3. Extract persisted subentry payload keys from:
|
||||
- `async_create_entry(data=...)` in subentry flow
|
||||
- direct subentry update calls with explicit data payloads
|
||||
4. Apply required/default/selector extraction exactly as for main config/option flows.
|
||||
|
||||
## Field Metadata Rules
|
||||
Each field entry should include:
|
||||
1. `required` (true/false)
|
||||
2. `selector` (valid HA selector structure)
|
||||
3. Optional `default` and `example` when directly known from code
|
||||
|
||||
### Required Flag
|
||||
1. `vol.Required(...)` -> `required: true`
|
||||
2. `vol.Optional(...)` -> `required: false`
|
||||
3. Literal dict payloads without schema context -> `required: true` unless clearly optional in code path
|
||||
|
||||
### Selector Mapping
|
||||
Use explicit selector calls when present (for example `TextSelector`, `NumberSelector`, `BooleanSelector`, `LocationSelector`, `SelectSelector`, etc).
|
||||
|
||||
If schema uses plain validators:
|
||||
1. `bool` / `cv.boolean` -> `selector: { boolean: {} }`
|
||||
2. numeric validators -> `selector: { number: {} }`
|
||||
3. `vol.In(...)` / constrained choices -> `selector: { select: {} }`
|
||||
4. unknown / string-like -> `selector: { text: {} }`
|
||||
5. structured blobs (for example OAuth `token`) -> `selector: { object: {} }`
|
||||
|
||||
## Validation Checklist (Per Integration)
|
||||
1. `config.yaml` exists when `manifest.json` has `config_flow: true`.
|
||||
2. `config_entry.versions` contains correct version entries.
|
||||
3. Documented fields exactly match persisted payloads (`data` vs `options`).
|
||||
4. `required` and selector format are valid.
|
||||
5. `subentries` are documented when supported.
|
||||
6. No placeholder empty blocks where code stores actual fields.
|
||||
|
||||
## Final QA Commands
|
||||
Run after updates:
|
||||
|
||||
```bash
|
||||
python -m script.hassfest -p config_entry --action validate
|
||||
ruff check script/hassfest/config_entry.py
|
||||
```
|
||||
|
||||
## High-Risk Pitfalls
|
||||
1. Assuming fields in forms are always stored in `data` (wrong for `SchemaConfigFlowHandler`).
|
||||
2. Missing fields when `data=user_input` is used with a non-empty schema.
|
||||
3. Skipping helper flows (`register_webhook_flow`, OAuth2 base handler behavior).
|
||||
4. Ignoring options/subentry flows that store separate payloads.
|
||||
5. Using placeholders instead of integration-specific field definitions.
|
||||
@@ -70,7 +70,7 @@ from .const import (
|
||||
SIGNAL_BOOTSTRAP_INTEGRATIONS,
|
||||
)
|
||||
from .core_config import async_process_ha_core_config
|
||||
from .exceptions import HomeAssistantError, UnsupportedStorageVersionError
|
||||
from .exceptions import HomeAssistantError
|
||||
from .helpers import (
|
||||
area_registry,
|
||||
category_registry,
|
||||
@@ -433,56 +433,32 @@ def _init_blocking_io_modules_in_executor() -> None:
|
||||
is_docker_env()
|
||||
|
||||
|
||||
async def async_load_base_functionality(hass: core.HomeAssistant) -> bool:
|
||||
"""Load the registries and modules that will do blocking I/O.
|
||||
|
||||
Return whether loading succeeded.
|
||||
"""
|
||||
async def async_load_base_functionality(hass: core.HomeAssistant) -> None:
|
||||
"""Load the registries and modules that will do blocking I/O."""
|
||||
if DATA_REGISTRIES_LOADED in hass.data:
|
||||
return True
|
||||
|
||||
return
|
||||
hass.data[DATA_REGISTRIES_LOADED] = None
|
||||
entity.async_setup(hass)
|
||||
frame.async_setup(hass)
|
||||
template.async_setup(hass)
|
||||
translation.async_setup(hass)
|
||||
|
||||
recovery = hass.config.recovery_mode
|
||||
try:
|
||||
await asyncio.gather(
|
||||
create_eager_task(get_internal_store_manager(hass).async_initialize()),
|
||||
create_eager_task(area_registry.async_load(hass, load_empty=recovery)),
|
||||
create_eager_task(category_registry.async_load(hass, load_empty=recovery)),
|
||||
create_eager_task(device_registry.async_load(hass, load_empty=recovery)),
|
||||
create_eager_task(entity_registry.async_load(hass, load_empty=recovery)),
|
||||
create_eager_task(floor_registry.async_load(hass, load_empty=recovery)),
|
||||
create_eager_task(issue_registry.async_load(hass, load_empty=recovery)),
|
||||
create_eager_task(label_registry.async_load(hass, load_empty=recovery)),
|
||||
hass.async_add_executor_job(_init_blocking_io_modules_in_executor),
|
||||
create_eager_task(template.async_load_custom_templates(hass)),
|
||||
create_eager_task(restore_state.async_load(hass, load_empty=recovery)),
|
||||
create_eager_task(hass.config_entries.async_initialize()),
|
||||
create_eager_task(async_get_system_info(hass)),
|
||||
create_eager_task(condition.async_setup(hass)),
|
||||
create_eager_task(trigger.async_setup(hass)),
|
||||
)
|
||||
except UnsupportedStorageVersionError as err:
|
||||
# If we're already in recovery mode, we don't want to handle the exception
|
||||
# and activate recovery mode again, as that would lead to an infinite loop.
|
||||
if recovery:
|
||||
raise
|
||||
|
||||
_LOGGER.error(
|
||||
"Storage file %s was created by a newer version of Home Assistant"
|
||||
" (storage version %s > %s); activating recovery mode; on-disk data"
|
||||
" is preserved; upgrade Home Assistant or restore from a backup",
|
||||
err.storage_key,
|
||||
err.found_version,
|
||||
err.max_supported_version,
|
||||
)
|
||||
return False
|
||||
|
||||
return True
|
||||
await asyncio.gather(
|
||||
create_eager_task(get_internal_store_manager(hass).async_initialize()),
|
||||
create_eager_task(area_registry.async_load(hass)),
|
||||
create_eager_task(category_registry.async_load(hass)),
|
||||
create_eager_task(device_registry.async_load(hass)),
|
||||
create_eager_task(entity_registry.async_load(hass)),
|
||||
create_eager_task(floor_registry.async_load(hass)),
|
||||
create_eager_task(issue_registry.async_load(hass)),
|
||||
create_eager_task(label_registry.async_load(hass)),
|
||||
hass.async_add_executor_job(_init_blocking_io_modules_in_executor),
|
||||
create_eager_task(template.async_load_custom_templates(hass)),
|
||||
create_eager_task(restore_state.async_load(hass)),
|
||||
create_eager_task(hass.config_entries.async_initialize()),
|
||||
create_eager_task(async_get_system_info(hass)),
|
||||
create_eager_task(condition.async_setup(hass)),
|
||||
create_eager_task(trigger.async_setup(hass)),
|
||||
)
|
||||
|
||||
|
||||
async def async_from_config_dict(
|
||||
@@ -499,9 +475,7 @@ async def async_from_config_dict(
|
||||
# Prime custom component cache early so we know if registry entries are tied
|
||||
# to a custom integration
|
||||
await loader.async_get_custom_components(hass)
|
||||
|
||||
if not await async_load_base_functionality(hass):
|
||||
return None
|
||||
await async_load_base_functionality(hass)
|
||||
|
||||
# Set up core.
|
||||
_LOGGER.debug("Setting up %s", CORE_INTEGRATIONS)
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"domain": "ubisys",
|
||||
"name": "Ubisys",
|
||||
"iot_standards": ["zigbee"]
|
||||
}
|
||||
14
homeassistant/components/abode/config.yaml
Normal file
14
homeassistant/components/abode/config.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
polling:
|
||||
required: true
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
14
homeassistant/components/acaia/config.yaml
Normal file
14
homeassistant/components/acaia/config.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
is_new_style_scale:
|
||||
required: true
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
26
homeassistant/components/accuweather/config.yaml
Normal file
26
homeassistant/components/accuweather/config.yaml
Normal file
@@ -0,0 +1,26 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
api_key:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
latitude:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
longitude:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
name:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
@@ -191,7 +191,7 @@ class AccuWeatherEntity(
|
||||
{
|
||||
ATTR_FORECAST_TIME: utc_from_timestamp(item["EpochDate"]).isoformat(),
|
||||
ATTR_FORECAST_CLOUD_COVERAGE: item["CloudCoverDay"],
|
||||
ATTR_FORECAST_HUMIDITY: item["RelativeHumidityDay"].get("Average"),
|
||||
ATTR_FORECAST_HUMIDITY: item["RelativeHumidityDay"]["Average"],
|
||||
ATTR_FORECAST_NATIVE_TEMP: item["TemperatureMax"][ATTR_VALUE],
|
||||
ATTR_FORECAST_NATIVE_TEMP_LOW: item["TemperatureMin"][ATTR_VALUE],
|
||||
ATTR_FORECAST_NATIVE_APPARENT_TEMP: item["RealFeelTemperatureMax"][
|
||||
|
||||
14
homeassistant/components/acmeda/config.yaml
Normal file
14
homeassistant/components/acmeda/config.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
id:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
14
homeassistant/components/actron_air/config.yaml
Normal file
14
homeassistant/components/actron_air/config.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
api_token:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
18
homeassistant/components/adax/config.yaml
Normal file
18
homeassistant/components/adax/config.yaml
Normal file
@@ -0,0 +1,18 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 2
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
connection_type:
|
||||
required: true
|
||||
selector:
|
||||
select:
|
||||
options:
|
||||
- Cloud
|
||||
- Local
|
||||
default: Cloud
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
34
homeassistant/components/adguard/config.yaml
Normal file
34
homeassistant/components/adguard/config.yaml
Normal file
@@ -0,0 +1,34 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
host:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
password:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
port:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
ssl:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
username:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
verify_ssl:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
18
homeassistant/components/advantage_air/config.yaml
Normal file
18
homeassistant/components/advantage_air/config.yaml
Normal file
@@ -0,0 +1,18 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
ip_address:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
port:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
34
homeassistant/components/aemet/config.yaml
Normal file
34
homeassistant/components/aemet/config.yaml
Normal file
@@ -0,0 +1,34 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
api_key:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
latitude:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
longitude:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
name:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields:
|
||||
radar_updates:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
station_updates:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
subentries: {}
|
||||
14
homeassistant/components/aftership/config.yaml
Normal file
14
homeassistant/components/aftership/config.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
api_key:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
14
homeassistant/components/agent_dvr/config.yaml
Normal file
14
homeassistant/components/agent_dvr/config.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
server_url:
|
||||
required: true
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
14
homeassistant/components/airgradient/config.yaml
Normal file
14
homeassistant/components/airgradient/config.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
host:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
26
homeassistant/components/airly/config.yaml
Normal file
26
homeassistant/components/airly/config.yaml
Normal file
@@ -0,0 +1,26 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
api_key:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
latitude:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
longitude:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
name:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
30
homeassistant/components/airnow/config.yaml
Normal file
30
homeassistant/components/airnow/config.yaml
Normal file
@@ -0,0 +1,30 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 2
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
api_key:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
latitude:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
longitude:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
radius:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields:
|
||||
radius:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
subentries: {}
|
||||
22
homeassistant/components/airobot/config.yaml
Normal file
22
homeassistant/components/airobot/config.yaml
Normal file
@@ -0,0 +1,22 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
host:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
password:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
username:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
19
homeassistant/components/airos/config.yaml
Normal file
19
homeassistant/components/airos/config.yaml
Normal file
@@ -0,0 +1,19 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 2
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
advanced_settings:
|
||||
required: true
|
||||
selector:
|
||||
text: {}
|
||||
mac_address:
|
||||
required: true
|
||||
selector:
|
||||
select:
|
||||
options: []
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
18
homeassistant/components/airpatrol/config.yaml
Normal file
18
homeassistant/components/airpatrol/config.yaml
Normal file
@@ -0,0 +1,18 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
email:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
password:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
26
homeassistant/components/airq/config.yaml
Normal file
26
homeassistant/components/airq/config.yaml
Normal file
@@ -0,0 +1,26 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
ip_address:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
password:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields:
|
||||
clip_negatives:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
return_average:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
subentries: {}
|
||||
18
homeassistant/components/airthings/config.yaml
Normal file
18
homeassistant/components/airthings/config.yaml
Normal file
@@ -0,0 +1,18 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
secret:
|
||||
required: true
|
||||
selector:
|
||||
text: {}
|
||||
id:
|
||||
required: true
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
14
homeassistant/components/airthings_ble/config.yaml
Normal file
14
homeassistant/components/airthings_ble/config.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
device_model:
|
||||
required: true
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
14
homeassistant/components/airtouch4/config.yaml
Normal file
14
homeassistant/components/airtouch4/config.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
host:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
14
homeassistant/components/airtouch5/config.yaml
Normal file
14
homeassistant/components/airtouch5/config.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
host:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
18
homeassistant/components/airvisual/config.yaml
Normal file
18
homeassistant/components/airvisual/config.yaml
Normal file
@@ -0,0 +1,18 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 3
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
integration_type:
|
||||
required: true
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields:
|
||||
show_on_map:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
subentries: {}
|
||||
18
homeassistant/components/airvisual_pro/config.yaml
Normal file
18
homeassistant/components/airvisual_pro/config.yaml
Normal file
@@ -0,0 +1,18 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
ip_address:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
password:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
22
homeassistant/components/airzone/config.yaml
Normal file
22
homeassistant/components/airzone/config.yaml
Normal file
@@ -0,0 +1,22 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 2
|
||||
data:
|
||||
fields:
|
||||
host:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
id:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
port:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
22
homeassistant/components/airzone_cloud/config.yaml
Normal file
22
homeassistant/components/airzone_cloud/config.yaml
Normal file
@@ -0,0 +1,22 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
id:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
password:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
username:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
18
homeassistant/components/aladdin_connect/config.yaml
Normal file
18
homeassistant/components/aladdin_connect/config.yaml
Normal file
@@ -0,0 +1,18 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 2
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
auth_implementation:
|
||||
required: true
|
||||
selector:
|
||||
text: {}
|
||||
token:
|
||||
required: true
|
||||
selector:
|
||||
object: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
@@ -44,7 +44,7 @@ def make_entity_state_trigger_required_features(
|
||||
class CustomTrigger(EntityStateTriggerRequiredFeatures):
|
||||
"""Trigger for entity state changes."""
|
||||
|
||||
_domains = {domain}
|
||||
_domain = domain
|
||||
_to_states = {to_state}
|
||||
_required_features = required_features
|
||||
|
||||
|
||||
29
homeassistant/components/alarmdecoder/config.yaml
Normal file
29
homeassistant/components/alarmdecoder/config.yaml
Normal file
@@ -0,0 +1,29 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
device_baudrate:
|
||||
required: true
|
||||
selector:
|
||||
number:
|
||||
mode: box
|
||||
default: 115200
|
||||
device_path:
|
||||
required: true
|
||||
selector:
|
||||
text: {}
|
||||
default: /dev/ttyUSB0
|
||||
options:
|
||||
fields:
|
||||
arm_options:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
zone_options:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
subentries: {}
|
||||
14
homeassistant/components/alexa_devices/config.yaml
Normal file
14
homeassistant/components/alexa_devices/config.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 3
|
||||
data:
|
||||
fields:
|
||||
login_data:
|
||||
required: true
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
@@ -8,5 +8,5 @@
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["aioamazondevices"],
|
||||
"quality_scale": "platinum",
|
||||
"requirements": ["aioamazondevices==12.0.2"]
|
||||
"requirements": ["aioamazondevices==12.0.0"]
|
||||
}
|
||||
|
||||
14
homeassistant/components/altruist/config.yaml
Normal file
14
homeassistant/components/altruist/config.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
host:
|
||||
required: true
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
16
homeassistant/components/amberelectric/config.yaml
Normal file
16
homeassistant/components/amberelectric/config.yaml
Normal file
@@ -0,0 +1,16 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
site_id:
|
||||
required: true
|
||||
selector:
|
||||
select:
|
||||
mode: dropdown
|
||||
options: []
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
17
homeassistant/components/ambient_network/config.yaml
Normal file
17
homeassistant/components/ambient_network/config.yaml
Normal file
@@ -0,0 +1,17 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
station:
|
||||
required: true
|
||||
selector:
|
||||
select:
|
||||
multiple: false
|
||||
sort: true
|
||||
options: []
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
18
homeassistant/components/ambient_station/config.yaml
Normal file
18
homeassistant/components/ambient_station/config.yaml
Normal file
@@ -0,0 +1,18 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 2
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
api_key:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
app_key:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
34
homeassistant/components/analytics_insights/config.yaml
Normal file
34
homeassistant/components/analytics_insights/config.yaml
Normal file
@@ -0,0 +1,34 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 2
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
tracked_apps:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
tracked_custom_integrations:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
tracked_integrations:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields:
|
||||
tracked_apps:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
tracked_custom_integrations:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
tracked_integrations:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
subentries: {}
|
||||
26
homeassistant/components/android_ip_webcam/config.yaml
Normal file
26
homeassistant/components/android_ip_webcam/config.yaml
Normal file
@@ -0,0 +1,26 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
host:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
password:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
port:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
username:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
77
homeassistant/components/androidtv/config.yaml
Normal file
77
homeassistant/components/androidtv/config.yaml
Normal file
@@ -0,0 +1,77 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 2
|
||||
data:
|
||||
fields:
|
||||
adb_server_ip:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
adb_server_port:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
adbkey:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
device_class:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
host:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
port:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields:
|
||||
app_delete:
|
||||
required: false
|
||||
selector:
|
||||
boolean: {}
|
||||
default: false
|
||||
apps:
|
||||
required: false
|
||||
selector:
|
||||
select:
|
||||
mode: dropdown
|
||||
options: []
|
||||
exclude_unnamed_apps:
|
||||
required: false
|
||||
selector:
|
||||
boolean: {}
|
||||
get_sources:
|
||||
required: false
|
||||
selector:
|
||||
boolean: {}
|
||||
rule_delete:
|
||||
required: false
|
||||
selector:
|
||||
boolean: {}
|
||||
default: false
|
||||
screencap_interval:
|
||||
required: true
|
||||
selector:
|
||||
number:
|
||||
mode: box
|
||||
state_detection_rules:
|
||||
required: false
|
||||
selector:
|
||||
select:
|
||||
mode: dropdown
|
||||
options: []
|
||||
turn_off_command:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
turn_on_command:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
subentries: {}
|
||||
38
homeassistant/components/androidtv_remote/config.yaml
Normal file
38
homeassistant/components/androidtv_remote/config.yaml
Normal file
@@ -0,0 +1,38 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
pin:
|
||||
required: true
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields:
|
||||
app_delete:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
app_icon:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
app_id:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
app_name:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
apps:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
enable_ime:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
subentries: {}
|
||||
16
homeassistant/components/anglian_water/config.yaml
Normal file
16
homeassistant/components/anglian_water/config.yaml
Normal file
@@ -0,0 +1,16 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
account_number:
|
||||
required: true
|
||||
selector:
|
||||
select:
|
||||
multiple: false
|
||||
options: []
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
18
homeassistant/components/anova/config.yaml
Normal file
18
homeassistant/components/anova/config.yaml
Normal file
@@ -0,0 +1,18 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 2
|
||||
data:
|
||||
fields:
|
||||
password:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
username:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
18
homeassistant/components/anthemav/config.yaml
Normal file
18
homeassistant/components/anthemav/config.yaml
Normal file
@@ -0,0 +1,18 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
host:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
port:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
68
homeassistant/components/anthropic/config.yaml
Normal file
68
homeassistant/components/anthropic/config.yaml
Normal file
@@ -0,0 +1,68 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 2
|
||||
minor: 3
|
||||
data:
|
||||
fields:
|
||||
api_key:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries:
|
||||
ai_task_data:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
chat_model:
|
||||
required: false
|
||||
selector:
|
||||
select:
|
||||
custom_value: true
|
||||
options: []
|
||||
max_tokens:
|
||||
required: false
|
||||
selector:
|
||||
number:
|
||||
mode: box
|
||||
temperature:
|
||||
required: false
|
||||
selector:
|
||||
number:
|
||||
min: 0
|
||||
max: 1
|
||||
step: 0.05
|
||||
options:
|
||||
fields: {}
|
||||
conversation:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
chat_model:
|
||||
required: false
|
||||
selector:
|
||||
select:
|
||||
custom_value: true
|
||||
options: []
|
||||
max_tokens:
|
||||
required: false
|
||||
selector:
|
||||
number:
|
||||
mode: box
|
||||
temperature:
|
||||
required: false
|
||||
selector:
|
||||
number:
|
||||
min: 0
|
||||
max: 1
|
||||
step: 0.05
|
||||
options:
|
||||
fields: {}
|
||||
18
homeassistant/components/aosmith/config.yaml
Normal file
18
homeassistant/components/aosmith/config.yaml
Normal file
@@ -0,0 +1,18 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
email:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
password:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
18
homeassistant/components/apcupsd/config.yaml
Normal file
18
homeassistant/components/apcupsd/config.yaml
Normal file
@@ -0,0 +1,18 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
host:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
port:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
18
homeassistant/components/apple_tv/config.yaml
Normal file
18
homeassistant/components/apple_tv/config.yaml
Normal file
@@ -0,0 +1,18 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
device_input:
|
||||
required: true
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields:
|
||||
start_off:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
subentries: {}
|
||||
18
homeassistant/components/aprilaire/config.yaml
Normal file
18
homeassistant/components/aprilaire/config.yaml
Normal file
@@ -0,0 +1,18 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
host:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
port:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
18
homeassistant/components/apsystems/config.yaml
Normal file
18
homeassistant/components/apsystems/config.yaml
Normal file
@@ -0,0 +1,18 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
ip_address:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
port:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
23
homeassistant/components/aquacell/config.yaml
Normal file
23
homeassistant/components/aquacell/config.yaml
Normal file
@@ -0,0 +1,23 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
brand:
|
||||
required: true
|
||||
selector:
|
||||
select:
|
||||
options: []
|
||||
refresh_token:
|
||||
required: true
|
||||
selector:
|
||||
text: {}
|
||||
refresh_token_creation_time:
|
||||
required: true
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
14
homeassistant/components/aranet/config.yaml
Normal file
14
homeassistant/components/aranet/config.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
address:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
18
homeassistant/components/arcam_fmj/config.yaml
Normal file
18
homeassistant/components/arcam_fmj/config.yaml
Normal file
@@ -0,0 +1,18 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
host:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
port:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
18
homeassistant/components/arve/config.yaml
Normal file
18
homeassistant/components/arve/config.yaml
Normal file
@@ -0,0 +1,18 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 2
|
||||
data:
|
||||
fields:
|
||||
access_token:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
client_secret:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
18
homeassistant/components/aseko_pool_live/config.yaml
Normal file
18
homeassistant/components/aseko_pool_live/config.yaml
Normal file
@@ -0,0 +1,18 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 2
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
email:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
password:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
58
homeassistant/components/asuswrt/config.yaml
Normal file
58
homeassistant/components/asuswrt/config.yaml
Normal file
@@ -0,0 +1,58 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
host:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
mode:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
password:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
port:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
protocol:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
ssh_key:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
username:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields:
|
||||
consider_home:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
dnsmasq:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
interface:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
require_ip:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
track_unknown:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
subentries: {}
|
||||
18
homeassistant/components/atag/config.yaml
Normal file
18
homeassistant/components/atag/config.yaml
Normal file
@@ -0,0 +1,18 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
host:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
port:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
14
homeassistant/components/august/config.yaml
Normal file
14
homeassistant/components/august/config.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
implementation:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
@@ -30,5 +30,5 @@
|
||||
"integration_type": "hub",
|
||||
"iot_class": "cloud_push",
|
||||
"loggers": ["pubnub", "yalexs"],
|
||||
"requirements": ["yalexs==9.2.0", "yalexs-ble==3.2.7"]
|
||||
"requirements": ["yalexs==9.2.0", "yalexs-ble==3.2.4"]
|
||||
}
|
||||
|
||||
26
homeassistant/components/aurora/config.yaml
Normal file
26
homeassistant/components/aurora/config.yaml
Normal file
@@ -0,0 +1,26 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
latitude:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
longitude:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
name:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields:
|
||||
forecast_threshold:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
subentries: {}
|
||||
18
homeassistant/components/aurora_abb_powerone/config.yaml
Normal file
18
homeassistant/components/aurora_abb_powerone/config.yaml
Normal file
@@ -0,0 +1,18 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
address:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
port:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
@@ -61,13 +61,7 @@ class AuroraAbbDataUpdateCoordinator(DataUpdateCoordinator[dict[str, float]]):
|
||||
frequency = self.client.measure(4)
|
||||
i_leak_dcdc = self.client.measure(6)
|
||||
i_leak_inverter = self.client.measure(7)
|
||||
power_in_1 = self.client.measure(8)
|
||||
power_in_2 = self.client.measure(9)
|
||||
temperature_c = self.client.measure(21)
|
||||
voltage_in_1 = self.client.measure(23)
|
||||
current_in_1 = self.client.measure(25)
|
||||
voltage_in_2 = self.client.measure(26)
|
||||
current_in_2 = self.client.measure(27)
|
||||
r_iso = self.client.measure(30)
|
||||
energy_wh = self.client.cumulated_energy(5)
|
||||
[alarm, *_] = self.client.alarms()
|
||||
@@ -93,13 +87,7 @@ class AuroraAbbDataUpdateCoordinator(DataUpdateCoordinator[dict[str, float]]):
|
||||
data["grid_frequency"] = round(frequency, 1)
|
||||
data["i_leak_dcdc"] = i_leak_dcdc
|
||||
data["i_leak_inverter"] = i_leak_inverter
|
||||
data["power_in_1"] = round(power_in_1, 1)
|
||||
data["power_in_2"] = round(power_in_2, 1)
|
||||
data["temp"] = round(temperature_c, 1)
|
||||
data["voltage_in_1"] = round(voltage_in_1, 1)
|
||||
data["current_in_1"] = round(current_in_1, 1)
|
||||
data["voltage_in_2"] = round(voltage_in_2, 1)
|
||||
data["current_in_2"] = round(current_in_2, 1)
|
||||
data["r_iso"] = r_iso
|
||||
data["totalenergy"] = round(energy_wh / 1000, 2)
|
||||
data["alarm"] = alarm
|
||||
|
||||
@@ -68,7 +68,6 @@ SENSOR_TYPES = [
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
native_unit_of_measurement=UnitOfFrequency.HERTZ,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
translation_key="grid_frequency",
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
@@ -89,60 +88,6 @@ SENSOR_TYPES = [
|
||||
translation_key="i_leak_inverter",
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="power_in_1",
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
native_unit_of_measurement=UnitOfPower.WATT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
translation_key="power_in_1",
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="power_in_2",
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
native_unit_of_measurement=UnitOfPower.WATT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
translation_key="power_in_2",
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="voltage_in_1",
|
||||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
translation_key="voltage_in_1",
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="current_in_1",
|
||||
device_class=SensorDeviceClass.CURRENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
translation_key="current_in_1",
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="voltage_in_2",
|
||||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
translation_key="voltage_in_2",
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="current_in_2",
|
||||
device_class=SensorDeviceClass.CURRENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
translation_key="current_in_2",
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="alarm",
|
||||
device_class=SensorDeviceClass.ENUM,
|
||||
|
||||
@@ -24,18 +24,9 @@
|
||||
"alarm": {
|
||||
"name": "Alarm status"
|
||||
},
|
||||
"current_in_1": {
|
||||
"name": "String 1 current"
|
||||
},
|
||||
"current_in_2": {
|
||||
"name": "String 2 current"
|
||||
},
|
||||
"grid_current": {
|
||||
"name": "Grid current"
|
||||
},
|
||||
"grid_frequency": {
|
||||
"name": "Grid frequency"
|
||||
},
|
||||
"grid_voltage": {
|
||||
"name": "Grid voltage"
|
||||
},
|
||||
@@ -45,12 +36,6 @@
|
||||
"i_leak_inverter": {
|
||||
"name": "Inverter leak current"
|
||||
},
|
||||
"power_in_1": {
|
||||
"name": "String 1 power"
|
||||
},
|
||||
"power_in_2": {
|
||||
"name": "String 2 power"
|
||||
},
|
||||
"power_output": {
|
||||
"name": "Power output"
|
||||
},
|
||||
@@ -59,12 +44,6 @@
|
||||
},
|
||||
"total_energy": {
|
||||
"name": "Total energy"
|
||||
},
|
||||
"voltage_in_1": {
|
||||
"name": "String 1 voltage"
|
||||
},
|
||||
"voltage_in_2": {
|
||||
"name": "String 2 voltage"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
26
homeassistant/components/aussie_broadband/config.yaml
Normal file
26
homeassistant/components/aussie_broadband/config.yaml
Normal file
@@ -0,0 +1,26 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
password:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
services:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
username:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields:
|
||||
services:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
subentries: {}
|
||||
18
homeassistant/components/autarco/config.yaml
Normal file
18
homeassistant/components/autarco/config.yaml
Normal file
@@ -0,0 +1,18 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
email:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
password:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
26
homeassistant/components/awair/config.yaml
Normal file
26
homeassistant/components/awair/config.yaml
Normal file
@@ -0,0 +1,26 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
access_token:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
device:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
email:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
host:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
31
homeassistant/components/aws_s3/config.yaml
Normal file
31
homeassistant/components/aws_s3/config.yaml
Normal file
@@ -0,0 +1,31 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
access_key_id:
|
||||
required: true
|
||||
selector:
|
||||
text: {}
|
||||
bucket:
|
||||
required: true
|
||||
selector:
|
||||
text: {}
|
||||
endpoint_url:
|
||||
required: true
|
||||
selector:
|
||||
text: {}
|
||||
prefix:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
default: ""
|
||||
secret_access_key:
|
||||
required: true
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
34
homeassistant/components/axis/config.yaml
Normal file
34
homeassistant/components/axis/config.yaml
Normal file
@@ -0,0 +1,34 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 3
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
host:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
password:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
port:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
protocol:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
username:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields:
|
||||
stream_profile:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
subentries: {}
|
||||
39
homeassistant/components/azure_data_explorer/config.yaml
Normal file
39
homeassistant/components/azure_data_explorer/config.yaml
Normal file
@@ -0,0 +1,39 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
authority_id:
|
||||
required: true
|
||||
selector:
|
||||
text: {}
|
||||
client_id:
|
||||
required: true
|
||||
selector:
|
||||
text: {}
|
||||
client_secret:
|
||||
required: true
|
||||
selector:
|
||||
text: {}
|
||||
cluster_ingest_uri:
|
||||
required: true
|
||||
selector:
|
||||
text: {}
|
||||
database:
|
||||
required: true
|
||||
selector:
|
||||
text: {}
|
||||
table:
|
||||
required: true
|
||||
selector:
|
||||
text: {}
|
||||
use_queued_ingestion:
|
||||
required: true
|
||||
selector:
|
||||
boolean: {}
|
||||
default: false
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
22
homeassistant/components/azure_devops/config.yaml
Normal file
22
homeassistant/components/azure_devops/config.yaml
Normal file
@@ -0,0 +1,22 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
organization:
|
||||
required: true
|
||||
selector:
|
||||
text: {}
|
||||
personal_access_token:
|
||||
required: true
|
||||
selector:
|
||||
text: {}
|
||||
project:
|
||||
required: true
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
39
homeassistant/components/azure_event_hub/config.yaml
Normal file
39
homeassistant/components/azure_event_hub/config.yaml
Normal file
@@ -0,0 +1,39 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
event_hub_connection_string:
|
||||
required: true
|
||||
selector:
|
||||
text: {}
|
||||
event_hub_instance_name:
|
||||
required: true
|
||||
selector:
|
||||
text: {}
|
||||
event_hub_namespace:
|
||||
required: true
|
||||
selector:
|
||||
text: {}
|
||||
event_hub_sas_key:
|
||||
required: true
|
||||
selector:
|
||||
text: {}
|
||||
event_hub_sas_policy:
|
||||
required: true
|
||||
selector:
|
||||
text: {}
|
||||
use_connection_string:
|
||||
required: false
|
||||
selector:
|
||||
boolean: {}
|
||||
default: false
|
||||
options:
|
||||
fields:
|
||||
send_interval:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
subentries: {}
|
||||
22
homeassistant/components/azure_storage/config.yaml
Normal file
22
homeassistant/components/azure_storage/config.yaml
Normal file
@@ -0,0 +1,22 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
account_name:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
container_name:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
storage_account_key:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
26
homeassistant/components/backblaze_b2/config.yaml
Normal file
26
homeassistant/components/backblaze_b2/config.yaml
Normal file
@@ -0,0 +1,26 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
application_key:
|
||||
required: true
|
||||
selector:
|
||||
text: {}
|
||||
key_id:
|
||||
required: true
|
||||
selector:
|
||||
text: {}
|
||||
bucket:
|
||||
required: true
|
||||
selector:
|
||||
text: {}
|
||||
prefix:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
@@ -29,17 +29,12 @@ class StoredBackupData(TypedDict):
|
||||
class _BackupStore(Store[StoredBackupData]):
|
||||
"""Class to help storing backup data."""
|
||||
|
||||
# Maximum version we support reading for forward compatibility.
|
||||
# This allows reading data written by a newer HA version after downgrade.
|
||||
_MAX_READABLE_VERSION = 2
|
||||
|
||||
def __init__(self, hass: HomeAssistant) -> None:
|
||||
"""Initialize storage class."""
|
||||
super().__init__(
|
||||
hass,
|
||||
STORAGE_VERSION,
|
||||
STORAGE_KEY,
|
||||
max_readable_version=self._MAX_READABLE_VERSION,
|
||||
minor_version=STORAGE_VERSION_MINOR,
|
||||
)
|
||||
|
||||
@@ -91,8 +86,8 @@ class _BackupStore(Store[StoredBackupData]):
|
||||
# data["config"]["schedule"]["state"] will be removed. The bump to 2 is
|
||||
# planned to happen after a 6 month quiet period with no minor version
|
||||
# changes.
|
||||
# Reject if major version is higher than _MAX_READABLE_VERSION.
|
||||
if old_major_version > self._MAX_READABLE_VERSION:
|
||||
# Reject if major version is higher than 2.
|
||||
if old_major_version > 2:
|
||||
raise NotImplementedError
|
||||
return data
|
||||
|
||||
|
||||
14
homeassistant/components/baf/config.yaml
Normal file
14
homeassistant/components/baf/config.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
ip_address:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
18
homeassistant/components/balboa/config.yaml
Normal file
18
homeassistant/components/balboa/config.yaml
Normal file
@@ -0,0 +1,18 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
host:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields:
|
||||
sync_time:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
subentries: {}
|
||||
18
homeassistant/components/bang_olufsen/config.yaml
Normal file
18
homeassistant/components/bang_olufsen/config.yaml
Normal file
@@ -0,0 +1,18 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
host:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
model:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
111
homeassistant/components/bayesian/config.yaml
Normal file
111
homeassistant/components/bayesian/config.yaml
Normal file
@@ -0,0 +1,111 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
above:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
below:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
device_class:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
entity_id:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
name:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
prior:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
prob_given_false:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
prob_given_true:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
probability_threshold:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
to_state:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
value_template:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields:
|
||||
device_class:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
name:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
prior:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
probability_threshold:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
subentries:
|
||||
observation:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
above:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
below:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
entity_id:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
name:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
prob_given_false:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
prob_given_true:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
to_state:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
value_template:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
@@ -24,7 +24,7 @@ class BinarySensorOnOffTrigger(EntityTargetStateTriggerBase):
|
||||
"""Class for binary sensor on/off triggers."""
|
||||
|
||||
_device_class: BinarySensorDeviceClass | None
|
||||
_domains = {DOMAIN}
|
||||
_domain: str = DOMAIN
|
||||
|
||||
def entity_filter(self, entities: set[str]) -> set[str]:
|
||||
"""Filter entities of this domain."""
|
||||
|
||||
@@ -190,7 +190,7 @@ class BitcoinSensor(SensorEntity):
|
||||
elif sensor_type == "miners_revenue_usd":
|
||||
self._attr_native_value = f"{stats.miners_revenue_usd:.0f}"
|
||||
elif sensor_type == "btc_mined":
|
||||
self._attr_native_value = str(stats.btc_mined * 1e-8)
|
||||
self._attr_native_value = str(stats.btc_mined * 0.00000001)
|
||||
elif sensor_type == "trade_volume_usd":
|
||||
self._attr_native_value = f"{stats.trade_volume_usd:.1f}"
|
||||
elif sensor_type == "difficulty":
|
||||
@@ -208,13 +208,13 @@ class BitcoinSensor(SensorEntity):
|
||||
elif sensor_type == "blocks_size":
|
||||
self._attr_native_value = f"{stats.blocks_size:.1f}"
|
||||
elif sensor_type == "total_fees_btc":
|
||||
self._attr_native_value = f"{stats.total_fees_btc * 1e-8:.2f}"
|
||||
self._attr_native_value = f"{stats.total_fees_btc * 0.00000001:.2f}"
|
||||
elif sensor_type == "total_btc_sent":
|
||||
self._attr_native_value = f"{stats.total_btc_sent * 1e-8:.2f}"
|
||||
self._attr_native_value = f"{stats.total_btc_sent * 0.00000001:.2f}"
|
||||
elif sensor_type == "estimated_btc_sent":
|
||||
self._attr_native_value = f"{stats.estimated_btc_sent * 1e-8:.2f}"
|
||||
self._attr_native_value = f"{stats.estimated_btc_sent * 0.00000001:.2f}"
|
||||
elif sensor_type == "total_btc":
|
||||
self._attr_native_value = f"{stats.total_btc * 1e-8:.2f}"
|
||||
self._attr_native_value = f"{stats.total_btc * 0.00000001:.2f}"
|
||||
elif sensor_type == "total_blocks":
|
||||
self._attr_native_value = f"{stats.total_blocks:.0f}"
|
||||
elif sensor_type == "next_retarget":
|
||||
@@ -222,7 +222,7 @@ class BitcoinSensor(SensorEntity):
|
||||
elif sensor_type == "estimated_transaction_volume_usd":
|
||||
self._attr_native_value = f"{stats.estimated_transaction_volume_usd:.2f}"
|
||||
elif sensor_type == "miners_revenue_btc":
|
||||
self._attr_native_value = f"{stats.miners_revenue_btc * 1e-8:.1f}"
|
||||
self._attr_native_value = f"{stats.miners_revenue_btc * 0.00000001:.1f}"
|
||||
elif sensor_type == "market_price_usd":
|
||||
self._attr_native_value = f"{stats.market_price_usd:.2f}"
|
||||
|
||||
|
||||
18
homeassistant/components/blebox/config.yaml
Normal file
18
homeassistant/components/blebox/config.yaml
Normal file
@@ -0,0 +1,18 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
host:
|
||||
required: true
|
||||
selector:
|
||||
text: {}
|
||||
port:
|
||||
required: true
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
26
homeassistant/components/blink/config.yaml
Normal file
26
homeassistant/components/blink/config.yaml
Normal file
@@ -0,0 +1,26 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 4
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
password:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
pin:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
username:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields:
|
||||
scan_interval:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
subentries: {}
|
||||
18
homeassistant/components/blue_current/config.yaml
Normal file
18
homeassistant/components/blue_current/config.yaml
Normal file
@@ -0,0 +1,18 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
api_token:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields:
|
||||
card:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
subentries: {}
|
||||
14
homeassistant/components/bluemaestro/config.yaml
Normal file
14
homeassistant/components/bluemaestro/config.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
address:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
18
homeassistant/components/bluesound/config.yaml
Normal file
18
homeassistant/components/bluesound/config.yaml
Normal file
@@ -0,0 +1,18 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
host:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
port:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
19
homeassistant/components/bluetooth/config.yaml
Normal file
19
homeassistant/components/bluetooth/config.yaml
Normal file
@@ -0,0 +1,19 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
adapter:
|
||||
required: true
|
||||
selector:
|
||||
select:
|
||||
options: []
|
||||
options:
|
||||
fields:
|
||||
passive:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
subentries: {}
|
||||
22
homeassistant/components/bmw_connected_drive/config.yaml
Normal file
22
homeassistant/components/bmw_connected_drive/config.yaml
Normal file
@@ -0,0 +1,22 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
gcid:
|
||||
required: true
|
||||
selector:
|
||||
text: {}
|
||||
refresh_token:
|
||||
required: true
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields:
|
||||
read_only:
|
||||
required: false
|
||||
selector:
|
||||
boolean: {}
|
||||
subentries: {}
|
||||
18
homeassistant/components/bond/config.yaml
Normal file
18
homeassistant/components/bond/config.yaml
Normal file
@@ -0,0 +1,18 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
access_token:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
host:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
30
homeassistant/components/bosch_alarm/config.yaml
Normal file
30
homeassistant/components/bosch_alarm/config.yaml
Normal file
@@ -0,0 +1,30 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
host:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
installer_code:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
password:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
port:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
user_code:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
22
homeassistant/components/bosch_shc/config.yaml
Normal file
22
homeassistant/components/bosch_shc/config.yaml
Normal file
@@ -0,0 +1,22 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
hostname:
|
||||
required: true
|
||||
selector:
|
||||
text: {}
|
||||
ssl_certificate:
|
||||
required: true
|
||||
selector:
|
||||
text: {}
|
||||
ssl_key:
|
||||
required: true
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
26
homeassistant/components/braviatv/config.yaml
Normal file
26
homeassistant/components/braviatv/config.yaml
Normal file
@@ -0,0 +1,26 @@
|
||||
config_entry:
|
||||
versions:
|
||||
- version:
|
||||
major: 1
|
||||
minor: 1
|
||||
data:
|
||||
fields:
|
||||
host:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
pin:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
use_psk:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
use_ssl:
|
||||
required: false
|
||||
selector:
|
||||
text: {}
|
||||
options:
|
||||
fields: {}
|
||||
subentries: {}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user