mirror of
https://github.com/home-assistant/core.git
synced 2026-04-02 21:44:55 +00:00
Compare commits
43 Commits
copilot/su
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
375bd55ae6 | ||
|
|
fbd0cb8666 | ||
|
|
7e061262ad | ||
|
|
0e9c17fb16 | ||
|
|
940de5ea84 | ||
|
|
bf4773d9bc | ||
|
|
0bedcc55ce | ||
|
|
313f97fc47 | ||
|
|
b7d32e0650 | ||
|
|
b9afb2a861 | ||
|
|
d3a01d4c80 | ||
|
|
05bd2d05f5 | ||
|
|
23469d8950 | ||
|
|
26f677dcd1 | ||
|
|
484d9b0cbe | ||
|
|
03ed46aa07 | ||
|
|
e5f4000ac2 | ||
|
|
406598dbfa | ||
|
|
7f23a35155 | ||
|
|
0e521eda2e | ||
|
|
5a72dc8eca | ||
|
|
b11292385f | ||
|
|
2179a5405a | ||
|
|
78f5989cd6 | ||
|
|
cca44c675c | ||
|
|
0ebe65c25b | ||
|
|
f7ee95c4b9 | ||
|
|
d78c05ab62 | ||
|
|
9f41e3341f | ||
|
|
8ab3d482b9 | ||
|
|
a485c3d410 | ||
|
|
38b27d624a | ||
|
|
f437d65d3c | ||
|
|
5ba0764a87 | ||
|
|
69fd6532cc | ||
|
|
ee8bd9f016 | ||
|
|
de5a2d47a5 | ||
|
|
54b2e0285c | ||
|
|
a0e118d411 | ||
|
|
07c33233ee | ||
|
|
962cac902b | ||
|
|
9ff5c9863f | ||
|
|
b60e396241 |
5
.github/copilot-instructions.md
vendored
5
.github/copilot-instructions.md
vendored
@@ -11,10 +11,9 @@
|
||||
|
||||
This repository contains the core of Home Assistant, a Python 3 based home automation application.
|
||||
|
||||
## Code Review Guidelines
|
||||
## Git Commit Guidelines
|
||||
|
||||
**Git commit practices during review:**
|
||||
- **Do NOT amend, squash, or rebase commits after review has started** - Reviewers need to see what changed since their last review
|
||||
- **Do NOT amend, squash, or rebase commits that have already been pushed to the PR branch after the PR is opened** - Reviewers need to follow the commit history, as well as see what changed since their last review
|
||||
|
||||
## Development Commands
|
||||
|
||||
|
||||
2
.github/workflows/builder.yml
vendored
2
.github/workflows/builder.yml
vendored
@@ -342,7 +342,7 @@ jobs:
|
||||
registry: ["ghcr.io/home-assistant", "docker.io/homeassistant"]
|
||||
steps:
|
||||
- name: Install Cosign
|
||||
uses: sigstore/cosign-installer@ba7bc0a3fef59531c69a25acd34668d6d3fe6f22 # v4.1.0
|
||||
uses: sigstore/cosign-installer@cad07c2e89fa2edd6e2d7bab4c1aa38e53f76003 # v4.1.1
|
||||
with:
|
||||
cosign-release: "v2.5.3"
|
||||
|
||||
|
||||
@@ -87,6 +87,13 @@ repos:
|
||||
language: script
|
||||
types: [text]
|
||||
files: ^(homeassistant/.+/manifest\.json|homeassistant/brands/.+\.json|pyproject\.toml|\.pre-commit-config\.yaml|script/gen_requirements_all\.py)$
|
||||
- id: gen_copilot_instructions
|
||||
name: gen_copilot_instructions
|
||||
entry: script/run-in-env.sh python3 -m script.gen_copilot_instructions
|
||||
pass_filenames: false
|
||||
language: script
|
||||
types: [text]
|
||||
files: ^(AGENTS\.md|\.claude/skills/(?!github-pr-reviewer/).+/SKILL\.md|\.github/copilot-instructions\.md|script/gen_copilot_instructions\.py)$
|
||||
- id: hassfest
|
||||
name: hassfest
|
||||
entry: script/run-in-env.sh python3 -m script.hassfest
|
||||
|
||||
@@ -2,10 +2,9 @@
|
||||
|
||||
This repository contains the core of Home Assistant, a Python 3 based home automation application.
|
||||
|
||||
## Code Review Guidelines
|
||||
## Git Commit Guidelines
|
||||
|
||||
**Git commit practices during review:**
|
||||
- **Do NOT amend, squash, or rebase commits after review has started** - Reviewers need to see what changed since their last review
|
||||
- **Do NOT amend, squash, or rebase commits that have already been pushed to the PR branch after the PR is opened** - Reviewers need to follow the commit history, as well as see what changed since their last review
|
||||
|
||||
## Development Commands
|
||||
|
||||
|
||||
9
CODEOWNERS
generated
9
CODEOWNERS
generated
@@ -37,6 +37,13 @@ build.json @home-assistant/supervisor
|
||||
# Other code
|
||||
/homeassistant/scripts/check_config.py @kellerza
|
||||
|
||||
# Agent Configurations
|
||||
AGENTS.md @home-assistant/core
|
||||
CLAUDE.md @home-assistant/core
|
||||
/.agent/ @home-assistant/core
|
||||
/.claude/ @home-assistant/core
|
||||
/.gemini/ @home-assistant/core
|
||||
|
||||
# Integrations
|
||||
/homeassistant/components/abode/ @shred86
|
||||
/tests/components/abode/ @shred86
|
||||
@@ -1301,6 +1308,8 @@ build.json @home-assistant/supervisor
|
||||
/tests/components/pi_hole/ @shenxn
|
||||
/homeassistant/components/picnic/ @corneyl @codesalatdev
|
||||
/tests/components/picnic/ @corneyl @codesalatdev
|
||||
/homeassistant/components/picotts/ @rooggiieerr
|
||||
/tests/components/picotts/ @rooggiieerr
|
||||
/homeassistant/components/ping/ @jpbede
|
||||
/tests/components/ping/ @jpbede
|
||||
/homeassistant/components/plaato/ @JohNan
|
||||
|
||||
@@ -36,7 +36,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ActronAirConfigEntry) ->
|
||||
translation_key="auth_error",
|
||||
) from err
|
||||
except ActronAirAPIError as err:
|
||||
raise ConfigEntryNotReady from err
|
||||
raise ConfigEntryNotReady(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="setup_connection_error",
|
||||
) from err
|
||||
|
||||
system_coordinators: dict[str, ActronAirSystemCoordinator] = {}
|
||||
for system in systems:
|
||||
|
||||
@@ -64,7 +64,7 @@ rules:
|
||||
status: exempt
|
||||
comment: Not required for this integration at this stage.
|
||||
entity-translations: todo
|
||||
exception-translations: todo
|
||||
exception-translations: done
|
||||
icon-translations: todo
|
||||
reconfiguration-flow: todo
|
||||
repair-issues:
|
||||
|
||||
@@ -55,6 +55,9 @@
|
||||
"auth_error": {
|
||||
"message": "Authentication failed, please reauthenticate"
|
||||
},
|
||||
"setup_connection_error": {
|
||||
"message": "Failed to connect to the Actron Air API"
|
||||
},
|
||||
"update_error": {
|
||||
"message": "An error occurred while retrieving data from the Actron Air API: {error}"
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"integration_type": "device",
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["arcam"],
|
||||
"requirements": ["arcam-fmj==1.8.2"],
|
||||
"requirements": ["arcam-fmj==1.8.3"],
|
||||
"ssdp": [
|
||||
{
|
||||
"deviceType": "urn:schemas-upnp-org:device:MediaRenderer:1",
|
||||
|
||||
@@ -91,6 +91,7 @@ SENSORS: tuple[ArcamFmjSensorEntityDescription, ...] = (
|
||||
value_fn=lambda state: (
|
||||
vp.colorspace.name.lower()
|
||||
if (vp := state.get_incoming_video_parameters()) is not None
|
||||
and vp.colorspace is not None
|
||||
else None
|
||||
),
|
||||
),
|
||||
|
||||
@@ -239,7 +239,7 @@
|
||||
"message": "Provided humidity {humidity} is not valid. Accepted range is {min_humidity} to {max_humidity}."
|
||||
},
|
||||
"low_temp_higher_than_high_temp": {
|
||||
"message": "'Lower target temperature' can not be higher than 'Upper target temperature'."
|
||||
"message": "'Lower target temperature' cannot be higher than 'Upper target temperature'."
|
||||
},
|
||||
"missing_target_temperature_entity_feature": {
|
||||
"message": "Set temperature action was used with the 'Target temperature' parameter but the entity does not support it."
|
||||
|
||||
@@ -21,6 +21,7 @@ from .const import ( # noqa: F401
|
||||
ATTR_DEV_ID,
|
||||
ATTR_GPS,
|
||||
ATTR_HOST_NAME,
|
||||
ATTR_IN_ZONES,
|
||||
ATTR_IP,
|
||||
ATTR_LOCATION_NAME,
|
||||
ATTR_MAC,
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from typing import final
|
||||
from typing import Any, final
|
||||
|
||||
from propcache.api import cached_property
|
||||
|
||||
@@ -18,7 +18,7 @@ from homeassistant.const import (
|
||||
STATE_NOT_HOME,
|
||||
EntityCategory,
|
||||
)
|
||||
from homeassistant.core import Event, HomeAssistant, callback
|
||||
from homeassistant.core import Event, HomeAssistant, State, callback
|
||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||
from homeassistant.helpers.device_registry import (
|
||||
DeviceInfo,
|
||||
@@ -33,6 +33,7 @@ from homeassistant.util.hass_dict import HassKey
|
||||
|
||||
from .const import (
|
||||
ATTR_HOST_NAME,
|
||||
ATTR_IN_ZONES,
|
||||
ATTR_IP,
|
||||
ATTR_MAC,
|
||||
ATTR_SOURCE_TYPE,
|
||||
@@ -223,6 +224,9 @@ class TrackerEntity(
|
||||
_attr_longitude: float | None = None
|
||||
_attr_source_type: SourceType = SourceType.GPS
|
||||
|
||||
__active_zone: State | None = None
|
||||
__in_zones: list[str] | None = None
|
||||
|
||||
@cached_property
|
||||
def should_poll(self) -> bool:
|
||||
"""No polling for entities that have location pushed."""
|
||||
@@ -256,6 +260,18 @@ class TrackerEntity(
|
||||
"""Return longitude value of the device."""
|
||||
return self._attr_longitude
|
||||
|
||||
@callback
|
||||
def _async_write_ha_state(self) -> None:
|
||||
"""Calculate active zones."""
|
||||
if self.latitude is not None and self.longitude is not None:
|
||||
self.__active_zone, self.__in_zones = zone.async_in_zones(
|
||||
self.hass, self.latitude, self.longitude, self.location_accuracy
|
||||
)
|
||||
else:
|
||||
self.__active_zone = None
|
||||
self.__in_zones = None
|
||||
super()._async_write_ha_state()
|
||||
|
||||
@property
|
||||
def state(self) -> str | None:
|
||||
"""Return the state of the device."""
|
||||
@@ -263,9 +279,7 @@ class TrackerEntity(
|
||||
return self.location_name
|
||||
|
||||
if self.latitude is not None and self.longitude is not None:
|
||||
zone_state = zone.async_active_zone(
|
||||
self.hass, self.latitude, self.longitude, self.location_accuracy
|
||||
)
|
||||
zone_state = self.__active_zone
|
||||
if zone_state is None:
|
||||
state = STATE_NOT_HOME
|
||||
elif zone_state.entity_id == zone.ENTITY_ID_HOME:
|
||||
@@ -278,12 +292,13 @@ class TrackerEntity(
|
||||
|
||||
@final
|
||||
@property
|
||||
def state_attributes(self) -> dict[str, StateType]:
|
||||
def state_attributes(self) -> dict[str, Any]:
|
||||
"""Return the device state attributes."""
|
||||
attr: dict[str, StateType] = {}
|
||||
attr: dict[str, Any] = {ATTR_IN_ZONES: []}
|
||||
attr.update(super().state_attributes)
|
||||
|
||||
if self.latitude is not None and self.longitude is not None:
|
||||
attr[ATTR_IN_ZONES] = self.__in_zones or []
|
||||
attr[ATTR_LATITUDE] = self.latitude
|
||||
attr[ATTR_LONGITUDE] = self.longitude
|
||||
attr[ATTR_GPS_ACCURACY] = self.location_accuracy
|
||||
|
||||
@@ -43,6 +43,7 @@ ATTR_BATTERY: Final = "battery"
|
||||
ATTR_DEV_ID: Final = "dev_id"
|
||||
ATTR_GPS: Final = "gps"
|
||||
ATTR_HOST_NAME: Final = "host_name"
|
||||
ATTR_IN_ZONES: Final = "in_zones"
|
||||
ATTR_LOCATION_NAME: Final = "location_name"
|
||||
ATTR_MAC: Final = "mac"
|
||||
ATTR_SOURCE_TYPE: Final = "source_type"
|
||||
|
||||
@@ -24,11 +24,10 @@ class EcowittEntity(Entity):
|
||||
|
||||
self._attr_unique_id = f"{sensor.station.key}-{sensor.key}"
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={
|
||||
(DOMAIN, sensor.station.key),
|
||||
},
|
||||
identifiers={(DOMAIN, sensor.station.key)},
|
||||
name=sensor.station.model,
|
||||
model=sensor.station.model,
|
||||
manufacturer="Ecowitt",
|
||||
sw_version=sensor.station.version,
|
||||
)
|
||||
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/holiday",
|
||||
"iot_class": "local_polling",
|
||||
"requirements": ["holidays==0.84", "babel==2.15.0"]
|
||||
"requirements": ["holidays==0.93", "babel==2.15.0"]
|
||||
}
|
||||
|
||||
@@ -69,6 +69,10 @@ class HydrawiseEntity(CoordinatorEntity[HydrawiseDataUpdateCoordinator]):
|
||||
@callback
|
||||
def _handle_coordinator_update(self) -> None:
|
||||
"""Get the latest data and updates the state."""
|
||||
# Guard against updates arriving after the controller has been removed
|
||||
# but before the entity has been unsubscribed from the coordinator.
|
||||
if self.controller.id not in self.coordinator.data.controllers:
|
||||
return
|
||||
self.controller = self.coordinator.data.controllers[self.controller.id]
|
||||
self._update_attrs()
|
||||
super()._handle_coordinator_update()
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/kaleidescape",
|
||||
"integration_type": "device",
|
||||
"iot_class": "local_push",
|
||||
"requirements": ["pykaleidescape==1.1.4"],
|
||||
"requirements": ["pykaleidescape==1.1.5"],
|
||||
"ssdp": [
|
||||
{
|
||||
"deviceType": "schemas-upnp-org:device:Basic:1",
|
||||
|
||||
@@ -2,111 +2,29 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import socket
|
||||
from typing import Any
|
||||
from urllib.parse import urlencode
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.notify import (
|
||||
ATTR_DATA,
|
||||
PLATFORM_SCHEMA as NOTIFY_PLATFORM_SCHEMA,
|
||||
BaseNotificationService,
|
||||
)
|
||||
from homeassistant.const import CONF_HOST, CONF_PORT
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers import config_validation as cv, issue_registry as ir
|
||||
from homeassistant.components.notify import PLATFORM_SCHEMA as NOTIFY_PLATFORM_SCHEMA
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import issue_registry as ir
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
|
||||
DOMAIN = "lannouncer"
|
||||
|
||||
ATTR_METHOD = "method"
|
||||
ATTR_METHOD_DEFAULT = "speak"
|
||||
ATTR_METHOD_ALLOWED = ["speak", "alarm"]
|
||||
|
||||
DEFAULT_PORT = 1035
|
||||
|
||||
PLATFORM_SCHEMA = NOTIFY_PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_HOST): cv.string,
|
||||
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
|
||||
}
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
PLATFORM_SCHEMA = NOTIFY_PLATFORM_SCHEMA.extend({}, extra=vol.ALLOW_EXTRA)
|
||||
|
||||
|
||||
def get_service(
|
||||
async def async_get_service(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigType,
|
||||
discovery_info: DiscoveryInfoType | None = None,
|
||||
) -> LannouncerNotificationService:
|
||||
) -> None:
|
||||
"""Get the Lannouncer notification service."""
|
||||
|
||||
@callback
|
||||
def _async_create_issue() -> None:
|
||||
"""Create issue for removed integration."""
|
||||
ir.async_create_issue(
|
||||
hass,
|
||||
DOMAIN,
|
||||
"integration_removed",
|
||||
is_fixable=False,
|
||||
breaks_in_ha_version="2026.3.0",
|
||||
severity=ir.IssueSeverity.WARNING,
|
||||
translation_key="integration_removed",
|
||||
)
|
||||
|
||||
hass.add_job(_async_create_issue)
|
||||
|
||||
host = config.get(CONF_HOST)
|
||||
port = config.get(CONF_PORT)
|
||||
|
||||
return LannouncerNotificationService(hass, host, port)
|
||||
|
||||
|
||||
class LannouncerNotificationService(BaseNotificationService):
|
||||
"""Implementation of a notification service for Lannouncer."""
|
||||
|
||||
def __init__(self, hass, host, port):
|
||||
"""Initialize the service."""
|
||||
self._hass = hass
|
||||
self._host = host
|
||||
self._port = port
|
||||
|
||||
def send_message(self, message: str = "", **kwargs: Any) -> None:
|
||||
"""Send a message to Lannouncer."""
|
||||
data = kwargs.get(ATTR_DATA)
|
||||
if data is not None and ATTR_METHOD in data:
|
||||
method = data.get(ATTR_METHOD)
|
||||
else:
|
||||
method = ATTR_METHOD_DEFAULT
|
||||
|
||||
if method not in ATTR_METHOD_ALLOWED:
|
||||
_LOGGER.error("Unknown method %s", method)
|
||||
return
|
||||
|
||||
cmd = urlencode({method: message})
|
||||
|
||||
try:
|
||||
# Open socket
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.settimeout(10)
|
||||
sock.connect((self._host, self._port))
|
||||
|
||||
# Send message
|
||||
_LOGGER.debug("Sending message: %s", cmd)
|
||||
sock.sendall(cmd.encode())
|
||||
sock.sendall(b"&@DONE@\n")
|
||||
|
||||
# Check response
|
||||
buffer = sock.recv(1024)
|
||||
if buffer != b"LANnouncer: OK":
|
||||
_LOGGER.error("Error sending data to Lannnouncer: %s", buffer.decode())
|
||||
|
||||
# Close socket
|
||||
sock.close()
|
||||
except socket.gaierror:
|
||||
_LOGGER.error("Unable to connect to host %s", self._host)
|
||||
except OSError:
|
||||
_LOGGER.exception("Failed to send data to Lannnouncer")
|
||||
ir.async_create_issue(
|
||||
hass,
|
||||
DOMAIN,
|
||||
DOMAIN,
|
||||
is_fixable=False,
|
||||
severity=ir.IssueSeverity.ERROR,
|
||||
translation_key="integration_removed",
|
||||
)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"issues": {
|
||||
"integration_removed": {
|
||||
"description": "The LANnouncer Android app is no longer available, so this integration has been deprecated and will be removed in a future release.\n\nTo resolve this issue:\n1. Remove the LANnouncer integration from your `configuration.yaml`.\n2. Restart the Home Assistant instance.\n\nAfter removal, this issue will disappear.",
|
||||
"title": "LANnouncer integration is deprecated"
|
||||
"description": "The LANnouncer integration has been removed from Home Assistant because the LANnouncer Android app is no longer available.\n\nTo resolve this issue:\n1. Remove the LANnouncer integration from your `configuration.yaml`.\n2. Restart the Home Assistant instance.\n\nAfter removal, this issue will disappear.",
|
||||
"title": "LANnouncer integration has been removed"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,32 +2,26 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import MutesyncUpdateCoordinator
|
||||
from .coordinator import MutesyncConfigEntry, MutesyncUpdateCoordinator
|
||||
|
||||
PLATFORMS = [Platform.BINARY_SENSOR]
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: MutesyncConfigEntry) -> bool:
|
||||
"""Set up mütesync from a config entry."""
|
||||
coordinator = hass.data.setdefault(DOMAIN, {})[entry.entry_id] = (
|
||||
MutesyncUpdateCoordinator(hass, entry)
|
||||
)
|
||||
coordinator = MutesyncUpdateCoordinator(hass, entry)
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
|
||||
entry.runtime_data = coordinator
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: MutesyncConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
if unload_ok:
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
"""mütesync binary sensor entities."""
|
||||
|
||||
from homeassistant.components.binary_sensor import BinarySensorEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import MutesyncUpdateCoordinator
|
||||
from .coordinator import MutesyncConfigEntry, MutesyncUpdateCoordinator
|
||||
|
||||
SENSORS = (
|
||||
"in_meeting",
|
||||
@@ -18,11 +17,11 @@ SENSORS = (
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: MutesyncConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the mütesync button."""
|
||||
coordinator = hass.data[DOMAIN][config_entry.entry_id]
|
||||
"""Set up the mütesync binary sensors."""
|
||||
coordinator = config_entry.runtime_data
|
||||
async_add_entities(
|
||||
[MuteStatus(coordinator, sensor_type) for sensor_type in SENSORS], True
|
||||
)
|
||||
|
||||
@@ -15,18 +15,20 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, Upda
|
||||
|
||||
from .const import DOMAIN, UPDATE_INTERVAL_IN_MEETING, UPDATE_INTERVAL_NOT_IN_MEETING
|
||||
|
||||
type MutesyncConfigEntry = ConfigEntry[MutesyncUpdateCoordinator]
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class MutesyncUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
||||
"""Coordinator for the mütesync integration."""
|
||||
|
||||
config_entry: ConfigEntry
|
||||
config_entry: MutesyncConfigEntry
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: MutesyncConfigEntry,
|
||||
) -> None:
|
||||
"""Initialize the coordinator."""
|
||||
super().__init__(
|
||||
|
||||
@@ -7,7 +7,6 @@ from nibe.connection.modbus import Modbus
|
||||
from nibe.connection.nibegw import NibeGW, ProductInfo
|
||||
from nibe.heatpump import HeatPump, Model
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
CONF_IP_ADDRESS,
|
||||
CONF_MODEL,
|
||||
@@ -30,7 +29,7 @@ from .const import (
|
||||
CONF_WORD_SWAP,
|
||||
DOMAIN,
|
||||
)
|
||||
from .coordinator import CoilCoordinator
|
||||
from .coordinator import CoilCoordinator, NibeHeatpumpConfigEntry
|
||||
|
||||
PLATFORMS: list[Platform] = [
|
||||
Platform.BINARY_SENSOR,
|
||||
@@ -45,7 +44,9 @@ PLATFORMS: list[Platform] = [
|
||||
COIL_READ_RETRIES = 5
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant, entry: NibeHeatpumpConfigEntry
|
||||
) -> bool:
|
||||
"""Set up Nibe Heat Pump from a config entry."""
|
||||
|
||||
heatpump = HeatPump(Model[entry.data[CONF_MODEL]])
|
||||
@@ -83,8 +84,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
|
||||
coordinator = CoilCoordinator(hass, entry, heatpump, connection)
|
||||
|
||||
data = hass.data.setdefault(DOMAIN, {})
|
||||
data[entry.entry_id] = coordinator
|
||||
entry.runtime_data = coordinator
|
||||
|
||||
reg = dr.async_get(hass)
|
||||
device_entry = reg.async_get_or_create(
|
||||
@@ -113,9 +113,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(
|
||||
hass: HomeAssistant, entry: NibeHeatpumpConfigEntry
|
||||
) -> bool:
|
||||
"""Unload a config entry."""
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
@@ -5,24 +5,22 @@ from __future__ import annotations
|
||||
from nibe.coil import Coil, CoilData
|
||||
|
||||
from homeassistant.components.binary_sensor import ENTITY_ID_FORMAT, BinarySensorEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import EntityCategory
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import CoilCoordinator
|
||||
from .coordinator import CoilCoordinator, NibeHeatpumpConfigEntry
|
||||
from .entity import CoilEntity
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: NibeHeatpumpConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up platform."""
|
||||
|
||||
coordinator: CoilCoordinator = hass.data[DOMAIN][config_entry.entry_id]
|
||||
coordinator = config_entry.runtime_data
|
||||
|
||||
async_add_entities(
|
||||
BinarySensor(coordinator, coil)
|
||||
|
||||
@@ -6,24 +6,23 @@ from nibe.coil_groups import UNIT_COILGROUPS, UnitCoilGroup
|
||||
from nibe.exceptions import CoilNotFoundException
|
||||
|
||||
from homeassistant.components.button import ButtonEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import EntityCategory
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import DOMAIN, LOGGER
|
||||
from .coordinator import CoilCoordinator
|
||||
from .const import LOGGER
|
||||
from .coordinator import CoilCoordinator, NibeHeatpumpConfigEntry
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: NibeHeatpumpConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up platform."""
|
||||
|
||||
coordinator: CoilCoordinator = hass.data[DOMAIN][config_entry.entry_id]
|
||||
coordinator = config_entry.runtime_data
|
||||
|
||||
def reset_buttons():
|
||||
if unit := UNIT_COILGROUPS.get(coordinator.series, {}).get("main"):
|
||||
|
||||
@@ -24,31 +24,29 @@ from homeassistant.components.climate import (
|
||||
HVACAction,
|
||||
HVACMode,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import ServiceValidationError
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import (
|
||||
DOMAIN,
|
||||
LOGGER,
|
||||
VALUES_COOL_WITH_ROOM_SENSOR_OFF,
|
||||
VALUES_MIXING_VALVE_CLOSED_STATE,
|
||||
VALUES_PRIORITY_COOLING,
|
||||
VALUES_PRIORITY_HEATING,
|
||||
)
|
||||
from .coordinator import CoilCoordinator
|
||||
from .coordinator import CoilCoordinator, NibeHeatpumpConfigEntry
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: NibeHeatpumpConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up platform."""
|
||||
|
||||
coordinator: CoilCoordinator = hass.data[DOMAIN][config_entry.entry_id]
|
||||
coordinator = config_entry.runtime_data
|
||||
|
||||
main_unit = UNIT_COILGROUPS[coordinator.series]["main"]
|
||||
|
||||
|
||||
@@ -28,6 +28,8 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, Upda
|
||||
|
||||
from .const import DOMAIN, LOGGER
|
||||
|
||||
type NibeHeatpumpConfigEntry = ConfigEntry[CoilCoordinator]
|
||||
|
||||
|
||||
class ContextCoordinator[_DataTypeT, _ContextTypeT](DataUpdateCoordinator[_DataTypeT]):
|
||||
"""Update coordinator with context adjustments."""
|
||||
@@ -73,12 +75,12 @@ class ContextCoordinator[_DataTypeT, _ContextTypeT](DataUpdateCoordinator[_DataT
|
||||
class CoilCoordinator(ContextCoordinator[dict[int, CoilData], int]):
|
||||
"""Update coordinator for nibe heat pumps."""
|
||||
|
||||
config_entry: ConfigEntry
|
||||
config_entry: NibeHeatpumpConfigEntry
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: NibeHeatpumpConfigEntry,
|
||||
heatpump: HeatPump,
|
||||
connection: Connection,
|
||||
) -> None:
|
||||
|
||||
@@ -5,24 +5,22 @@ from __future__ import annotations
|
||||
from nibe.coil import Coil, CoilData
|
||||
|
||||
from homeassistant.components.number import ENTITY_ID_FORMAT, NumberEntity, NumberMode
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import EntityCategory
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import CoilCoordinator
|
||||
from .coordinator import CoilCoordinator, NibeHeatpumpConfigEntry
|
||||
from .entity import CoilEntity
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: NibeHeatpumpConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up platform."""
|
||||
|
||||
coordinator: CoilCoordinator = hass.data[DOMAIN][config_entry.entry_id]
|
||||
coordinator = config_entry.runtime_data
|
||||
|
||||
async_add_entities(
|
||||
Number(coordinator, coil)
|
||||
|
||||
@@ -5,24 +5,22 @@ from __future__ import annotations
|
||||
from nibe.coil import Coil, CoilData
|
||||
|
||||
from homeassistant.components.select import ENTITY_ID_FORMAT, SelectEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import EntityCategory
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import CoilCoordinator
|
||||
from .coordinator import CoilCoordinator, NibeHeatpumpConfigEntry
|
||||
from .entity import CoilEntity
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: NibeHeatpumpConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up platform."""
|
||||
|
||||
coordinator: CoilCoordinator = hass.data[DOMAIN][config_entry.entry_id]
|
||||
coordinator = config_entry.runtime_data
|
||||
|
||||
async_add_entities(
|
||||
Select(coordinator, coil)
|
||||
|
||||
@@ -11,7 +11,6 @@ from homeassistant.components.sensor import (
|
||||
SensorEntityDescription,
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
PERCENTAGE,
|
||||
EntityCategory,
|
||||
@@ -28,8 +27,7 @@ from homeassistant.const import (
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import CoilCoordinator
|
||||
from .coordinator import CoilCoordinator, NibeHeatpumpConfigEntry
|
||||
from .entity import CoilEntity
|
||||
|
||||
UNIT_DESCRIPTIONS = {
|
||||
@@ -185,12 +183,12 @@ UNIT_DESCRIPTIONS = {
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: NibeHeatpumpConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up platform."""
|
||||
|
||||
coordinator: CoilCoordinator = hass.data[DOMAIN][config_entry.entry_id]
|
||||
coordinator = config_entry.runtime_data
|
||||
|
||||
async_add_entities(
|
||||
Sensor(coordinator, coil, UNIT_DESCRIPTIONS.get(coil.unit))
|
||||
|
||||
@@ -7,24 +7,22 @@ from typing import Any
|
||||
from nibe.coil import Coil, CoilData
|
||||
|
||||
from homeassistant.components.switch import ENTITY_ID_FORMAT, SwitchEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import EntityCategory
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import CoilCoordinator
|
||||
from .coordinator import CoilCoordinator, NibeHeatpumpConfigEntry
|
||||
from .entity import CoilEntity
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: NibeHeatpumpConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up platform."""
|
||||
|
||||
coordinator: CoilCoordinator = hass.data[DOMAIN][config_entry.entry_id]
|
||||
coordinator = config_entry.runtime_data
|
||||
|
||||
async_add_entities(
|
||||
Switch(coordinator, coil)
|
||||
|
||||
@@ -14,29 +14,27 @@ from homeassistant.components.water_heater import (
|
||||
WaterHeaterEntity,
|
||||
WaterHeaterEntityFeature,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import (
|
||||
DOMAIN,
|
||||
LOGGER,
|
||||
VALUES_TEMPORARY_LUX_INACTIVE,
|
||||
VALUES_TEMPORARY_LUX_ONE_TIME_INCREASE,
|
||||
)
|
||||
from .coordinator import CoilCoordinator
|
||||
from .coordinator import CoilCoordinator, NibeHeatpumpConfigEntry
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: NibeHeatpumpConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up platform."""
|
||||
|
||||
coordinator: CoilCoordinator = hass.data[DOMAIN][config_entry.entry_id]
|
||||
coordinator = config_entry.runtime_data
|
||||
|
||||
def water_heaters():
|
||||
for key, group in WATER_HEATER_COILGROUPS.get(coordinator.series, ()).items():
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"same_station": "[%key:component::nmbs::config::error::same_station%]"
|
||||
},
|
||||
"error": {
|
||||
"same_station": "Departure and arrival station can not be the same."
|
||||
"same_station": "The departure and arrival station cannot be the same."
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
|
||||
@@ -224,7 +224,7 @@ class NumberDeviceClass(StrEnum):
|
||||
FREQUENCY = "frequency"
|
||||
"""Frequency.
|
||||
|
||||
Unit of measurement: `Hz`, `kHz`, `MHz`, `GHz`
|
||||
Unit of measurement: `mHz`, `Hz`, `kHz`, `MHz`, `GHz`
|
||||
"""
|
||||
|
||||
GAS = "gas"
|
||||
|
||||
@@ -2,31 +2,28 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_API_KEY, CONF_BASE, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
|
||||
from .const import BASE_UPDATE_INTERVAL, DOMAIN, LOGGER
|
||||
from .coordinator import OpenexchangeratesCoordinator
|
||||
from .coordinator import OpenexchangeratesConfigEntry, OpenexchangeratesCoordinator
|
||||
|
||||
PLATFORMS = [Platform.SENSOR]
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant, entry: OpenexchangeratesConfigEntry
|
||||
) -> bool:
|
||||
"""Set up Open Exchange Rates from a config entry."""
|
||||
api_key: str = entry.data[CONF_API_KEY]
|
||||
base: str = entry.data[CONF_BASE]
|
||||
|
||||
# Create one coordinator per base currency per API key.
|
||||
existing_coordinators: dict[str, OpenexchangeratesCoordinator] = hass.data.get(
|
||||
DOMAIN, {}
|
||||
)
|
||||
existing_coordinator_for_api_key = {
|
||||
existing_coordinator
|
||||
for config_entry_id, existing_coordinator in existing_coordinators.items()
|
||||
if (config_entry := hass.config_entries.async_get_entry(config_entry_id))
|
||||
and config_entry.data[CONF_API_KEY] == api_key
|
||||
existing_entry.runtime_data
|
||||
for existing_entry in hass.config_entries.async_loaded_entries(DOMAIN)
|
||||
if existing_entry.data[CONF_API_KEY] == api_key
|
||||
}
|
||||
|
||||
# Adjust update interval by coordinators per API key.
|
||||
@@ -48,16 +45,15 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator
|
||||
entry.runtime_data = coordinator
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(
|
||||
hass: HomeAssistant, entry: OpenexchangeratesConfigEntry
|
||||
) -> bool:
|
||||
"""Unload a config entry."""
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
@@ -20,16 +20,18 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, Upda
|
||||
|
||||
from .const import CLIENT_TIMEOUT, DOMAIN, LOGGER
|
||||
|
||||
type OpenexchangeratesConfigEntry = ConfigEntry[OpenexchangeratesCoordinator]
|
||||
|
||||
|
||||
class OpenexchangeratesCoordinator(DataUpdateCoordinator[Latest]):
|
||||
"""Represent a coordinator for Open Exchange Rates API."""
|
||||
|
||||
config_entry: ConfigEntry
|
||||
config_entry: OpenexchangeratesConfigEntry
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: OpenexchangeratesConfigEntry,
|
||||
session: ClientSession,
|
||||
api_key: str,
|
||||
base: str,
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from homeassistant.components.sensor import SensorEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_QUOTE
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
|
||||
@@ -11,19 +10,19 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import OpenexchangeratesCoordinator
|
||||
from .coordinator import OpenexchangeratesConfigEntry, OpenexchangeratesCoordinator
|
||||
|
||||
ATTRIBUTION = "Data provided by openexchangerates.org"
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: OpenexchangeratesConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the Open Exchange Rates sensor."""
|
||||
quote: str = config_entry.data.get(CONF_QUOTE, "EUR")
|
||||
coordinator = hass.data[DOMAIN][config_entry.entry_id]
|
||||
coordinator = config_entry.runtime_data
|
||||
|
||||
async_add_entities(
|
||||
OpenexchangeratesSensor(
|
||||
@@ -43,7 +42,7 @@ class OpenexchangeratesSensor(
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: OpenexchangeratesConfigEntry,
|
||||
coordinator: OpenexchangeratesCoordinator,
|
||||
quote: str,
|
||||
enabled: bool,
|
||||
|
||||
@@ -18,6 +18,8 @@ from .services import async_setup_services
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
type OpenhomeConfigEntry = ConfigEntry[Device]
|
||||
|
||||
CONFIG_SCHEMA = cv.empty_config_schema(DOMAIN)
|
||||
PLATFORMS = [Platform.MEDIA_PLAYER, Platform.UPDATE]
|
||||
|
||||
@@ -30,7 +32,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: OpenhomeConfigEntry,
|
||||
) -> bool:
|
||||
"""Set up the configuration config entry."""
|
||||
_LOGGER.debug("Setting up config entry: %s", config_entry.unique_id)
|
||||
@@ -44,18 +46,15 @@ async def async_setup_entry(
|
||||
|
||||
_LOGGER.debug("Initialised device: %s", device.uuid())
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})[config_entry.entry_id] = device
|
||||
config_entry.runtime_data = device
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(
|
||||
hass: HomeAssistant, config_entry: OpenhomeConfigEntry
|
||||
) -> bool:
|
||||
"""Cleanup before removing config entry."""
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(
|
||||
config_entry, PLATFORMS
|
||||
)
|
||||
hass.data[DOMAIN].pop(config_entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
return await hass.config_entries.async_unload_platforms(config_entry, PLATFORMS)
|
||||
|
||||
@@ -19,11 +19,11 @@ from homeassistant.components.media_player import (
|
||||
MediaType,
|
||||
async_process_play_media_url,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import OpenhomeConfigEntry
|
||||
from .const import DOMAIN
|
||||
|
||||
SUPPORT_OPENHOME = (
|
||||
@@ -37,14 +37,14 @@ _LOGGER = logging.getLogger(__name__)
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: OpenhomeConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the Openhome config entry."""
|
||||
|
||||
_LOGGER.debug("Setting up config entry: %s", config_entry.unique_id)
|
||||
|
||||
device = hass.data[DOMAIN][config_entry.entry_id]
|
||||
device = config_entry.runtime_data
|
||||
|
||||
entity = OpenhomeDevice(device)
|
||||
|
||||
|
||||
@@ -13,12 +13,12 @@ from homeassistant.components.update import (
|
||||
UpdateEntity,
|
||||
UpdateEntityFeature,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import OpenhomeConfigEntry
|
||||
from .const import DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -26,14 +26,14 @@ _LOGGER = logging.getLogger(__name__)
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: OpenhomeConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up update entities for Reolink component."""
|
||||
|
||||
_LOGGER.debug("Setting up config entry: %s", config_entry.unique_id)
|
||||
|
||||
device = hass.data[DOMAIN][config_entry.entry_id]
|
||||
device = config_entry.runtime_data
|
||||
|
||||
entity = OpenhomeUpdateEntity(device)
|
||||
|
||||
|
||||
@@ -7,21 +7,20 @@ import logging
|
||||
import aiohttp
|
||||
from ovoenergy import OVOEnergy
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
|
||||
from .const import CONF_ACCOUNT, DATA_CLIENT, DATA_COORDINATOR, DOMAIN
|
||||
from .coordinator import OVOEnergyDataUpdateCoordinator
|
||||
from .const import CONF_ACCOUNT
|
||||
from .coordinator import OVOEnergyConfigEntry, OVOEnergyDataUpdateCoordinator
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
PLATFORMS = [Platform.SENSOR]
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: OVOEnergyConfigEntry) -> bool:
|
||||
"""Set up OVO Energy from a config entry."""
|
||||
|
||||
client = OVOEnergy(
|
||||
@@ -45,26 +44,15 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
|
||||
coordinator = OVOEnergyDataUpdateCoordinator(hass, entry, client)
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
hass.data[DOMAIN][entry.entry_id] = {
|
||||
DATA_CLIENT: client,
|
||||
DATA_COORDINATOR: coordinator,
|
||||
}
|
||||
|
||||
# Fetch initial data so we have data when entities subscribe
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
|
||||
# Setup components
|
||||
entry.runtime_data = coordinator
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: OVOEnergyConfigEntry) -> bool:
|
||||
"""Unload OVO Energy config entry."""
|
||||
# Unload sensors
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
del hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
return unload_ok
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
@@ -2,6 +2,4 @@
|
||||
|
||||
DOMAIN = "ovo_energy"
|
||||
|
||||
DATA_CLIENT = "ovo_client"
|
||||
DATA_COORDINATOR = "coordinator"
|
||||
CONF_ACCOUNT = "account"
|
||||
|
||||
@@ -21,16 +21,18 @@ from .const import CONF_ACCOUNT
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
type OVOEnergyConfigEntry = ConfigEntry[OVOEnergyDataUpdateCoordinator]
|
||||
|
||||
|
||||
class OVOEnergyDataUpdateCoordinator(DataUpdateCoordinator[OVODailyUsage]):
|
||||
"""Class to manage fetching OVO Energy data."""
|
||||
|
||||
config_entry: ConfigEntry
|
||||
config_entry: OVOEnergyConfigEntry
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: OVOEnergyConfigEntry,
|
||||
client: OVOEnergy,
|
||||
) -> None:
|
||||
"""Initialize."""
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from ovoenergy import OVOEnergy
|
||||
|
||||
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
@@ -16,15 +14,6 @@ class OVOEnergyEntity(CoordinatorEntity[OVOEnergyDataUpdateCoordinator]):
|
||||
|
||||
_attr_has_entity_name = True
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: OVOEnergyDataUpdateCoordinator,
|
||||
client: OVOEnergy,
|
||||
) -> None:
|
||||
"""Initialize the OVO Energy entity."""
|
||||
super().__init__(coordinator)
|
||||
self._client = client
|
||||
|
||||
|
||||
class OVOEnergyDeviceEntity(OVOEnergyEntity):
|
||||
"""Defines a OVO Energy device entity."""
|
||||
@@ -34,7 +23,7 @@ class OVOEnergyDeviceEntity(OVOEnergyEntity):
|
||||
"""Return device information about this OVO Energy instance."""
|
||||
return DeviceInfo(
|
||||
entry_type=DeviceEntryType.SERVICE,
|
||||
identifiers={(DOMAIN, self._client.account_id)},
|
||||
identifiers={(DOMAIN, self.coordinator.client.account_id)},
|
||||
manufacturer="OVO Energy",
|
||||
name=self._client.username,
|
||||
name=self.coordinator.client.username,
|
||||
)
|
||||
|
||||
@@ -7,7 +7,6 @@ import dataclasses
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Final
|
||||
|
||||
from ovoenergy import OVOEnergy
|
||||
from ovoenergy.models import OVODailyUsage
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
@@ -16,15 +15,14 @@ from homeassistant.components.sensor import (
|
||||
SensorEntityDescription,
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import UnitOfEnergy
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.typing import StateType
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from .const import DATA_CLIENT, DATA_COORDINATOR, DOMAIN
|
||||
from .coordinator import OVOEnergyDataUpdateCoordinator
|
||||
from .const import DOMAIN
|
||||
from .coordinator import OVOEnergyConfigEntry, OVOEnergyDataUpdateCoordinator
|
||||
from .entity import OVOEnergyDeviceEntity
|
||||
|
||||
SCAN_INTERVAL = timedelta(seconds=300)
|
||||
@@ -114,14 +112,11 @@ SENSOR_TYPES_GAS: tuple[OVOEnergySensorEntityDescription, ...] = (
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: OVOEnergyConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up OVO Energy sensor based on a config entry."""
|
||||
coordinator: OVOEnergyDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id][
|
||||
DATA_COORDINATOR
|
||||
]
|
||||
client: OVOEnergy = hass.data[DOMAIN][entry.entry_id][DATA_CLIENT]
|
||||
coordinator = entry.runtime_data
|
||||
|
||||
entities = []
|
||||
|
||||
@@ -139,7 +134,7 @@ async def async_setup_entry(
|
||||
coordinator.data.electricity[-1].cost.currency_unit
|
||||
),
|
||||
)
|
||||
entities.append(OVOEnergySensor(coordinator, description, client))
|
||||
entities.append(OVOEnergySensor(coordinator, description))
|
||||
if coordinator.data.gas:
|
||||
for description in SENSOR_TYPES_GAS:
|
||||
if (
|
||||
@@ -153,7 +148,7 @@ async def async_setup_entry(
|
||||
-1
|
||||
].cost.currency_unit,
|
||||
)
|
||||
entities.append(OVOEnergySensor(coordinator, description, client))
|
||||
entities.append(OVOEnergySensor(coordinator, description))
|
||||
|
||||
async_add_entities(entities, True)
|
||||
|
||||
@@ -167,11 +162,12 @@ class OVOEnergySensor(OVOEnergyDeviceEntity, SensorEntity):
|
||||
self,
|
||||
coordinator: OVOEnergyDataUpdateCoordinator,
|
||||
description: OVOEnergySensorEntityDescription,
|
||||
client: OVOEnergy,
|
||||
) -> None:
|
||||
"""Initialize."""
|
||||
super().__init__(coordinator, client)
|
||||
self._attr_unique_id = f"{DOMAIN}_{client.account_id}_{description.key}"
|
||||
super().__init__(coordinator)
|
||||
self._attr_unique_id = (
|
||||
f"{DOMAIN}_{coordinator.client.account_id}_{description.key}"
|
||||
)
|
||||
self.entity_description = description
|
||||
|
||||
@property
|
||||
|
||||
@@ -19,7 +19,6 @@ from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
from .const import (
|
||||
ATTR_DEVICE_INFO,
|
||||
ATTR_REMOTE,
|
||||
ATTR_UDN,
|
||||
CONF_APP_ID,
|
||||
CONF_ENCRYPTION_KEY,
|
||||
@@ -29,6 +28,8 @@ from .const import (
|
||||
DOMAIN,
|
||||
)
|
||||
|
||||
type PanasonicVieraConfigEntry = ConfigEntry[Remote]
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema(
|
||||
@@ -68,10 +69,10 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant, config_entry: PanasonicVieraConfigEntry
|
||||
) -> bool:
|
||||
"""Set up Panasonic Viera from a config entry."""
|
||||
panasonic_viera_data = hass.data.setdefault(DOMAIN, {})
|
||||
|
||||
config = config_entry.data
|
||||
|
||||
host = config[CONF_HOST]
|
||||
@@ -88,7 +89,7 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
|
||||
remote = Remote(hass, host, port, on_action, **params)
|
||||
await remote.async_create_remote_control(during_setup=True)
|
||||
|
||||
panasonic_viera_data[config_entry.entry_id] = {ATTR_REMOTE: remote}
|
||||
config_entry.runtime_data = remote
|
||||
|
||||
# Add device_info to older config entries
|
||||
if ATTR_DEVICE_INFO not in config or config[ATTR_DEVICE_INFO] is None:
|
||||
@@ -112,15 +113,11 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(
|
||||
hass: HomeAssistant, config_entry: PanasonicVieraConfigEntry
|
||||
) -> bool:
|
||||
"""Unload a config entry."""
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(
|
||||
config_entry, PLATFORMS
|
||||
)
|
||||
if unload_ok:
|
||||
hass.data[DOMAIN].pop(config_entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
return await hass.config_entries.async_unload_platforms(config_entry, PLATFORMS)
|
||||
|
||||
|
||||
class Remote:
|
||||
|
||||
@@ -17,17 +17,16 @@ from homeassistant.components.media_player import (
|
||||
MediaType,
|
||||
async_process_play_media_url,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_NAME
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import PanasonicVieraConfigEntry
|
||||
from .const import (
|
||||
ATTR_DEVICE_INFO,
|
||||
ATTR_MANUFACTURER,
|
||||
ATTR_MODEL_NUMBER,
|
||||
ATTR_REMOTE,
|
||||
ATTR_UDN,
|
||||
DEFAULT_MANUFACTURER,
|
||||
DEFAULT_MODEL_NUMBER,
|
||||
@@ -39,14 +38,14 @@ _LOGGER = logging.getLogger(__name__)
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: PanasonicVieraConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Panasonic Viera TV from a config entry."""
|
||||
|
||||
config = config_entry.data
|
||||
|
||||
remote = hass.data[DOMAIN][config_entry.entry_id][ATTR_REMOTE]
|
||||
remote = config_entry.runtime_data
|
||||
name = config[CONF_NAME]
|
||||
device_info = config[ATTR_DEVICE_INFO]
|
||||
|
||||
|
||||
@@ -6,18 +6,16 @@ from collections.abc import Iterable
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.components.remote import RemoteEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_NAME, STATE_ON
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import Remote
|
||||
from . import PanasonicVieraConfigEntry, Remote
|
||||
from .const import (
|
||||
ATTR_DEVICE_INFO,
|
||||
ATTR_MANUFACTURER,
|
||||
ATTR_MODEL_NUMBER,
|
||||
ATTR_REMOTE,
|
||||
ATTR_UDN,
|
||||
DEFAULT_MANUFACTURER,
|
||||
DEFAULT_MODEL_NUMBER,
|
||||
@@ -27,14 +25,14 @@ from .const import (
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: PanasonicVieraConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Panasonic Viera TV Remote from a config entry."""
|
||||
|
||||
config = config_entry.data
|
||||
|
||||
remote = hass.data[DOMAIN][config_entry.entry_id][ATTR_REMOTE]
|
||||
remote = config_entry.runtime_data
|
||||
name = config[CONF_NAME]
|
||||
device_info = config[ATTR_DEVICE_INFO]
|
||||
|
||||
|
||||
@@ -4,37 +4,39 @@ from __future__ import annotations
|
||||
|
||||
from typing import Final
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .const import CONF_PHONE_NUMBER, DOMAIN
|
||||
from .coordinator import PecoOutageCoordinator, PecoSmartMeterCoordinator
|
||||
from .const import CONF_PHONE_NUMBER
|
||||
from .coordinator import (
|
||||
PecoConfigEntry,
|
||||
PecoOutageCoordinator,
|
||||
PecoRuntimeData,
|
||||
PecoSmartMeterCoordinator,
|
||||
)
|
||||
|
||||
PLATFORMS: Final = [Platform.BINARY_SENSOR, Platform.SENSOR]
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: PecoConfigEntry) -> bool:
|
||||
"""Set up PECO Outage Counter from a config entry."""
|
||||
outage_coordinator = PecoOutageCoordinator(hass, entry)
|
||||
await outage_coordinator.async_config_entry_first_refresh()
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = {
|
||||
"outage_count": outage_coordinator
|
||||
}
|
||||
|
||||
meter_coordinator: PecoSmartMeterCoordinator | None = None
|
||||
if phone_number := entry.data.get(CONF_PHONE_NUMBER):
|
||||
meter_coordinator = PecoSmartMeterCoordinator(hass, entry, phone_number)
|
||||
await meter_coordinator.async_config_entry_first_refresh()
|
||||
hass.data[DOMAIN][entry.entry_id]["smart_meter"] = meter_coordinator
|
||||
|
||||
entry.runtime_data = PecoRuntimeData(
|
||||
outage_coordinator=outage_coordinator,
|
||||
meter_coordinator=meter_coordinator,
|
||||
)
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: PecoConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
@@ -8,28 +8,23 @@ from homeassistant.components.binary_sensor import (
|
||||
BinarySensorDeviceClass,
|
||||
BinarySensorEntity,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import PecoSmartMeterCoordinator
|
||||
from .coordinator import PecoConfigEntry, PecoSmartMeterCoordinator
|
||||
|
||||
PARALLEL_UPDATES: Final = 0
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: PecoConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up binary sensor for PECO."""
|
||||
if "smart_meter" not in hass.data[DOMAIN][config_entry.entry_id]:
|
||||
if (coordinator := config_entry.runtime_data.meter_coordinator) is None:
|
||||
return
|
||||
coordinator: PecoSmartMeterCoordinator = hass.data[DOMAIN][config_entry.entry_id][
|
||||
"smart_meter"
|
||||
]
|
||||
|
||||
async_add_entities(
|
||||
[PecoBinarySensor(coordinator, phone_number=config_entry.data["phone_number"])]
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
"""DataUpdateCoordinator for the PECO Outage Counter integration."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from datetime import timedelta
|
||||
|
||||
@@ -28,12 +30,23 @@ class PECOCoordinatorData:
|
||||
alerts: AlertResults
|
||||
|
||||
|
||||
@dataclass
|
||||
class PecoRuntimeData:
|
||||
"""Runtime data for the PECO integration."""
|
||||
|
||||
outage_coordinator: PecoOutageCoordinator
|
||||
meter_coordinator: PecoSmartMeterCoordinator | None = None
|
||||
|
||||
|
||||
type PecoConfigEntry = ConfigEntry[PecoRuntimeData]
|
||||
|
||||
|
||||
class PecoOutageCoordinator(DataUpdateCoordinator[PECOCoordinatorData]):
|
||||
"""Coordinator for PECO outage data."""
|
||||
|
||||
config_entry: ConfigEntry
|
||||
config_entry: PecoConfigEntry
|
||||
|
||||
def __init__(self, hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
def __init__(self, hass: HomeAssistant, entry: PecoConfigEntry) -> None:
|
||||
"""Initialize the outage coordinator."""
|
||||
super().__init__(
|
||||
hass,
|
||||
@@ -65,10 +78,10 @@ class PecoOutageCoordinator(DataUpdateCoordinator[PECOCoordinatorData]):
|
||||
class PecoSmartMeterCoordinator(DataUpdateCoordinator[bool]):
|
||||
"""Coordinator for PECO smart meter data."""
|
||||
|
||||
config_entry: ConfigEntry
|
||||
config_entry: PecoConfigEntry
|
||||
|
||||
def __init__(
|
||||
self, hass: HomeAssistant, entry: ConfigEntry, phone_number: str
|
||||
self, hass: HomeAssistant, entry: PecoConfigEntry, phone_number: str
|
||||
) -> None:
|
||||
"""Initialize the smart meter coordinator."""
|
||||
super().__init__(
|
||||
|
||||
@@ -11,7 +11,6 @@ from homeassistant.components.sensor import (
|
||||
SensorEntityDescription,
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import PERCENTAGE
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
@@ -19,7 +18,7 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import ATTR_CONTENT, CONF_COUNTY, DOMAIN
|
||||
from .coordinator import PECOCoordinatorData, PecoOutageCoordinator
|
||||
from .coordinator import PecoConfigEntry, PECOCoordinatorData, PecoOutageCoordinator
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
@@ -72,12 +71,12 @@ SENSOR_LIST: tuple[PECOSensorEntityDescription, ...] = (
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: PecoConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the sensor platform."""
|
||||
county: str = config_entry.data[CONF_COUNTY]
|
||||
coordinator = hass.data[DOMAIN][config_entry.entry_id]["outage_count"]
|
||||
coordinator = config_entry.runtime_data.outage_coordinator
|
||||
|
||||
async_add_entities(
|
||||
PecoSensor(sensor, county, coordinator) for sensor in SENSOR_LIST
|
||||
|
||||
@@ -6,7 +6,6 @@ import logging
|
||||
|
||||
from mypermobil import MyPermobil, MyPermobilClientException
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
CONF_CODE,
|
||||
CONF_EMAIL,
|
||||
@@ -19,15 +18,15 @@ from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
|
||||
from .const import APPLICATION, DOMAIN
|
||||
from .coordinator import MyPermobilCoordinator
|
||||
from .const import APPLICATION
|
||||
from .coordinator import MyPermobilCoordinator, PermobilConfigEntry
|
||||
|
||||
PLATFORMS: list[Platform] = [Platform.BINARY_SENSOR, Platform.SENSOR]
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: PermobilConfigEntry) -> bool:
|
||||
"""Set up MyPermobil from a config entry."""
|
||||
|
||||
# create the API object from the config and save it in hass
|
||||
@@ -51,15 +50,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
coordinator = MyPermobilCoordinator(hass, entry, p_api)
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator
|
||||
entry.runtime_data = coordinator
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: PermobilConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
@@ -8,7 +8,6 @@ from typing import Any
|
||||
|
||||
from mypermobil import BATTERY_CHARGING
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components.binary_sensor import (
|
||||
BinarySensorEntity,
|
||||
BinarySensorEntityDescription,
|
||||
@@ -16,8 +15,7 @@ from homeassistant.components.binary_sensor import (
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import MyPermobilCoordinator
|
||||
from .coordinator import PermobilConfigEntry
|
||||
from .entity import PermobilEntity
|
||||
|
||||
|
||||
@@ -41,12 +39,12 @@ BINARY_SENSOR_DESCRIPTIONS: tuple[PermobilBinarySensorEntityDescription, ...] =
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: config_entries.ConfigEntry,
|
||||
config_entry: PermobilConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Create and setup the binary sensor."""
|
||||
|
||||
coordinator: MyPermobilCoordinator = hass.data[DOMAIN][config_entry.entry_id]
|
||||
coordinator = config_entry.runtime_data
|
||||
|
||||
async_add_entities(
|
||||
PermobilbinarySensor(coordinator=coordinator, description=description)
|
||||
|
||||
@@ -13,6 +13,8 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, Upda
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
type PermobilConfigEntry = ConfigEntry[MyPermobilCoordinator]
|
||||
|
||||
|
||||
@dataclass
|
||||
class MyPermobilData:
|
||||
@@ -26,10 +28,10 @@ class MyPermobilData:
|
||||
class MyPermobilCoordinator(DataUpdateCoordinator[MyPermobilData]):
|
||||
"""MyPermobil coordinator."""
|
||||
|
||||
config_entry: ConfigEntry
|
||||
config_entry: PermobilConfigEntry
|
||||
|
||||
def __init__(
|
||||
self, hass: HomeAssistant, config_entry: ConfigEntry, p_api: MyPermobil
|
||||
self, hass: HomeAssistant, config_entry: PermobilConfigEntry, p_api: MyPermobil
|
||||
) -> None:
|
||||
"""Initialize my coordinator."""
|
||||
super().__init__(
|
||||
|
||||
@@ -23,7 +23,6 @@ from mypermobil import (
|
||||
USAGE_DISTANCE,
|
||||
)
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components.sensor import (
|
||||
SensorDeviceClass,
|
||||
SensorEntity,
|
||||
@@ -34,8 +33,8 @@ from homeassistant.const import PERCENTAGE, UnitOfEnergy, UnitOfLength, UnitOfTi
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import BATTERY_ASSUMED_VOLTAGE, DOMAIN, KM, MILES
|
||||
from .coordinator import MyPermobilCoordinator
|
||||
from .const import BATTERY_ASSUMED_VOLTAGE, KM, MILES
|
||||
from .coordinator import PermobilConfigEntry
|
||||
from .entity import PermobilEntity
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -176,12 +175,12 @@ DISTANCE_UNITS: dict[Any, UnitOfLength] = {
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: config_entries.ConfigEntry,
|
||||
config_entry: PermobilConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Create sensors from a config entry created in the integrations UI."""
|
||||
|
||||
coordinator: MyPermobilCoordinator = hass.data[DOMAIN][config_entry.entry_id]
|
||||
coordinator = config_entry.runtime_data
|
||||
|
||||
async_add_entities(
|
||||
PermobilSensor(coordinator=coordinator, description=description)
|
||||
|
||||
@@ -11,6 +11,7 @@ import voluptuous as vol
|
||||
from homeassistant.auth import EVENT_USER_REMOVED
|
||||
from homeassistant.components import persistent_notification, websocket_api
|
||||
from homeassistant.components.device_tracker import (
|
||||
ATTR_IN_ZONES,
|
||||
ATTR_SOURCE_TYPE,
|
||||
DOMAIN as DEVICE_TRACKER_DOMAIN,
|
||||
SourceType,
|
||||
@@ -435,6 +436,7 @@ class Person(
|
||||
self._unsub_track_device: Callable[[], None] | None = None
|
||||
self._attr_state: str | None = None
|
||||
self.device_trackers: list[str] = []
|
||||
self._in_zones: list[str] = []
|
||||
|
||||
self._attr_unique_id = config[CONF_ID]
|
||||
self._set_attrs_from_config()
|
||||
@@ -552,6 +554,7 @@ class Person(
|
||||
self._latitude = None
|
||||
self._longitude = None
|
||||
self._gps_accuracy = None
|
||||
self._in_zones = []
|
||||
|
||||
self._update_extra_state_attributes()
|
||||
self.async_write_ha_state()
|
||||
@@ -566,7 +569,8 @@ class Person(
|
||||
self._source = state.entity_id
|
||||
self._latitude = coordinates.attributes.get(ATTR_LATITUDE)
|
||||
self._longitude = coordinates.attributes.get(ATTR_LONGITUDE)
|
||||
self._gps_accuracy = state.attributes.get(ATTR_GPS_ACCURACY)
|
||||
self._gps_accuracy = coordinates.attributes.get(ATTR_GPS_ACCURACY)
|
||||
self._in_zones = coordinates.attributes.get(ATTR_IN_ZONES, [])
|
||||
|
||||
@callback
|
||||
def _update_extra_state_attributes(self) -> None:
|
||||
@@ -575,6 +579,7 @@ class Person(
|
||||
ATTR_EDITABLE: self.editable,
|
||||
ATTR_ID: self.unique_id,
|
||||
ATTR_DEVICE_TRACKERS: self.device_trackers,
|
||||
ATTR_IN_ZONES: self._in_zones,
|
||||
}
|
||||
|
||||
if self._latitude is not None:
|
||||
|
||||
@@ -2,14 +2,13 @@
|
||||
|
||||
from python_picnic_api2 import PicnicAPI
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_COUNTRY_CODE, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
from .const import CONF_API, CONF_COORDINATOR, DOMAIN
|
||||
from .coordinator import PicnicUpdateCoordinator
|
||||
from .const import DOMAIN
|
||||
from .coordinator import PicnicConfigEntry, PicnicUpdateCoordinator
|
||||
from .services import async_setup_services
|
||||
|
||||
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
|
||||
@@ -24,7 +23,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
def create_picnic_client(entry: ConfigEntry):
|
||||
def create_picnic_client(entry: PicnicConfigEntry):
|
||||
"""Create an instance of the PicnicAPI client."""
|
||||
return PicnicAPI(
|
||||
auth_token=entry.data.get(CONF_ACCESS_TOKEN),
|
||||
@@ -32,7 +31,7 @@ def create_picnic_client(entry: ConfigEntry):
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: PicnicConfigEntry) -> bool:
|
||||
"""Set up Picnic from a config entry."""
|
||||
picnic_client = await hass.async_add_executor_job(create_picnic_client, entry)
|
||||
picnic_coordinator = PicnicUpdateCoordinator(hass, picnic_client, entry)
|
||||
@@ -40,21 +39,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
# Fetch initial data so we have data when entities subscribe
|
||||
await picnic_coordinator.async_config_entry_first_refresh()
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
hass.data[DOMAIN][entry.entry_id] = {
|
||||
CONF_API: picnic_client,
|
||||
CONF_COORDINATOR: picnic_coordinator,
|
||||
}
|
||||
entry.runtime_data = picnic_coordinator
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: PicnicConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
if unload_ok:
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
@@ -4,9 +4,6 @@ from __future__ import annotations
|
||||
|
||||
DOMAIN = "picnic"
|
||||
|
||||
CONF_API = "api"
|
||||
CONF_COORDINATOR = "coordinator"
|
||||
|
||||
SERVICE_ADD_PRODUCT_TO_CART = "add_product"
|
||||
|
||||
ATTR_PRODUCT_ID = "product_id"
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
"""Coordinator to fetch data from the Picnic API."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from contextlib import suppress
|
||||
import copy
|
||||
@@ -17,17 +19,19 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, Upda
|
||||
|
||||
from .const import ADDRESS, CART_DATA, LAST_ORDER_DATA, NEXT_DELIVERY_DATA, SLOT_DATA
|
||||
|
||||
type PicnicConfigEntry = ConfigEntry[PicnicUpdateCoordinator]
|
||||
|
||||
|
||||
class PicnicUpdateCoordinator(DataUpdateCoordinator):
|
||||
"""The coordinator to fetch data from the Picnic API at a set interval."""
|
||||
|
||||
config_entry: ConfigEntry
|
||||
config_entry: PicnicConfigEntry
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
picnic_api_client: PicnicAPI,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: PicnicConfigEntry,
|
||||
) -> None:
|
||||
"""Initialize the coordinator with the given Picnic API client."""
|
||||
self.picnic_api_client = picnic_api_client
|
||||
|
||||
@@ -12,7 +12,6 @@ from homeassistant.components.sensor import (
|
||||
SensorEntity,
|
||||
SensorEntityDescription,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CURRENCY_EURO
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
|
||||
@@ -23,7 +22,6 @@ from homeassistant.util import dt as dt_util
|
||||
|
||||
from .const import (
|
||||
ATTRIBUTION,
|
||||
CONF_COORDINATOR,
|
||||
DOMAIN,
|
||||
SENSOR_CART_ITEMS_COUNT,
|
||||
SENSOR_CART_TOTAL_PRICE,
|
||||
@@ -42,7 +40,7 @@ from .const import (
|
||||
SENSOR_SELECTED_SLOT_MIN_ORDER_VALUE,
|
||||
SENSOR_SELECTED_SLOT_START,
|
||||
)
|
||||
from .coordinator import PicnicUpdateCoordinator
|
||||
from .coordinator import PicnicConfigEntry, PicnicUpdateCoordinator
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
@@ -202,11 +200,11 @@ SENSOR_TYPES: tuple[PicnicSensorEntityDescription, ...] = (
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: PicnicConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Picnic sensor entries."""
|
||||
picnic_coordinator = hass.data[DOMAIN][config_entry.entry_id][CONF_COORDINATOR]
|
||||
picnic_coordinator = config_entry.runtime_data
|
||||
|
||||
# Add an entity for each sensor type
|
||||
async_add_entities(
|
||||
@@ -225,7 +223,7 @@ class PicnicSensor(SensorEntity, CoordinatorEntity[PicnicUpdateCoordinator]):
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: PicnicUpdateCoordinator,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: PicnicConfigEntry,
|
||||
description: PicnicSensorEntityDescription,
|
||||
) -> None:
|
||||
"""Init a Picnic sensor."""
|
||||
|
||||
@@ -7,6 +7,7 @@ from typing import cast
|
||||
from python_picnic_api2 import PicnicAPI
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.const import ATTR_CONFIG_ENTRY_ID
|
||||
from homeassistant.core import HomeAssistant, ServiceCall, callback
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
@@ -16,10 +17,10 @@ from .const import (
|
||||
ATTR_PRODUCT_ID,
|
||||
ATTR_PRODUCT_IDENTIFIERS,
|
||||
ATTR_PRODUCT_NAME,
|
||||
CONF_API,
|
||||
DOMAIN,
|
||||
SERVICE_ADD_PRODUCT_TO_CART,
|
||||
)
|
||||
from .coordinator import PicnicConfigEntry
|
||||
|
||||
|
||||
class PicnicServiceException(Exception):
|
||||
@@ -50,10 +51,14 @@ def async_setup_services(hass: HomeAssistant) -> None:
|
||||
|
||||
|
||||
async def get_api_client(hass: HomeAssistant, config_entry_id: str) -> PicnicAPI:
|
||||
"""Get the right Picnic API client based on the device id, else get the default one."""
|
||||
if config_entry_id not in hass.data[DOMAIN]:
|
||||
"""Get the right Picnic API client based on the config entry id."""
|
||||
|
||||
entry: PicnicConfigEntry | None = hass.config_entries.async_get_entry(
|
||||
config_entry_id
|
||||
)
|
||||
if entry is None or entry.state != ConfigEntryState.LOADED:
|
||||
raise ValueError(f"Config entry with id {config_entry_id} not found!")
|
||||
return hass.data[DOMAIN][config_entry_id][CONF_API]
|
||||
return entry.runtime_data.picnic_api_client
|
||||
|
||||
|
||||
async def handle_add_product(
|
||||
|
||||
@@ -11,15 +11,14 @@ from homeassistant.components.todo import (
|
||||
TodoListEntity,
|
||||
TodoListEntityFeature,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ServiceValidationError
|
||||
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import CONF_COORDINATOR, DOMAIN
|
||||
from .coordinator import PicnicUpdateCoordinator
|
||||
from .const import DOMAIN
|
||||
from .coordinator import PicnicConfigEntry, PicnicUpdateCoordinator
|
||||
from .services import product_search
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -27,11 +26,11 @@ _LOGGER = logging.getLogger(__name__)
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: PicnicConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the Picnic shopping cart todo platform config entry."""
|
||||
picnic_coordinator = hass.data[DOMAIN][config_entry.entry_id][CONF_COORDINATOR]
|
||||
picnic_coordinator = config_entry.runtime_data
|
||||
|
||||
async_add_entities([PicnicCart(picnic_coordinator, config_entry)])
|
||||
|
||||
@@ -46,7 +45,7 @@ class PicnicCart(TodoListEntity, CoordinatorEntity[PicnicUpdateCoordinator]):
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: PicnicUpdateCoordinator,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: PicnicConfigEntry,
|
||||
) -> None:
|
||||
"""Initialize PicnicCart."""
|
||||
super().__init__(coordinator)
|
||||
|
||||
@@ -1 +1,31 @@
|
||||
"""Support for pico integration."""
|
||||
"""The Pico TTS integration."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import shutil
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryError
|
||||
|
||||
from .const import DOMAIN
|
||||
|
||||
PLATFORMS: list[Platform] = [Platform.TTS]
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up Pico TTS from a config entry."""
|
||||
if await hass.async_add_executor_job(shutil.which, "pico2wave") is None:
|
||||
raise ConfigEntryError(
|
||||
translation_domain=DOMAIN, translation_key="binary_not_found"
|
||||
)
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
49
homeassistant/components/picotts/config_flow.py
Normal file
49
homeassistant/components/picotts/config_flow.py
Normal file
@@ -0,0 +1,49 @@
|
||||
"""Config flow for Pico TTS integration."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import shutil
|
||||
from typing import Any
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.tts import CONF_LANG
|
||||
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
|
||||
|
||||
from .const import DEFAULT_LANG, DOMAIN, SUPPORT_LANGUAGES
|
||||
|
||||
STEP_USER_DATA_SCHEMA = vol.Schema(
|
||||
{vol.Required(CONF_LANG, default=DEFAULT_LANG): vol.In(SUPPORT_LANGUAGES)}
|
||||
)
|
||||
|
||||
|
||||
class PicoTTSConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
"""Handle a config flow for Pico TTS."""
|
||||
|
||||
async def async_step_user(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
"""Handle the initial step."""
|
||||
if await self.hass.async_add_executor_job(shutil.which, "pico2wave") is None:
|
||||
return self.async_abort(reason="binary_not_found")
|
||||
|
||||
if user_input is None:
|
||||
return self.async_show_form(
|
||||
step_id="user", data_schema=STEP_USER_DATA_SCHEMA
|
||||
)
|
||||
|
||||
language = user_input[CONF_LANG]
|
||||
|
||||
self._async_abort_entries_match({CONF_LANG: language})
|
||||
|
||||
title = f"Pico TTS {language}"
|
||||
data = {
|
||||
CONF_LANG: language,
|
||||
}
|
||||
|
||||
return self.async_create_entry(title=title, data=data)
|
||||
|
||||
async def async_step_import(self, import_info: dict[str, Any]) -> ConfigFlowResult:
|
||||
"""Import Pico TTS config from yaml."""
|
||||
|
||||
return await self.async_step_user(import_info)
|
||||
6
homeassistant/components/picotts/const.py
Normal file
6
homeassistant/components/picotts/const.py
Normal file
@@ -0,0 +1,6 @@
|
||||
"""Constants for the Pico TTS integration."""
|
||||
|
||||
DEFAULT_LANG = "en-US"
|
||||
DOMAIN = "picotts"
|
||||
|
||||
SUPPORT_LANGUAGES = ["en-US", "en-GB", "de-DE", "es-ES", "fr-FR", "it-IT"]
|
||||
25
homeassistant/components/picotts/issue.py
Normal file
25
homeassistant/components/picotts/issue.py
Normal file
@@ -0,0 +1,25 @@
|
||||
"""Issues for Pico TTS integration."""
|
||||
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
|
||||
|
||||
from .const import DOMAIN
|
||||
|
||||
|
||||
@callback
|
||||
def deprecate_yaml_issue(hass: HomeAssistant) -> None:
|
||||
"""Deprecate yaml issue."""
|
||||
async_create_issue(
|
||||
hass,
|
||||
DOMAIN,
|
||||
f"deprecated_yaml_{DOMAIN}",
|
||||
is_fixable=False,
|
||||
issue_domain=DOMAIN,
|
||||
breaks_in_ha_version="2026.10.0",
|
||||
severity=IssueSeverity.WARNING,
|
||||
translation_key="deprecated_yaml",
|
||||
translation_placeholders={
|
||||
"domain": DOMAIN,
|
||||
"integration_title": "Pico TTS",
|
||||
},
|
||||
)
|
||||
@@ -1,8 +1,9 @@
|
||||
{
|
||||
"domain": "picotts",
|
||||
"name": "Pico TTS",
|
||||
"codeowners": [],
|
||||
"codeowners": ["@rooggiieerr"],
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/picotts",
|
||||
"iot_class": "local_push",
|
||||
"quality_scale": "legacy"
|
||||
"integration_type": "service",
|
||||
"iot_class": "local_push"
|
||||
}
|
||||
|
||||
38
homeassistant/components/picotts/strings.json
Normal file
38
homeassistant/components/picotts/strings.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"common": {
|
||||
"binary_not_found": "pico2wave binary could not be found"
|
||||
},
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]",
|
||||
"binary_not_found": "[%key:component::picotts::common::binary_not_found%]"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"language": "[%key:common::config_flow::data::language%]"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"exceptions": {
|
||||
"binary_not_found": {
|
||||
"message": "[%key:component::picotts::common::binary_not_found%]"
|
||||
},
|
||||
"file_read_error": {
|
||||
"message": "Error trying to read {filename}"
|
||||
},
|
||||
"returncode_error": {
|
||||
"message": "Error running pico2wave, return code: {returncode}"
|
||||
},
|
||||
"timeout_error": {
|
||||
"message": "Timeout running pico2wave"
|
||||
}
|
||||
},
|
||||
"issues": {
|
||||
"deprecated_yaml": {
|
||||
"description": "Configuring {integration_title} using YAML is being removed.\n\nYour existing YAML configuration has been imported into the UI automatically.\n\nThe actions `tts.{domain}_*_say` will be removed and automations should be updated to use the `tts.speak` action with the new tts entities. Then remove the `{domain}` configuration from your configuration.yaml file and restart Home Assistant to fix this issue.",
|
||||
"title": "[%key:component::homeassistant::issues::deprecated_yaml::title%]"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
"""Support for the Pico TTS speech service."""
|
||||
|
||||
import contextlib
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
@@ -13,32 +14,114 @@ from homeassistant.components.tts import (
|
||||
CONF_LANG,
|
||||
PLATFORM_SCHEMA as TTS_PLATFORM_SCHEMA,
|
||||
Provider,
|
||||
TextToSpeechEntity,
|
||||
TtsAudioType,
|
||||
)
|
||||
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
|
||||
from .const import DEFAULT_LANG, DOMAIN, SUPPORT_LANGUAGES
|
||||
from .issue import deprecate_yaml_issue
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
SUPPORT_LANGUAGES = ["en-US", "en-GB", "de-DE", "es-ES", "fr-FR", "it-IT"]
|
||||
|
||||
DEFAULT_LANG = "en-US"
|
||||
|
||||
PLATFORM_SCHEMA = TTS_PLATFORM_SCHEMA.extend(
|
||||
{vol.Optional(CONF_LANG, default=DEFAULT_LANG): vol.In(SUPPORT_LANGUAGES)}
|
||||
)
|
||||
|
||||
|
||||
def get_engine(hass, config, discovery_info=None):
|
||||
async def async_get_engine(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigType,
|
||||
discovery_info: DiscoveryInfoType | None = None,
|
||||
) -> Provider | None:
|
||||
"""Set up Pico speech component."""
|
||||
if shutil.which("pico2wave") is None:
|
||||
if await hass.async_add_executor_job(shutil.which, "pico2wave") is None:
|
||||
_LOGGER.error("'pico2wave' was not found")
|
||||
return False
|
||||
return None
|
||||
|
||||
hass.async_create_task(
|
||||
hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_IMPORT}, data=config
|
||||
)
|
||||
)
|
||||
|
||||
deprecate_yaml_issue(hass)
|
||||
|
||||
return PicoProvider(config[CONF_LANG])
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Pico TTS speech component via config entry."""
|
||||
async_add_entities([PicoTTSEntity(config_entry, config_entry.data[CONF_LANG])])
|
||||
|
||||
|
||||
class PicoTTSEntity(TextToSpeechEntity):
|
||||
"""The Pico TTS API entity."""
|
||||
|
||||
_attr_supported_languages = SUPPORT_LANGUAGES
|
||||
|
||||
def __init__(self, config_entry: ConfigEntry, lang: str) -> None:
|
||||
"""Initialize Pico TTS service."""
|
||||
self._attr_default_language = lang
|
||||
self._attr_name = f"Pico TTS {lang}"
|
||||
self._attr_unique_id = config_entry.entry_id
|
||||
self._attr_device_info = DeviceInfo(
|
||||
entry_type=DeviceEntryType.SERVICE,
|
||||
identifiers={(DOMAIN, config_entry.entry_id)},
|
||||
model="Pico TTS",
|
||||
name=f"Pico TTS {lang}",
|
||||
)
|
||||
|
||||
def get_tts_audio(
|
||||
self, message: str, language: str, options: dict[str, Any]
|
||||
) -> TtsAudioType:
|
||||
"""Load TTS using pico2wave."""
|
||||
with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tmpf:
|
||||
fname = tmpf.name
|
||||
|
||||
cmd = ["pico2wave", "--wave", fname, "-l", language]
|
||||
try:
|
||||
subprocess.run(cmd, text=True, input=message, check=True, timeout=30)
|
||||
with open(fname, "rb") as voice:
|
||||
data = voice.read()
|
||||
except subprocess.CalledProcessError as exc:
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="returncode_error",
|
||||
translation_placeholders={"returncode": str(exc.returncode)},
|
||||
) from exc
|
||||
except subprocess.TimeoutExpired as exc:
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="timeout_error",
|
||||
) from exc
|
||||
except OSError as exc:
|
||||
_LOGGER.debug("Full exception %s", exc)
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="file_read_error",
|
||||
translation_placeholders={"filename": fname},
|
||||
) from exc
|
||||
finally:
|
||||
with contextlib.suppress(OSError):
|
||||
os.remove(fname)
|
||||
|
||||
return "wav", data
|
||||
|
||||
|
||||
class PicoProvider(Provider):
|
||||
"""The Pico TTS API provider."""
|
||||
|
||||
def __init__(self, lang):
|
||||
def __init__(self, lang: str) -> None:
|
||||
"""Initialize Pico TTS provider."""
|
||||
self._lang = lang
|
||||
self.name = "PicoTTS"
|
||||
@@ -68,15 +151,15 @@ class PicoProvider(Provider):
|
||||
_LOGGER.error(
|
||||
"Error running pico2wave, return code: %s", result.returncode
|
||||
)
|
||||
return (None, None)
|
||||
return None, None
|
||||
with open(fname, "rb") as voice:
|
||||
data = voice.read()
|
||||
except OSError:
|
||||
_LOGGER.error("Error trying to read %s", fname)
|
||||
return (None, None)
|
||||
return None, None
|
||||
finally:
|
||||
os.remove(fname)
|
||||
|
||||
if data:
|
||||
return ("wav", data)
|
||||
return (None, None)
|
||||
return None, None
|
||||
|
||||
@@ -26,7 +26,7 @@ from pyportainer.models.stacks import Stack
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_URL
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||
|
||||
from .const import DOMAIN, ContainerState, EndpointStatus
|
||||
@@ -118,13 +118,13 @@ class PortainerCoordinator(DataUpdateCoordinator[dict[int, PortainerCoordinatorD
|
||||
translation_placeholders={"error": repr(err)},
|
||||
) from err
|
||||
except PortainerConnectionError as err:
|
||||
raise ConfigEntryNotReady(
|
||||
raise UpdateFailed(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="cannot_connect",
|
||||
translation_placeholders={"error": repr(err)},
|
||||
) from err
|
||||
except PortainerTimeoutError as err:
|
||||
raise ConfigEntryNotReady(
|
||||
raise UpdateFailed(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="timeout_connect",
|
||||
translation_placeholders={"error": repr(err)},
|
||||
|
||||
@@ -8,33 +8,32 @@ from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .const import DOMAIN
|
||||
|
||||
PLATFORMS = [Platform.BINARY_SENSOR, Platform.SWITCH]
|
||||
|
||||
type ProgettiHWSWConfigEntry = ConfigEntry[ProgettiHWSWAPI]
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant, entry: ProgettiHWSWConfigEntry
|
||||
) -> bool:
|
||||
"""Set up ProgettiHWSW Automation from a config entry."""
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
hass.data[DOMAIN][entry.entry_id] = ProgettiHWSWAPI(
|
||||
f"{entry.data['host']}:{entry.data['port']}"
|
||||
)
|
||||
api = ProgettiHWSWAPI(f"{entry.data['host']}:{entry.data['port']}")
|
||||
|
||||
# Check board validation again to load new values to API.
|
||||
await hass.data[DOMAIN][entry.entry_id].check_board()
|
||||
await api.check_board()
|
||||
|
||||
entry.runtime_data = api
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(
|
||||
hass: HomeAssistant, entry: ProgettiHWSWConfigEntry
|
||||
) -> bool:
|
||||
"""Unload a config entry."""
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
if unload_ok:
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
|
||||
def setup_input(api: ProgettiHWSWAPI, input_number: int) -> Input:
|
||||
|
||||
@@ -7,7 +7,6 @@ import logging
|
||||
from ProgettiHWSW.input import Input
|
||||
|
||||
from homeassistant.components.binary_sensor import BinarySensorEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import (
|
||||
@@ -15,19 +14,19 @@ from homeassistant.helpers.update_coordinator import (
|
||||
DataUpdateCoordinator,
|
||||
)
|
||||
|
||||
from . import setup_input
|
||||
from .const import DEFAULT_POLLING_INTERVAL_SEC, DOMAIN
|
||||
from . import ProgettiHWSWConfigEntry, setup_input
|
||||
from .const import DEFAULT_POLLING_INTERVAL_SEC
|
||||
|
||||
_LOGGER = logging.getLogger(DOMAIN)
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: ProgettiHWSWConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the binary sensors from a config entry."""
|
||||
board_api = hass.data[DOMAIN][config_entry.entry_id]
|
||||
board_api = config_entry.runtime_data
|
||||
input_count = config_entry.data["input_count"]
|
||||
|
||||
async def async_update_data():
|
||||
|
||||
@@ -8,7 +8,6 @@ from typing import Any
|
||||
from ProgettiHWSW.relay import Relay
|
||||
|
||||
from homeassistant.components.switch import SwitchEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import (
|
||||
@@ -16,19 +15,19 @@ from homeassistant.helpers.update_coordinator import (
|
||||
DataUpdateCoordinator,
|
||||
)
|
||||
|
||||
from . import setup_switch
|
||||
from .const import DEFAULT_POLLING_INTERVAL_SEC, DOMAIN
|
||||
from . import ProgettiHWSWConfigEntry, setup_switch
|
||||
from .const import DEFAULT_POLLING_INTERVAL_SEC
|
||||
|
||||
_LOGGER = logging.getLogger(DOMAIN)
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: ProgettiHWSWConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the switches from a config entry."""
|
||||
board_api = hass.data[DOMAIN][config_entry.entry_id]
|
||||
board_api = config_entry.runtime_data
|
||||
relay_count = config_entry.data["relay_count"]
|
||||
|
||||
async def async_update_data():
|
||||
|
||||
@@ -10,25 +10,24 @@ from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
|
||||
from homeassistant.helpers import aiohttp_client
|
||||
|
||||
from .const import DOMAIN
|
||||
|
||||
PLATFORMS = [Platform.ALARM_CONTROL_PANEL, Platform.CAMERA]
|
||||
|
||||
type ProsegurConfigEntry = ConfigEntry[Auth]
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ProsegurConfigEntry) -> bool:
|
||||
"""Set up Prosegur Alarm from a config entry."""
|
||||
try:
|
||||
session = aiohttp_client.async_get_clientsession(hass)
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
hass.data[DOMAIN][entry.entry_id] = Auth(
|
||||
auth = Auth(
|
||||
session,
|
||||
entry.data[CONF_USERNAME],
|
||||
entry.data[CONF_PASSWORD],
|
||||
entry.data[CONF_COUNTRY],
|
||||
)
|
||||
await hass.data[DOMAIN][entry.entry_id].login()
|
||||
await auth.login()
|
||||
|
||||
except ConnectionRefusedError as error:
|
||||
_LOGGER.error("Configured credential are invalid, %s", error)
|
||||
@@ -39,15 +38,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
_LOGGER.error("Could not connect with Prosegur backend: %s", error)
|
||||
raise ConfigEntryNotReady from error
|
||||
|
||||
entry.runtime_data = auth
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ProsegurConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
if unload_ok:
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
@@ -12,12 +12,12 @@ from homeassistant.components.alarm_control_panel import (
|
||||
AlarmControlPanelEntityFeature,
|
||||
AlarmControlPanelState,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import DOMAIN
|
||||
from . import ProsegurConfigEntry
|
||||
from .const import DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -31,12 +31,12 @@ STATE_MAPPING = {
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: ProsegurConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the Prosegur alarm control panel platform."""
|
||||
async_add_entities(
|
||||
[ProsegurAlarm(entry.data["contract"], hass.data[DOMAIN][entry.entry_id])],
|
||||
[ProsegurAlarm(entry.data["contract"], entry.runtime_data)],
|
||||
update_before_add=True,
|
||||
)
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ from pyprosegur.exceptions import ProsegurException
|
||||
from pyprosegur.installation import Camera as InstallationCamera, Installation
|
||||
|
||||
from homeassistant.components.camera import Camera
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import (
|
||||
@@ -17,15 +16,15 @@ from homeassistant.helpers.entity_platform import (
|
||||
async_get_current_platform,
|
||||
)
|
||||
|
||||
from . import DOMAIN
|
||||
from .const import SERVICE_REQUEST_IMAGE
|
||||
from . import ProsegurConfigEntry
|
||||
from .const import DOMAIN, SERVICE_REQUEST_IMAGE
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: ProsegurConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the Prosegur camera platform."""
|
||||
@@ -38,12 +37,12 @@ async def async_setup_entry(
|
||||
)
|
||||
|
||||
_installation = await Installation.retrieve(
|
||||
hass.data[DOMAIN][entry.entry_id], entry.data["contract"]
|
||||
entry.runtime_data, entry.data["contract"]
|
||||
)
|
||||
|
||||
async_add_entities(
|
||||
[
|
||||
ProsegurCamera(_installation, camera, hass.data[DOMAIN][entry.entry_id])
|
||||
ProsegurCamera(_installation, camera, entry.runtime_data)
|
||||
for camera in _installation.cameras
|
||||
],
|
||||
update_before_add=True,
|
||||
|
||||
@@ -7,24 +7,24 @@ from typing import Any
|
||||
from pyprosegur.installation import Installation
|
||||
|
||||
from homeassistant.components.diagnostics import async_redact_data
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .const import CONF_CONTRACT, DOMAIN
|
||||
from . import ProsegurConfigEntry
|
||||
from .const import CONF_CONTRACT
|
||||
|
||||
TO_REDACT = {"description", "latitude", "longitude", "contractId", "address"}
|
||||
|
||||
|
||||
async def async_get_config_entry_diagnostics(
|
||||
hass: HomeAssistant, entry: ConfigEntry
|
||||
hass: HomeAssistant, entry: ProsegurConfigEntry
|
||||
) -> dict[str, Any]:
|
||||
"""Return diagnostics for a config entry."""
|
||||
|
||||
installation = await Installation.retrieve(
|
||||
hass.data[DOMAIN][entry.entry_id], entry.data[CONF_CONTRACT]
|
||||
entry.runtime_data, entry.data[CONF_CONTRACT]
|
||||
)
|
||||
|
||||
activity = await installation.activity(hass.data[DOMAIN][entry.entry_id])
|
||||
activity = await installation.activity(entry.runtime_data)
|
||||
|
||||
return {
|
||||
"installation": async_redact_data(installation.data, TO_REDACT),
|
||||
|
||||
@@ -24,6 +24,7 @@ from .coordinator import (
|
||||
InfoUpdateCoordinator,
|
||||
JobUpdateCoordinator,
|
||||
LegacyStatusCoordinator,
|
||||
PrusaLinkConfigEntry,
|
||||
PrusaLinkUpdateCoordinator,
|
||||
StatusCoordinator,
|
||||
)
|
||||
@@ -36,7 +37,7 @@ PLATFORMS: list[Platform] = [
|
||||
]
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: PrusaLinkConfigEntry) -> bool:
|
||||
"""Set up PrusaLink from a config entry."""
|
||||
if entry.version == 1 and entry.minor_version < 2:
|
||||
raise ConfigEntryError("Please upgrade your printer's firmware.")
|
||||
@@ -57,7 +58,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
for coordinator in coordinators.values():
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinators
|
||||
entry.runtime_data = coordinators
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
@@ -120,9 +121,6 @@ async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) ->
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: PrusaLinkConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
@@ -13,12 +13,10 @@ from homeassistant.components.binary_sensor import (
|
||||
BinarySensorEntity,
|
||||
BinarySensorEntityDescription,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import PrusaLinkUpdateCoordinator
|
||||
from .coordinator import PrusaLinkConfigEntry, PrusaLinkUpdateCoordinator
|
||||
from .entity import PrusaLinkEntity
|
||||
|
||||
T = TypeVar("T", PrinterStatus, LegacyPrinterStatus, JobInfo, PrinterInfo)
|
||||
@@ -56,13 +54,11 @@ BINARY_SENSORS: dict[str, tuple[PrusaLinkBinarySensorEntityDescription, ...]] =
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: PrusaLinkConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up PrusaLink sensor based on a config entry."""
|
||||
coordinators: dict[str, PrusaLinkUpdateCoordinator] = hass.data[DOMAIN][
|
||||
entry.entry_id
|
||||
]
|
||||
coordinators = entry.runtime_data
|
||||
|
||||
entities: list[PrusaLinkEntity] = []
|
||||
for coordinator_type, binary_sensors in BINARY_SENSORS.items():
|
||||
|
||||
@@ -10,13 +10,11 @@ from pyprusalink import JobInfo, LegacyPrinterStatus, PrinterStatus, PrusaLink
|
||||
from pyprusalink.types import Conflict, PrinterState
|
||||
|
||||
from homeassistant.components.button import ButtonEntity, ButtonEntityDescription
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import PrusaLinkUpdateCoordinator
|
||||
from .coordinator import PrusaLinkConfigEntry, PrusaLinkUpdateCoordinator
|
||||
from .entity import PrusaLinkEntity
|
||||
|
||||
T = TypeVar("T", PrinterStatus, LegacyPrinterStatus, JobInfo)
|
||||
@@ -71,13 +69,11 @@ BUTTONS: dict[str, tuple[PrusaLinkButtonEntityDescription, ...]] = {
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: PrusaLinkConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up PrusaLink buttons based on a config entry."""
|
||||
coordinators: dict[str, PrusaLinkUpdateCoordinator] = hass.data[DOMAIN][
|
||||
entry.entry_id
|
||||
]
|
||||
coordinators = entry.runtime_data
|
||||
|
||||
entities: list[PrusaLinkEntity] = []
|
||||
|
||||
@@ -124,9 +120,7 @@ class PrusaLinkButtonEntity(PrusaLinkEntity, ButtonEntity):
|
||||
"Action conflicts with current printer state"
|
||||
) from err
|
||||
|
||||
coordinators: dict[str, PrusaLinkUpdateCoordinator] = self.hass.data[DOMAIN][
|
||||
self.coordinator.config_entry.entry_id
|
||||
]
|
||||
coordinators = self.coordinator.config_entry.runtime_data
|
||||
|
||||
for coordinator in coordinators.values():
|
||||
coordinator.expect_change()
|
||||
|
||||
@@ -5,22 +5,20 @@ from __future__ import annotations
|
||||
from pyprusalink.types import PrinterState
|
||||
|
||||
from homeassistant.components.camera import Camera
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import JobUpdateCoordinator
|
||||
from .coordinator import PrusaLinkConfigEntry, PrusaLinkUpdateCoordinator
|
||||
from .entity import PrusaLinkEntity
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: PrusaLinkConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up PrusaLink camera."""
|
||||
coordinator: JobUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]["job"]
|
||||
coordinator = entry.runtime_data["job"]
|
||||
async_add_entities([PrusaLinkJobPreviewEntity(coordinator)])
|
||||
|
||||
|
||||
@@ -31,7 +29,7 @@ class PrusaLinkJobPreviewEntity(PrusaLinkEntity, Camera):
|
||||
last_image: bytes
|
||||
_attr_translation_key = "job_preview"
|
||||
|
||||
def __init__(self, coordinator: JobUpdateCoordinator) -> None:
|
||||
def __init__(self, coordinator: PrusaLinkUpdateCoordinator) -> None:
|
||||
"""Initialize a PrusaLink camera entity."""
|
||||
super().__init__(coordinator)
|
||||
Camera.__init__(self)
|
||||
|
||||
@@ -35,14 +35,17 @@ _MINIMUM_REFRESH_INTERVAL = 1.0
|
||||
T = TypeVar("T", PrinterStatus, LegacyPrinterStatus, JobInfo)
|
||||
|
||||
|
||||
type PrusaLinkConfigEntry = ConfigEntry[dict[str, PrusaLinkUpdateCoordinator]]
|
||||
|
||||
|
||||
class PrusaLinkUpdateCoordinator(DataUpdateCoordinator[T], ABC):
|
||||
"""Update coordinator for the printer."""
|
||||
|
||||
config_entry: ConfigEntry
|
||||
config_entry: PrusaLinkConfigEntry
|
||||
expect_change_until = 0.0
|
||||
|
||||
def __init__(
|
||||
self, hass: HomeAssistant, config_entry: ConfigEntry, api: PrusaLink
|
||||
self, hass: HomeAssistant, config_entry: PrusaLinkConfigEntry, api: PrusaLink
|
||||
) -> None:
|
||||
"""Initialize the update coordinator."""
|
||||
self.api = api
|
||||
|
||||
@@ -16,7 +16,6 @@ from homeassistant.components.sensor import (
|
||||
SensorEntityDescription,
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
PERCENTAGE,
|
||||
REVOLUTIONS_PER_MINUTE,
|
||||
@@ -29,8 +28,7 @@ from homeassistant.helpers.typing import StateType
|
||||
from homeassistant.util.dt import utcnow
|
||||
from homeassistant.util.variance import ignore_variance
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import PrusaLinkUpdateCoordinator
|
||||
from .coordinator import PrusaLinkConfigEntry, PrusaLinkUpdateCoordinator
|
||||
from .entity import PrusaLinkEntity
|
||||
|
||||
T = TypeVar("T", PrinterStatus, LegacyPrinterStatus, JobInfo, PrinterInfo)
|
||||
@@ -204,13 +202,11 @@ SENSORS: dict[str, tuple[PrusaLinkSensorEntityDescription, ...]] = {
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: PrusaLinkConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up PrusaLink sensor based on a config entry."""
|
||||
coordinators: dict[str, PrusaLinkUpdateCoordinator] = hass.data[DOMAIN][
|
||||
entry.entry_id
|
||||
]
|
||||
coordinators = entry.runtime_data
|
||||
|
||||
entities: list[PrusaLinkEntity] = []
|
||||
|
||||
|
||||
@@ -21,6 +21,8 @@ from homeassistant.helpers.typing import ConfigType
|
||||
from .api import PushBulletNotificationProvider
|
||||
from .const import DATA_HASS_CONFIG, DOMAIN
|
||||
|
||||
type PushbulletConfigEntry = ConfigEntry[PushBulletNotificationProvider]
|
||||
|
||||
PLATFORMS = [Platform.SENSOR]
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -35,7 +37,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: PushbulletConfigEntry) -> bool:
|
||||
"""Set up pushbullet from a config entry."""
|
||||
|
||||
try:
|
||||
@@ -49,7 +51,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
raise ConfigEntryNotReady from err
|
||||
|
||||
pb_provider = PushBulletNotificationProvider(hass, pushbullet)
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = pb_provider
|
||||
entry.runtime_data = pb_provider
|
||||
|
||||
def start_listener(event: Event) -> None:
|
||||
"""Start the listener thread."""
|
||||
@@ -72,11 +74,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: PushbulletConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||
pb_provider: PushBulletNotificationProvider = hass.data[DOMAIN].pop(
|
||||
entry.entry_id
|
||||
)
|
||||
await hass.async_add_executor_job(pb_provider.close)
|
||||
await hass.async_add_executor_job(entry.runtime_data.close)
|
||||
return unload_ok
|
||||
|
||||
@@ -22,8 +22,7 @@ from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
|
||||
from .api import PushBulletNotificationProvider
|
||||
from .const import ATTR_FILE, ATTR_FILE_URL, ATTR_URL, DOMAIN
|
||||
from .const import ATTR_FILE, ATTR_FILE_URL, ATTR_URL
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -36,10 +35,10 @@ async def async_get_service(
|
||||
"""Get the Pushbullet notification service."""
|
||||
if TYPE_CHECKING:
|
||||
assert discovery_info is not None
|
||||
pb_provider: PushBulletNotificationProvider = hass.data[DOMAIN][
|
||||
discovery_info["entry_id"]
|
||||
]
|
||||
return PushBulletNotificationService(hass, pb_provider.pushbullet)
|
||||
entry = hass.config_entries.async_get_entry(discovery_info["entry_id"])
|
||||
if TYPE_CHECKING:
|
||||
assert entry is not None
|
||||
return PushBulletNotificationService(hass, entry.runtime_data.pushbullet)
|
||||
|
||||
|
||||
class PushBulletNotificationService(BaseNotificationService):
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from homeassistant.components.sensor import SensorEntity, SensorEntityDescription
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_NAME, MAX_LENGTH_STATE_STATE
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import PushbulletConfigEntry
|
||||
from .api import PushBulletNotificationProvider
|
||||
from .const import DATA_UPDATED, DOMAIN
|
||||
|
||||
@@ -69,12 +69,12 @@ SENSOR_KEYS: list[str] = [desc.key for desc in SENSOR_TYPES]
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: PushbulletConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the Pushbullet sensors from config entry."""
|
||||
|
||||
pb_provider: PushBulletNotificationProvider = hass.data[DOMAIN][entry.entry_id]
|
||||
pb_provider = entry.runtime_data
|
||||
|
||||
entities = [
|
||||
PushBulletNotificationSensor(entry.data[CONF_NAME], pb_provider, description)
|
||||
|
||||
@@ -2,26 +2,23 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .const import DOMAIN, PLATFORMS
|
||||
from .coordinator import PVOutputDataUpdateCoordinator
|
||||
from .const import PLATFORMS
|
||||
from .coordinator import PvOutputConfigEntry, PVOutputDataUpdateCoordinator
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: PvOutputConfigEntry) -> bool:
|
||||
"""Set up PVOutput from a config entry."""
|
||||
coordinator = PVOutputDataUpdateCoordinator(hass, entry)
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator
|
||||
entry.runtime_data = coordinator
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: PvOutputConfigEntry) -> bool:
|
||||
"""Unload PVOutput config entry."""
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||
del hass.data[DOMAIN][entry.entry_id]
|
||||
return unload_ok
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
@@ -13,13 +13,15 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, Upda
|
||||
|
||||
from .const import CONF_SYSTEM_ID, DOMAIN, LOGGER, SCAN_INTERVAL
|
||||
|
||||
type PvOutputConfigEntry = ConfigEntry[PVOutputDataUpdateCoordinator]
|
||||
|
||||
|
||||
class PVOutputDataUpdateCoordinator(DataUpdateCoordinator[Status]):
|
||||
"""The PVOutput Data Update Coordinator."""
|
||||
|
||||
config_entry: ConfigEntry
|
||||
config_entry: PvOutputConfigEntry
|
||||
|
||||
def __init__(self, hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
def __init__(self, hass: HomeAssistant, entry: PvOutputConfigEntry) -> None:
|
||||
"""Initialize the PVOutput coordinator."""
|
||||
self.pvoutput = PVOutput(
|
||||
api_key=entry.data[CONF_API_KEY],
|
||||
|
||||
@@ -4,16 +4,13 @@ from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import PVOutputDataUpdateCoordinator
|
||||
from .coordinator import PvOutputConfigEntry
|
||||
|
||||
|
||||
async def async_get_config_entry_diagnostics(
|
||||
hass: HomeAssistant, entry: ConfigEntry
|
||||
hass: HomeAssistant, entry: PvOutputConfigEntry
|
||||
) -> dict[str, Any]:
|
||||
"""Return diagnostics for a config entry."""
|
||||
coordinator: PVOutputDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
return coordinator.data.to_dict()
|
||||
return entry.runtime_data.data.to_dict()
|
||||
|
||||
@@ -13,7 +13,6 @@ from homeassistant.components.sensor import (
|
||||
SensorEntityDescription,
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
UnitOfElectricPotential,
|
||||
UnitOfEnergy,
|
||||
@@ -26,7 +25,7 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import CONF_SYSTEM_ID, DOMAIN
|
||||
from .coordinator import PVOutputDataUpdateCoordinator
|
||||
from .coordinator import PvOutputConfigEntry, PVOutputDataUpdateCoordinator
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
@@ -97,11 +96,11 @@ SENSORS: tuple[PVOutputSensorEntityDescription, ...] = (
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: PvOutputConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up a PVOutput sensors based on a config entry."""
|
||||
coordinator: PVOutputDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
coordinator = entry.runtime_data
|
||||
system = await coordinator.pvoutput.system()
|
||||
|
||||
async_add_entities(
|
||||
|
||||
@@ -5,7 +5,7 @@ from typing import Any
|
||||
|
||||
from qbittorrentapi import APIConnectionError, Forbidden403Error, LoginFailed
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.const import (
|
||||
ATTR_DEVICE_ID,
|
||||
CONF_PASSWORD,
|
||||
@@ -27,7 +27,7 @@ from .const import (
|
||||
STATE_ATTR_TORRENTS,
|
||||
TORRENT_FILTER,
|
||||
)
|
||||
from .coordinator import QBittorrentDataCoordinator
|
||||
from .coordinator import QBittorrentConfigEntry, QBittorrentDataCoordinator
|
||||
from .helpers import format_torrents, setup_client
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -68,7 +68,16 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
translation_placeholders={"device_id": entry_id or ""},
|
||||
)
|
||||
|
||||
coordinator: QBittorrentDataCoordinator = hass.data[DOMAIN][entry_id]
|
||||
entry: QBittorrentConfigEntry | None = hass.config_entries.async_get_entry(
|
||||
entry_id
|
||||
)
|
||||
if entry is None or entry.state != ConfigEntryState.LOADED:
|
||||
raise ServiceValidationError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="invalid_entry_id",
|
||||
translation_placeholders={"device_id": entry_id},
|
||||
)
|
||||
coordinator = entry.runtime_data
|
||||
items = await coordinator.get_torrents(service_call.data[TORRENT_FILTER])
|
||||
info = format_torrents(items)
|
||||
return {
|
||||
@@ -87,10 +96,10 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
) -> dict[str, Any] | None:
|
||||
torrents = {}
|
||||
|
||||
for key, value in hass.data[DOMAIN].items():
|
||||
coordinator: QBittorrentDataCoordinator = value
|
||||
for entry in hass.config_entries.async_loaded_entries(DOMAIN):
|
||||
coordinator: QBittorrentDataCoordinator = entry.runtime_data
|
||||
items = await coordinator.get_torrents(service_call.data[TORRENT_FILTER])
|
||||
torrents[key] = format_torrents(items)
|
||||
torrents[entry.entry_id] = format_torrents(items)
|
||||
|
||||
return {
|
||||
STATE_ATTR_ALL_TORRENTS: torrents,
|
||||
@@ -106,7 +115,9 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant, config_entry: QBittorrentConfigEntry
|
||||
) -> bool:
|
||||
"""Set up qBittorrent from a config entry."""
|
||||
|
||||
try:
|
||||
@@ -127,19 +138,15 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
|
||||
coordinator = QBittorrentDataCoordinator(hass, config_entry, client)
|
||||
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
hass.data.setdefault(DOMAIN, {})[config_entry.entry_id] = coordinator
|
||||
config_entry.runtime_data = coordinator
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(
|
||||
hass: HomeAssistant, config_entry: QBittorrentConfigEntry
|
||||
) -> bool:
|
||||
"""Unload qBittorrent config entry."""
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(
|
||||
config_entry, PLATFORMS
|
||||
):
|
||||
del hass.data[DOMAIN][config_entry.entry_id]
|
||||
if not hass.data[DOMAIN]:
|
||||
del hass.data[DOMAIN]
|
||||
return unload_ok
|
||||
return await hass.config_entries.async_unload_platforms(config_entry, PLATFORMS)
|
||||
|
||||
@@ -24,14 +24,16 @@ from .const import DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
type QBittorrentConfigEntry = ConfigEntry[QBittorrentDataCoordinator]
|
||||
|
||||
|
||||
class QBittorrentDataCoordinator(DataUpdateCoordinator[SyncMainDataDictionary]):
|
||||
"""Coordinator for updating QBittorrent data."""
|
||||
|
||||
config_entry: ConfigEntry
|
||||
config_entry: QBittorrentConfigEntry
|
||||
|
||||
def __init__(
|
||||
self, hass: HomeAssistant, config_entry: ConfigEntry, client: Client
|
||||
self, hass: HomeAssistant, config_entry: QBittorrentConfigEntry, client: Client
|
||||
) -> None:
|
||||
"""Initialize coordinator."""
|
||||
self.client = client
|
||||
|
||||
@@ -13,7 +13,6 @@ from homeassistant.components.sensor import (
|
||||
SensorEntityDescription,
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import STATE_IDLE, UnitOfDataRate, UnitOfInformation
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
|
||||
@@ -22,7 +21,7 @@ from homeassistant.helpers.typing import StateType
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import DOMAIN, STATE_DOWNLOADING, STATE_SEEDING, STATE_UP_DOWN
|
||||
from .coordinator import QBittorrentDataCoordinator
|
||||
from .coordinator import QBittorrentConfigEntry, QBittorrentDataCoordinator
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -236,12 +235,12 @@ SENSOR_TYPES: tuple[QBittorrentSensorEntityDescription, ...] = (
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: QBittorrentConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up qBittorrent sensor entries."""
|
||||
|
||||
coordinator: QBittorrentDataCoordinator = hass.data[DOMAIN][config_entry.entry_id]
|
||||
coordinator = config_entry.runtime_data
|
||||
|
||||
async_add_entities(
|
||||
QBittorrentSensor(coordinator, config_entry, description)
|
||||
@@ -258,7 +257,7 @@ class QBittorrentSensor(CoordinatorEntity[QBittorrentDataCoordinator], SensorEnt
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: QBittorrentDataCoordinator,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: QBittorrentConfigEntry,
|
||||
entity_description: QBittorrentSensorEntityDescription,
|
||||
) -> None:
|
||||
"""Initialize the qBittorrent sensor."""
|
||||
|
||||
@@ -7,14 +7,13 @@ from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import QBittorrentDataCoordinator
|
||||
from .coordinator import QBittorrentConfigEntry, QBittorrentDataCoordinator
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
@@ -42,12 +41,12 @@ SWITCH_TYPES: tuple[QBittorrentSwitchEntityDescription, ...] = (
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: QBittorrentConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up qBittorrent switch entries."""
|
||||
|
||||
coordinator: QBittorrentDataCoordinator = hass.data[DOMAIN][config_entry.entry_id]
|
||||
coordinator = config_entry.runtime_data
|
||||
|
||||
async_add_entities(
|
||||
QBittorrentSwitch(coordinator, config_entry, description)
|
||||
@@ -64,7 +63,7 @@ class QBittorrentSwitch(CoordinatorEntity[QBittorrentDataCoordinator], SwitchEnt
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: QBittorrentDataCoordinator,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: QBittorrentConfigEntry,
|
||||
entity_description: QBittorrentSwitchEntityDescription,
|
||||
) -> None:
|
||||
"""Initialize qBittorrent switch."""
|
||||
|
||||
@@ -2,33 +2,27 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import QnapCoordinator
|
||||
from .coordinator import QnapConfigEntry, QnapCoordinator
|
||||
|
||||
PLATFORMS: list[Platform] = [
|
||||
Platform.SENSOR,
|
||||
]
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, config_entry: QnapConfigEntry) -> bool:
|
||||
"""Set the config entry up."""
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
coordinator = QnapCoordinator(hass, config_entry)
|
||||
# Fetch initial data so we have data when entities subscribe
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
hass.data[DOMAIN][config_entry.entry_id] = coordinator
|
||||
config_entry.runtime_data = coordinator
|
||||
await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS)
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(
|
||||
hass: HomeAssistant, config_entry: QnapConfigEntry
|
||||
) -> bool:
|
||||
"""Unload a config entry."""
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(
|
||||
config_entry, PLATFORMS
|
||||
):
|
||||
hass.data[DOMAIN].pop(config_entry.entry_id)
|
||||
return unload_ok
|
||||
return await hass.config_entries.async_unload_platforms(config_entry, PLATFORMS)
|
||||
|
||||
@@ -26,6 +26,8 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||
|
||||
from .const import DOMAIN
|
||||
|
||||
type QnapConfigEntry = ConfigEntry[QnapCoordinator]
|
||||
|
||||
UPDATE_INTERVAL = timedelta(minutes=1)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -46,7 +48,9 @@ def suppress_insecure_request_warning():
|
||||
class QnapCoordinator(DataUpdateCoordinator[dict[str, dict[str, Any]]]):
|
||||
"""Custom coordinator for the qnap integration."""
|
||||
|
||||
def __init__(self, hass: HomeAssistant, config_entry: ConfigEntry) -> None:
|
||||
config_entry: QnapConfigEntry
|
||||
|
||||
def __init__(self, hass: HomeAssistant, config_entry: QnapConfigEntry) -> None:
|
||||
"""Initialize the qnap coordinator."""
|
||||
super().__init__(
|
||||
hass,
|
||||
|
||||
@@ -5,7 +5,6 @@ from __future__ import annotations
|
||||
from datetime import timedelta
|
||||
from typing import Any
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components.sensor import (
|
||||
SensorDeviceClass,
|
||||
SensorEntity,
|
||||
@@ -20,14 +19,13 @@ from homeassistant.const import (
|
||||
UnitOfTemperature,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import PlatformNotReady
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import QnapCoordinator
|
||||
from .coordinator import QnapConfigEntry, QnapCoordinator
|
||||
|
||||
ATTR_DRIVE = "Drive"
|
||||
ATTR_IP = "IP Address"
|
||||
@@ -247,14 +245,11 @@ SENSOR_KEYS: list[str] = [
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: config_entries.ConfigEntry,
|
||||
config_entry: QnapConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up entry."""
|
||||
coordinator = QnapCoordinator(hass, config_entry)
|
||||
await coordinator.async_refresh()
|
||||
if not coordinator.last_update_success:
|
||||
raise PlatformNotReady
|
||||
coordinator = config_entry.runtime_data
|
||||
uid = config_entry.unique_id
|
||||
assert uid is not None
|
||||
sensors: list[QNAPSensor] = []
|
||||
|
||||
@@ -128,9 +128,8 @@ class RingCam(RingEntity[RingDoorBell], Camera):
|
||||
self._device = self._get_coordinator_data().get_video_device(
|
||||
self._device.device_api_id
|
||||
)
|
||||
|
||||
history_data = self._device.last_history
|
||||
if history_data and self._device.has_subscription:
|
||||
if history_data:
|
||||
self._last_event = history_data[0]
|
||||
# will call async_update to update the attributes and get the
|
||||
# video url from the api
|
||||
@@ -155,16 +154,13 @@ class RingCam(RingEntity[RingDoorBell], Camera):
|
||||
self, width: int | None = None, height: int | None = None
|
||||
) -> bytes | None:
|
||||
"""Return a still image response from the camera."""
|
||||
if self._video_url is None:
|
||||
if not self._device.has_subscription:
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="no_subscription",
|
||||
)
|
||||
return None
|
||||
# For live_view cameras, get a fresh snapshot
|
||||
if self.entity_description.key == "live_view":
|
||||
return await self._async_get_fresh_snapshot()
|
||||
|
||||
# For last_recording cameras, use the cached video frame
|
||||
key = (width, height)
|
||||
if not (image := self._images.get(key)):
|
||||
if not (image := self._images.get(key)) and self._video_url is not None:
|
||||
image = await ffmpeg.async_get_image(
|
||||
self.hass,
|
||||
self._video_url,
|
||||
@@ -177,6 +173,11 @@ class RingCam(RingEntity[RingDoorBell], Camera):
|
||||
|
||||
return image
|
||||
|
||||
@exception_wrap
|
||||
async def _async_get_fresh_snapshot(self) -> bytes | None:
|
||||
"""Get a fresh snapshot from the camera."""
|
||||
return await self._device.async_get_snapshot()
|
||||
|
||||
async def handle_async_mjpeg_stream(
|
||||
self, request: web.Request
|
||||
) -> web.StreamResponse | None:
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user