mirror of
https://github.com/home-assistant/core.git
synced 2025-07-24 21:57:51 +00:00
Merge branch 'dev' into zha_3phase_remaining_meetering
This commit is contained in:
commit
8ca9608a7e
6
.github/workflows/builder.yml
vendored
6
.github/workflows/builder.yml
vendored
@ -94,7 +94,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Download nightly wheels of frontend
|
- name: Download nightly wheels of frontend
|
||||||
if: needs.init.outputs.channel == 'dev'
|
if: needs.init.outputs.channel == 'dev'
|
||||||
uses: dawidd6/action-download-artifact@v7
|
uses: dawidd6/action-download-artifact@v8
|
||||||
with:
|
with:
|
||||||
github_token: ${{secrets.GITHUB_TOKEN}}
|
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||||
repo: home-assistant/frontend
|
repo: home-assistant/frontend
|
||||||
@ -105,7 +105,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Download nightly wheels of intents
|
- name: Download nightly wheels of intents
|
||||||
if: needs.init.outputs.channel == 'dev'
|
if: needs.init.outputs.channel == 'dev'
|
||||||
uses: dawidd6/action-download-artifact@v7
|
uses: dawidd6/action-download-artifact@v8
|
||||||
with:
|
with:
|
||||||
github_token: ${{secrets.GITHUB_TOKEN}}
|
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||||
repo: home-assistant/intents-package
|
repo: home-assistant/intents-package
|
||||||
@ -531,7 +531,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Generate artifact attestation
|
- name: Generate artifact attestation
|
||||||
if: needs.init.outputs.channel != 'dev' && needs.init.outputs.publish == 'true'
|
if: needs.init.outputs.channel != 'dev' && needs.init.outputs.publish == 'true'
|
||||||
uses: actions/attest-build-provenance@7668571508540a607bdfd90a87a560489fe372eb # v2.1.0
|
uses: actions/attest-build-provenance@520d128f165991a6c774bcb264f323e3d70747f4 # v2.2.0
|
||||||
with:
|
with:
|
||||||
subject-name: ${{ env.HASSFEST_IMAGE_NAME }}
|
subject-name: ${{ env.HASSFEST_IMAGE_NAME }}
|
||||||
subject-digest: ${{ steps.push.outputs.digest }}
|
subject-digest: ${{ steps.push.outputs.digest }}
|
||||||
|
4
.github/workflows/ci.yaml
vendored
4
.github/workflows/ci.yaml
vendored
@ -1273,7 +1273,7 @@ jobs:
|
|||||||
pattern: coverage-*
|
pattern: coverage-*
|
||||||
- name: Upload coverage to Codecov
|
- name: Upload coverage to Codecov
|
||||||
if: needs.info.outputs.test_full_suite == 'true'
|
if: needs.info.outputs.test_full_suite == 'true'
|
||||||
uses: codecov/codecov-action@v5.1.2
|
uses: codecov/codecov-action@v5.3.0
|
||||||
with:
|
with:
|
||||||
fail_ci_if_error: true
|
fail_ci_if_error: true
|
||||||
flags: full-suite
|
flags: full-suite
|
||||||
@ -1411,7 +1411,7 @@ jobs:
|
|||||||
pattern: coverage-*
|
pattern: coverage-*
|
||||||
- name: Upload coverage to Codecov
|
- name: Upload coverage to Codecov
|
||||||
if: needs.info.outputs.test_full_suite == 'false'
|
if: needs.info.outputs.test_full_suite == 'false'
|
||||||
uses: codecov/codecov-action@v5.1.2
|
uses: codecov/codecov-action@v5.3.0
|
||||||
with:
|
with:
|
||||||
fail_ci_if_error: true
|
fail_ci_if_error: true
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
4
.github/workflows/codeql.yml
vendored
4
.github/workflows/codeql.yml
vendored
@ -24,11 +24,11 @@ jobs:
|
|||||||
uses: actions/checkout@v4.2.2
|
uses: actions/checkout@v4.2.2
|
||||||
|
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v3.28.1
|
uses: github/codeql-action/init@v3.28.4
|
||||||
with:
|
with:
|
||||||
languages: python
|
languages: python
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@v3.28.1
|
uses: github/codeql-action/analyze@v3.28.4
|
||||||
with:
|
with:
|
||||||
category: "/language:python"
|
category: "/language:python"
|
||||||
|
@ -237,6 +237,7 @@ homeassistant.components.homeassistant_green.*
|
|||||||
homeassistant.components.homeassistant_hardware.*
|
homeassistant.components.homeassistant_hardware.*
|
||||||
homeassistant.components.homeassistant_sky_connect.*
|
homeassistant.components.homeassistant_sky_connect.*
|
||||||
homeassistant.components.homeassistant_yellow.*
|
homeassistant.components.homeassistant_yellow.*
|
||||||
|
homeassistant.components.homee.*
|
||||||
homeassistant.components.homekit.*
|
homeassistant.components.homekit.*
|
||||||
homeassistant.components.homekit_controller
|
homeassistant.components.homekit_controller
|
||||||
homeassistant.components.homekit_controller.alarm_control_panel
|
homeassistant.components.homekit_controller.alarm_control_panel
|
||||||
@ -262,6 +263,7 @@ homeassistant.components.image_processing.*
|
|||||||
homeassistant.components.image_upload.*
|
homeassistant.components.image_upload.*
|
||||||
homeassistant.components.imap.*
|
homeassistant.components.imap.*
|
||||||
homeassistant.components.imgw_pib.*
|
homeassistant.components.imgw_pib.*
|
||||||
|
homeassistant.components.incomfort.*
|
||||||
homeassistant.components.input_button.*
|
homeassistant.components.input_button.*
|
||||||
homeassistant.components.input_select.*
|
homeassistant.components.input_select.*
|
||||||
homeassistant.components.input_text.*
|
homeassistant.components.input_text.*
|
||||||
@ -307,6 +309,7 @@ homeassistant.components.logbook.*
|
|||||||
homeassistant.components.logger.*
|
homeassistant.components.logger.*
|
||||||
homeassistant.components.london_underground.*
|
homeassistant.components.london_underground.*
|
||||||
homeassistant.components.lookin.*
|
homeassistant.components.lookin.*
|
||||||
|
homeassistant.components.lovelace.*
|
||||||
homeassistant.components.luftdaten.*
|
homeassistant.components.luftdaten.*
|
||||||
homeassistant.components.madvr.*
|
homeassistant.components.madvr.*
|
||||||
homeassistant.components.manual.*
|
homeassistant.components.manual.*
|
||||||
|
6
CODEOWNERS
generated
6
CODEOWNERS
generated
@ -682,8 +682,6 @@ build.json @home-assistant/supervisor
|
|||||||
/homeassistant/components/iammeter/ @lewei50
|
/homeassistant/components/iammeter/ @lewei50
|
||||||
/homeassistant/components/iaqualink/ @flz
|
/homeassistant/components/iaqualink/ @flz
|
||||||
/tests/components/iaqualink/ @flz
|
/tests/components/iaqualink/ @flz
|
||||||
/homeassistant/components/ibeacon/ @bdraco
|
|
||||||
/tests/components/ibeacon/ @bdraco
|
|
||||||
/homeassistant/components/icloud/ @Quentame @nzapponi
|
/homeassistant/components/icloud/ @Quentame @nzapponi
|
||||||
/tests/components/icloud/ @Quentame @nzapponi
|
/tests/components/icloud/ @Quentame @nzapponi
|
||||||
/homeassistant/components/idasen_desk/ @abmantis
|
/homeassistant/components/idasen_desk/ @abmantis
|
||||||
@ -1410,8 +1408,8 @@ build.json @home-assistant/supervisor
|
|||||||
/homeassistant/components/solaredge_local/ @drobtravels @scheric
|
/homeassistant/components/solaredge_local/ @drobtravels @scheric
|
||||||
/homeassistant/components/solarlog/ @Ernst79 @dontinelli
|
/homeassistant/components/solarlog/ @Ernst79 @dontinelli
|
||||||
/tests/components/solarlog/ @Ernst79 @dontinelli
|
/tests/components/solarlog/ @Ernst79 @dontinelli
|
||||||
/homeassistant/components/solax/ @squishykid
|
/homeassistant/components/solax/ @squishykid @Darsstar
|
||||||
/tests/components/solax/ @squishykid
|
/tests/components/solax/ @squishykid @Darsstar
|
||||||
/homeassistant/components/soma/ @ratsept @sebfortier2288
|
/homeassistant/components/soma/ @ratsept @sebfortier2288
|
||||||
/tests/components/soma/ @ratsept @sebfortier2288
|
/tests/components/soma/ @ratsept @sebfortier2288
|
||||||
/homeassistant/components/sonarr/ @ctalkington
|
/homeassistant/components/sonarr/ @ctalkington
|
||||||
|
@ -112,6 +112,11 @@ with contextlib.suppress(ImportError):
|
|||||||
# Ensure anyio backend is imported to avoid it being imported in the event loop
|
# Ensure anyio backend is imported to avoid it being imported in the event loop
|
||||||
from anyio._backends import _asyncio # noqa: F401
|
from anyio._backends import _asyncio # noqa: F401
|
||||||
|
|
||||||
|
with contextlib.suppress(ImportError):
|
||||||
|
# httpx will import trio if it is installed which does
|
||||||
|
# blocking I/O in the event loop. We want to avoid that.
|
||||||
|
import trio # noqa: F401
|
||||||
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .runner import RuntimeConfig
|
from .runner import RuntimeConfig
|
||||||
|
@ -26,5 +26,5 @@
|
|||||||
"iot_class": "local_push",
|
"iot_class": "local_push",
|
||||||
"loggers": ["aioacaia"],
|
"loggers": ["aioacaia"],
|
||||||
"quality_scale": "platinum",
|
"quality_scale": "platinum",
|
||||||
"requirements": ["aioacaia==0.1.13"]
|
"requirements": ["aioacaia==0.1.14"]
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,9 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|||||||
from . import AirGradientConfigEntry
|
from . import AirGradientConfigEntry
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
from .coordinator import AirGradientCoordinator
|
from .coordinator import AirGradientCoordinator
|
||||||
from .entity import AirGradientEntity
|
from .entity import AirGradientEntity, exception_handler
|
||||||
|
|
||||||
|
PARALLEL_UPDATES = 1
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True, kw_only=True)
|
@dataclass(frozen=True, kw_only=True)
|
||||||
@ -100,6 +102,7 @@ class AirGradientButton(AirGradientEntity, ButtonEntity):
|
|||||||
self.entity_description = description
|
self.entity_description = description
|
||||||
self._attr_unique_id = f"{coordinator.serial_number}-{description.key}"
|
self._attr_unique_id = f"{coordinator.serial_number}-{description.key}"
|
||||||
|
|
||||||
|
@exception_handler
|
||||||
async def async_press(self) -> None:
|
async def async_press(self) -> None:
|
||||||
"""Press the button."""
|
"""Press the button."""
|
||||||
await self.entity_description.press_fn(self.coordinator.client)
|
await self.entity_description.press_fn(self.coordinator.client)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
"""Config flow for Airgradient."""
|
"""Config flow for Airgradient."""
|
||||||
|
|
||||||
|
from collections.abc import Mapping
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from airgradient import (
|
from airgradient import (
|
||||||
@ -11,7 +12,12 @@ from airgradient import (
|
|||||||
from awesomeversion import AwesomeVersion
|
from awesomeversion import AwesomeVersion
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
|
from homeassistant.config_entries import (
|
||||||
|
SOURCE_RECONFIGURE,
|
||||||
|
SOURCE_USER,
|
||||||
|
ConfigFlow,
|
||||||
|
ConfigFlowResult,
|
||||||
|
)
|
||||||
from homeassistant.const import CONF_HOST, CONF_MODEL
|
from homeassistant.const import CONF_HOST, CONF_MODEL
|
||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
from homeassistant.helpers.service_info.zeroconf import ZeroconfServiceInfo
|
from homeassistant.helpers.service_info.zeroconf import ZeroconfServiceInfo
|
||||||
@ -95,10 +101,18 @@ class AirGradientConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
await self.async_set_unique_id(
|
await self.async_set_unique_id(
|
||||||
current_measures.serial_number, raise_on_progress=False
|
current_measures.serial_number, raise_on_progress=False
|
||||||
)
|
)
|
||||||
self._abort_if_unique_id_configured()
|
if self.source == SOURCE_USER:
|
||||||
|
self._abort_if_unique_id_configured()
|
||||||
|
if self.source == SOURCE_RECONFIGURE:
|
||||||
|
self._abort_if_unique_id_mismatch()
|
||||||
await self.set_configuration_source()
|
await self.set_configuration_source()
|
||||||
return self.async_create_entry(
|
if self.source == SOURCE_USER:
|
||||||
title=current_measures.model,
|
return self.async_create_entry(
|
||||||
|
title=current_measures.model,
|
||||||
|
data={CONF_HOST: user_input[CONF_HOST]},
|
||||||
|
)
|
||||||
|
return self.async_update_reload_and_abort(
|
||||||
|
self._get_reconfigure_entry(),
|
||||||
data={CONF_HOST: user_input[CONF_HOST]},
|
data={CONF_HOST: user_input[CONF_HOST]},
|
||||||
)
|
)
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
@ -106,3 +120,9 @@ class AirGradientConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
data_schema=vol.Schema({vol.Required(CONF_HOST): str}),
|
data_schema=vol.Schema({vol.Required(CONF_HOST): str}),
|
||||||
errors=errors,
|
errors=errors,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def async_step_reconfigure(
|
||||||
|
self, user_input: Mapping[str, Any]
|
||||||
|
) -> ConfigFlowResult:
|
||||||
|
"""Handle reconfiguration."""
|
||||||
|
return await self.async_step_user()
|
||||||
|
@ -55,7 +55,11 @@ class AirGradientCoordinator(DataUpdateCoordinator[AirGradientData]):
|
|||||||
measures = await self.client.get_current_measures()
|
measures = await self.client.get_current_measures()
|
||||||
config = await self.client.get_config()
|
config = await self.client.get_config()
|
||||||
except AirGradientError as error:
|
except AirGradientError as error:
|
||||||
raise UpdateFailed(error) from error
|
raise UpdateFailed(
|
||||||
|
translation_domain=DOMAIN,
|
||||||
|
translation_key="update_error",
|
||||||
|
translation_placeholders={"error": str(error)},
|
||||||
|
) from error
|
||||||
if measures.firmware_version != self._current_version:
|
if measures.firmware_version != self._current_version:
|
||||||
device_registry = dr.async_get(self.hass)
|
device_registry = dr.async_get(self.hass)
|
||||||
device_entry = device_registry.async_get_device(
|
device_entry = device_registry.async_get_device(
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
"""Base class for AirGradient entities."""
|
"""Base class for AirGradient entities."""
|
||||||
|
|
||||||
from airgradient import get_model_name
|
from collections.abc import Callable, Coroutine
|
||||||
|
from typing import Any, Concatenate
|
||||||
|
|
||||||
|
from airgradient import AirGradientConnectionError, AirGradientError, get_model_name
|
||||||
|
|
||||||
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.helpers.device_registry import DeviceInfo
|
from homeassistant.helpers.device_registry import DeviceInfo
|
||||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||||
|
|
||||||
@ -26,3 +30,31 @@ class AirGradientEntity(CoordinatorEntity[AirGradientCoordinator]):
|
|||||||
serial_number=coordinator.serial_number,
|
serial_number=coordinator.serial_number,
|
||||||
sw_version=measures.firmware_version,
|
sw_version=measures.firmware_version,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def exception_handler[_EntityT: AirGradientEntity, **_P](
|
||||||
|
func: Callable[Concatenate[_EntityT, _P], Coroutine[Any, Any, Any]],
|
||||||
|
) -> Callable[Concatenate[_EntityT, _P], Coroutine[Any, Any, None]]:
|
||||||
|
"""Decorate AirGradient calls to handle exceptions.
|
||||||
|
|
||||||
|
A decorator that wraps the passed in function, catches AirGradient errors.
|
||||||
|
"""
|
||||||
|
|
||||||
|
async def handler(self: _EntityT, *args: _P.args, **kwargs: _P.kwargs) -> None:
|
||||||
|
try:
|
||||||
|
await func(self, *args, **kwargs)
|
||||||
|
except AirGradientConnectionError as error:
|
||||||
|
raise HomeAssistantError(
|
||||||
|
translation_domain=DOMAIN,
|
||||||
|
translation_key="communication_error",
|
||||||
|
translation_placeholders={"error": str(error)},
|
||||||
|
) from error
|
||||||
|
|
||||||
|
except AirGradientError as error:
|
||||||
|
raise HomeAssistantError(
|
||||||
|
translation_domain=DOMAIN,
|
||||||
|
translation_key="unknown_error",
|
||||||
|
translation_placeholders={"error": str(error)},
|
||||||
|
) from error
|
||||||
|
|
||||||
|
return handler
|
||||||
|
@ -19,7 +19,9 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|||||||
from . import AirGradientConfigEntry
|
from . import AirGradientConfigEntry
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
from .coordinator import AirGradientCoordinator
|
from .coordinator import AirGradientCoordinator
|
||||||
from .entity import AirGradientEntity
|
from .entity import AirGradientEntity, exception_handler
|
||||||
|
|
||||||
|
PARALLEL_UPDATES = 1
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True, kw_only=True)
|
@dataclass(frozen=True, kw_only=True)
|
||||||
@ -121,6 +123,7 @@ class AirGradientNumber(AirGradientEntity, NumberEntity):
|
|||||||
"""Return the state of the number."""
|
"""Return the state of the number."""
|
||||||
return self.entity_description.value_fn(self.coordinator.data.config)
|
return self.entity_description.value_fn(self.coordinator.data.config)
|
||||||
|
|
||||||
|
@exception_handler
|
||||||
async def async_set_native_value(self, value: float) -> None:
|
async def async_set_native_value(self, value: float) -> None:
|
||||||
"""Set the selected value."""
|
"""Set the selected value."""
|
||||||
await self.entity_description.set_value_fn(self.coordinator.client, int(value))
|
await self.entity_description.set_value_fn(self.coordinator.client, int(value))
|
||||||
|
@ -29,7 +29,7 @@ rules:
|
|||||||
unique-config-entry: done
|
unique-config-entry: done
|
||||||
|
|
||||||
# Silver
|
# Silver
|
||||||
action-exceptions: todo
|
action-exceptions: done
|
||||||
config-entry-unloading: done
|
config-entry-unloading: done
|
||||||
docs-configuration-parameters:
|
docs-configuration-parameters:
|
||||||
status: exempt
|
status: exempt
|
||||||
@ -38,7 +38,7 @@ rules:
|
|||||||
entity-unavailable: done
|
entity-unavailable: done
|
||||||
integration-owner: done
|
integration-owner: done
|
||||||
log-when-unavailable: done
|
log-when-unavailable: done
|
||||||
parallel-updates: todo
|
parallel-updates: done
|
||||||
reauthentication-flow:
|
reauthentication-flow:
|
||||||
status: exempt
|
status: exempt
|
||||||
comment: |
|
comment: |
|
||||||
@ -68,9 +68,9 @@ rules:
|
|||||||
entity-device-class: done
|
entity-device-class: done
|
||||||
entity-disabled-by-default: done
|
entity-disabled-by-default: done
|
||||||
entity-translations: done
|
entity-translations: done
|
||||||
exception-translations: todo
|
exception-translations: done
|
||||||
icon-translations: done
|
icon-translations: done
|
||||||
reconfiguration-flow: todo
|
reconfiguration-flow: done
|
||||||
repair-issues:
|
repair-issues:
|
||||||
status: exempt
|
status: exempt
|
||||||
comment: |
|
comment: |
|
||||||
|
@ -19,7 +19,9 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|||||||
from . import AirGradientConfigEntry
|
from . import AirGradientConfigEntry
|
||||||
from .const import DOMAIN, PM_STANDARD, PM_STANDARD_REVERSE
|
from .const import DOMAIN, PM_STANDARD, PM_STANDARD_REVERSE
|
||||||
from .coordinator import AirGradientCoordinator
|
from .coordinator import AirGradientCoordinator
|
||||||
from .entity import AirGradientEntity
|
from .entity import AirGradientEntity, exception_handler
|
||||||
|
|
||||||
|
PARALLEL_UPDATES = 1
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True, kw_only=True)
|
@dataclass(frozen=True, kw_only=True)
|
||||||
@ -216,6 +218,7 @@ class AirGradientSelect(AirGradientEntity, SelectEntity):
|
|||||||
"""Return the state of the select."""
|
"""Return the state of the select."""
|
||||||
return self.entity_description.value_fn(self.coordinator.data.config)
|
return self.entity_description.value_fn(self.coordinator.data.config)
|
||||||
|
|
||||||
|
@exception_handler
|
||||||
async def async_select_option(self, option: str) -> None:
|
async def async_select_option(self, option: str) -> None:
|
||||||
"""Change the selected option."""
|
"""Change the selected option."""
|
||||||
await self.entity_description.set_value_fn(self.coordinator.client, option)
|
await self.entity_description.set_value_fn(self.coordinator.client, option)
|
||||||
|
@ -35,6 +35,8 @@ from .const import PM_STANDARD, PM_STANDARD_REVERSE
|
|||||||
from .coordinator import AirGradientCoordinator
|
from .coordinator import AirGradientCoordinator
|
||||||
from .entity import AirGradientEntity
|
from .entity import AirGradientEntity
|
||||||
|
|
||||||
|
PARALLEL_UPDATES = 0
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True, kw_only=True)
|
@dataclass(frozen=True, kw_only=True)
|
||||||
class AirGradientMeasurementSensorEntityDescription(SensorEntityDescription):
|
class AirGradientMeasurementSensorEntityDescription(SensorEntityDescription):
|
||||||
|
@ -17,7 +17,9 @@
|
|||||||
"abort": {
|
"abort": {
|
||||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
||||||
"already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]",
|
"already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]",
|
||||||
"invalid_version": "This firmware version is unsupported. Please upgrade the firmware of the device to at least version 3.1.1."
|
"invalid_version": "This firmware version is unsupported. Please upgrade the firmware of the device to at least version 3.1.1.",
|
||||||
|
"reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]",
|
||||||
|
"unique_id_mismatch": "Please ensure you reconfigure against the same device."
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||||
@ -165,5 +167,16 @@
|
|||||||
"name": "Post data to Airgradient"
|
"name": "Post data to Airgradient"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"exceptions": {
|
||||||
|
"communication_error": {
|
||||||
|
"message": "An error occurred while communicating with the Airgradient device: {error}"
|
||||||
|
},
|
||||||
|
"unknown_error": {
|
||||||
|
"message": "An unknown error occurred while communicating with the Airgradient device: {error}"
|
||||||
|
},
|
||||||
|
"update_error": {
|
||||||
|
"message": "An error occurred while communicating with the Airgradient device: {error}"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,9 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|||||||
from . import AirGradientConfigEntry
|
from . import AirGradientConfigEntry
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
from .coordinator import AirGradientCoordinator
|
from .coordinator import AirGradientCoordinator
|
||||||
from .entity import AirGradientEntity
|
from .entity import AirGradientEntity, exception_handler
|
||||||
|
|
||||||
|
PARALLEL_UPDATES = 1
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True, kw_only=True)
|
@dataclass(frozen=True, kw_only=True)
|
||||||
@ -99,11 +101,13 @@ class AirGradientSwitch(AirGradientEntity, SwitchEntity):
|
|||||||
"""Return the state of the switch."""
|
"""Return the state of the switch."""
|
||||||
return self.entity_description.value_fn(self.coordinator.data.config)
|
return self.entity_description.value_fn(self.coordinator.data.config)
|
||||||
|
|
||||||
|
@exception_handler
|
||||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||||
"""Turn the switch on."""
|
"""Turn the switch on."""
|
||||||
await self.entity_description.set_value_fn(self.coordinator.client, True)
|
await self.entity_description.set_value_fn(self.coordinator.client, True)
|
||||||
await self.coordinator.async_request_refresh()
|
await self.coordinator.async_request_refresh()
|
||||||
|
|
||||||
|
@exception_handler
|
||||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||||
"""Turn the switch off."""
|
"""Turn the switch off."""
|
||||||
await self.entity_description.set_value_fn(self.coordinator.client, False)
|
await self.entity_description.set_value_fn(self.coordinator.client, False)
|
||||||
|
@ -11,6 +11,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|||||||
from . import AirGradientConfigEntry, AirGradientCoordinator
|
from . import AirGradientConfigEntry, AirGradientCoordinator
|
||||||
from .entity import AirGradientEntity
|
from .entity import AirGradientEntity
|
||||||
|
|
||||||
|
PARALLEL_UPDATES = 1
|
||||||
SCAN_INTERVAL = timedelta(hours=1)
|
SCAN_INTERVAL = timedelta(hours=1)
|
||||||
|
|
||||||
|
|
||||||
|
@ -21,7 +21,6 @@ from .const import (
|
|||||||
ATTR_API_CAT_DESCRIPTION,
|
ATTR_API_CAT_DESCRIPTION,
|
||||||
ATTR_API_CAT_LEVEL,
|
ATTR_API_CAT_LEVEL,
|
||||||
ATTR_API_CATEGORY,
|
ATTR_API_CATEGORY,
|
||||||
ATTR_API_PM25,
|
|
||||||
ATTR_API_POLLUTANT,
|
ATTR_API_POLLUTANT,
|
||||||
ATTR_API_REPORT_DATE,
|
ATTR_API_REPORT_DATE,
|
||||||
ATTR_API_REPORT_HOUR,
|
ATTR_API_REPORT_HOUR,
|
||||||
@ -91,18 +90,16 @@ class AirNowDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
|||||||
max_aqi_desc = obv[ATTR_API_CATEGORY][ATTR_API_CAT_DESCRIPTION]
|
max_aqi_desc = obv[ATTR_API_CATEGORY][ATTR_API_CAT_DESCRIPTION]
|
||||||
max_aqi_poll = pollutant
|
max_aqi_poll = pollutant
|
||||||
|
|
||||||
# Copy other data from PM2.5 Value
|
# Copy Report Details
|
||||||
if obv[ATTR_API_AQI_PARAM] == ATTR_API_PM25:
|
data[ATTR_API_REPORT_DATE] = obv[ATTR_API_REPORT_DATE]
|
||||||
# Copy Report Details
|
data[ATTR_API_REPORT_HOUR] = obv[ATTR_API_REPORT_HOUR]
|
||||||
data[ATTR_API_REPORT_DATE] = obv[ATTR_API_REPORT_DATE]
|
data[ATTR_API_REPORT_TZ] = obv[ATTR_API_REPORT_TZ]
|
||||||
data[ATTR_API_REPORT_HOUR] = obv[ATTR_API_REPORT_HOUR]
|
|
||||||
data[ATTR_API_REPORT_TZ] = obv[ATTR_API_REPORT_TZ]
|
|
||||||
|
|
||||||
# Copy Station Details
|
# Copy Station Details
|
||||||
data[ATTR_API_STATE] = obv[ATTR_API_STATE]
|
data[ATTR_API_STATE] = obv[ATTR_API_STATE]
|
||||||
data[ATTR_API_STATION] = obv[ATTR_API_STATION]
|
data[ATTR_API_STATION] = obv[ATTR_API_STATION]
|
||||||
data[ATTR_API_STATION_LATITUDE] = obv[ATTR_API_STATION_LATITUDE]
|
data[ATTR_API_STATION_LATITUDE] = obv[ATTR_API_STATION_LATITUDE]
|
||||||
data[ATTR_API_STATION_LONGITUDE] = obv[ATTR_API_STATION_LONGITUDE]
|
data[ATTR_API_STATION_LONGITUDE] = obv[ATTR_API_STATION_LONGITUDE]
|
||||||
|
|
||||||
# Store Overall AQI
|
# Store Overall AQI
|
||||||
data[ATTR_API_AQI] = max_aqi
|
data[ATTR_API_AQI] = max_aqi
|
||||||
|
@ -86,7 +86,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: AirzoneConfigEntry) -> b
|
|||||||
options = ConnectionOptions(
|
options = ConnectionOptions(
|
||||||
entry.data[CONF_HOST],
|
entry.data[CONF_HOST],
|
||||||
entry.data[CONF_PORT],
|
entry.data[CONF_PORT],
|
||||||
entry.data.get(CONF_ID, DEFAULT_SYSTEM_ID),
|
entry.data[CONF_ID],
|
||||||
)
|
)
|
||||||
|
|
||||||
airzone = AirzoneLocalApi(aiohttp_client.async_get_clientsession(hass), options)
|
airzone = AirzoneLocalApi(aiohttp_client.async_get_clientsession(hass), options)
|
||||||
@ -120,3 +120,25 @@ async def async_setup_entry(hass: HomeAssistant, entry: AirzoneConfigEntry) -> b
|
|||||||
async def async_unload_entry(hass: HomeAssistant, entry: AirzoneConfigEntry) -> bool:
|
async def async_unload_entry(hass: HomeAssistant, entry: AirzoneConfigEntry) -> bool:
|
||||||
"""Unload a config entry."""
|
"""Unload a config entry."""
|
||||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||||
|
|
||||||
|
|
||||||
|
async def async_migrate_entry(hass: HomeAssistant, entry: AirzoneConfigEntry) -> bool:
|
||||||
|
"""Migrate an old entry."""
|
||||||
|
if entry.version == 1 and entry.minor_version < 2:
|
||||||
|
# Add missing CONF_ID
|
||||||
|
system_id = entry.data.get(CONF_ID, DEFAULT_SYSTEM_ID)
|
||||||
|
new_data = entry.data.copy()
|
||||||
|
new_data[CONF_ID] = system_id
|
||||||
|
hass.config_entries.async_update_entry(
|
||||||
|
entry,
|
||||||
|
data=new_data,
|
||||||
|
minor_version=2,
|
||||||
|
)
|
||||||
|
|
||||||
|
_LOGGER.info(
|
||||||
|
"Migration to configuration version %s.%s successful",
|
||||||
|
entry.version,
|
||||||
|
entry.minor_version,
|
||||||
|
)
|
||||||
|
|
||||||
|
return True
|
||||||
|
@ -44,6 +44,7 @@ class AirZoneConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
|
|
||||||
_discovered_ip: str | None = None
|
_discovered_ip: str | None = None
|
||||||
_discovered_mac: str | None = None
|
_discovered_mac: str | None = None
|
||||||
|
MINOR_VERSION = 2
|
||||||
|
|
||||||
async def async_step_user(
|
async def async_step_user(
|
||||||
self, user_input: dict[str, Any] | None = None
|
self, user_input: dict[str, Any] | None = None
|
||||||
@ -53,6 +54,9 @@ class AirZoneConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
errors = {}
|
errors = {}
|
||||||
|
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
|
if CONF_ID not in user_input:
|
||||||
|
user_input[CONF_ID] = DEFAULT_SYSTEM_ID
|
||||||
|
|
||||||
self._async_abort_entries_match(user_input)
|
self._async_abort_entries_match(user_input)
|
||||||
|
|
||||||
airzone = AirzoneLocalApi(
|
airzone = AirzoneLocalApi(
|
||||||
@ -60,7 +64,7 @@ class AirZoneConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
ConnectionOptions(
|
ConnectionOptions(
|
||||||
user_input[CONF_HOST],
|
user_input[CONF_HOST],
|
||||||
user_input[CONF_PORT],
|
user_input[CONF_PORT],
|
||||||
user_input.get(CONF_ID, DEFAULT_SYSTEM_ID),
|
user_input[CONF_ID],
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -84,6 +88,9 @@ class AirZoneConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
)
|
)
|
||||||
|
|
||||||
title = f"Airzone {user_input[CONF_HOST]}:{user_input[CONF_PORT]}"
|
title = f"Airzone {user_input[CONF_HOST]}:{user_input[CONF_PORT]}"
|
||||||
|
if user_input[CONF_ID] != DEFAULT_SYSTEM_ID:
|
||||||
|
title += f" #{user_input[CONF_ID]}"
|
||||||
|
|
||||||
return self.async_create_entry(title=title, data=user_input)
|
return self.async_create_entry(title=title, data=user_input)
|
||||||
|
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
|
@ -8,5 +8,5 @@
|
|||||||
"documentation": "https://www.home-assistant.io/integrations/anthropic",
|
"documentation": "https://www.home-assistant.io/integrations/anthropic",
|
||||||
"integration_type": "service",
|
"integration_type": "service",
|
||||||
"iot_class": "cloud_polling",
|
"iot_class": "cloud_polling",
|
||||||
"requirements": ["anthropic==0.31.2"]
|
"requirements": ["anthropic==0.44.0"]
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,10 @@ class APCUPSdData(dict[str, str]):
|
|||||||
@property
|
@property
|
||||||
def serial_no(self) -> str | None:
|
def serial_no(self) -> str | None:
|
||||||
"""Return the unique serial number of the UPS, if available."""
|
"""Return the unique serial number of the UPS, if available."""
|
||||||
return self.get("SERIALNO")
|
sn = self.get("SERIALNO")
|
||||||
|
# We had user reports that some UPS models simply return "Blank" as serial number, in
|
||||||
|
# which case we fall back to `None` to indicate that it is actually not available.
|
||||||
|
return None if sn == "Blank" else sn
|
||||||
|
|
||||||
|
|
||||||
class APCUPSdCoordinator(DataUpdateCoordinator[APCUPSdData]):
|
class APCUPSdCoordinator(DataUpdateCoordinator[APCUPSdData]):
|
||||||
|
@ -320,6 +320,7 @@ class BackupSchedule:
|
|||||||
time: dt.time | None = None
|
time: dt.time | None = None
|
||||||
cron_event: CronSim | None = field(init=False, default=None)
|
cron_event: CronSim | None = field(init=False, default=None)
|
||||||
next_automatic_backup: datetime | None = field(init=False, default=None)
|
next_automatic_backup: datetime | None = field(init=False, default=None)
|
||||||
|
next_automatic_backup_additional = False
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def apply(
|
def apply(
|
||||||
@ -378,6 +379,14 @@ class BackupSchedule:
|
|||||||
# add a day to the next time to avoid scheduling at the same time again
|
# add a day to the next time to avoid scheduling at the same time again
|
||||||
self.cron_event = CronSim(cron_pattern, now + timedelta(days=1))
|
self.cron_event = CronSim(cron_pattern, now + timedelta(days=1))
|
||||||
|
|
||||||
|
# Compare the computed next time with the next time from the cron pattern
|
||||||
|
# to determine if an additional backup has been scheduled
|
||||||
|
cron_event_configured = CronSim(cron_pattern, now)
|
||||||
|
next_configured_time = next(cron_event_configured)
|
||||||
|
self.next_automatic_backup_additional = next_time < next_configured_time
|
||||||
|
else:
|
||||||
|
self.next_automatic_backup_additional = False
|
||||||
|
|
||||||
async def _create_backup(now: datetime) -> None:
|
async def _create_backup(now: datetime) -> None:
|
||||||
"""Create backup."""
|
"""Create backup."""
|
||||||
manager.remove_next_backup_event = None
|
manager.remove_next_backup_event = None
|
||||||
|
@ -61,6 +61,7 @@ async def handle_info(
|
|||||||
"last_attempted_automatic_backup": manager.config.data.last_attempted_automatic_backup,
|
"last_attempted_automatic_backup": manager.config.data.last_attempted_automatic_backup,
|
||||||
"last_completed_automatic_backup": manager.config.data.last_completed_automatic_backup,
|
"last_completed_automatic_backup": manager.config.data.last_completed_automatic_backup,
|
||||||
"next_automatic_backup": manager.config.data.schedule.next_automatic_backup,
|
"next_automatic_backup": manager.config.data.schedule.next_automatic_backup,
|
||||||
|
"next_automatic_backup_additional": manager.config.data.schedule.next_automatic_backup_additional,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -329,7 +330,8 @@ async def handle_config_info(
|
|||||||
{
|
{
|
||||||
"config": config
|
"config": config
|
||||||
| {
|
| {
|
||||||
"next_automatic_backup": manager.config.data.schedule.next_automatic_backup
|
"next_automatic_backup": manager.config.data.schedule.next_automatic_backup,
|
||||||
|
"next_automatic_backup_additional": manager.config.data.schedule.next_automatic_backup_additional,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -16,11 +16,11 @@
|
|||||||
"quality_scale": "internal",
|
"quality_scale": "internal",
|
||||||
"requirements": [
|
"requirements": [
|
||||||
"bleak==0.22.3",
|
"bleak==0.22.3",
|
||||||
"bleak-retry-connector==3.7.0",
|
"bleak-retry-connector==3.8.0",
|
||||||
"bluetooth-adapters==0.21.0",
|
"bluetooth-adapters==0.21.1",
|
||||||
"bluetooth-auto-recovery==1.4.2",
|
"bluetooth-auto-recovery==1.4.2",
|
||||||
"bluetooth-data-tools==1.22.0",
|
"bluetooth-data-tools==1.22.0",
|
||||||
"dbus-fast==2.30.2",
|
"dbus-fast==2.30.2",
|
||||||
"habluetooth==3.9.2"
|
"habluetooth==3.12.0"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -12,13 +12,7 @@ from homeassistant.core import HomeAssistant
|
|||||||
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
|
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
|
||||||
from homeassistant.helpers import device_registry as dr
|
from homeassistant.helpers import device_registry as dr
|
||||||
|
|
||||||
from .const import (
|
from .const import CONF_SSL_CERTIFICATE, CONF_SSL_KEY, DOMAIN
|
||||||
CONF_SSL_CERTIFICATE,
|
|
||||||
CONF_SSL_KEY,
|
|
||||||
DATA_POLLING_HANDLER,
|
|
||||||
DATA_SESSION,
|
|
||||||
DOMAIN,
|
|
||||||
)
|
|
||||||
|
|
||||||
PLATFORMS = [
|
PLATFORMS = [
|
||||||
Platform.BINARY_SENSOR,
|
Platform.BINARY_SENSOR,
|
||||||
@ -30,7 +24,10 @@ PLATFORMS = [
|
|||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
type BoschConfigEntry = ConfigEntry[SHCSession]
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_entry(hass: HomeAssistant, entry: BoschConfigEntry) -> bool:
|
||||||
"""Set up Bosch SHC from a config entry."""
|
"""Set up Bosch SHC from a config entry."""
|
||||||
data = entry.data
|
data = entry.data
|
||||||
|
|
||||||
@ -53,10 +50,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
if shc_info.updateState.name == "UPDATE_AVAILABLE":
|
if shc_info.updateState.name == "UPDATE_AVAILABLE":
|
||||||
_LOGGER.warning("Please check for software updates in the Bosch Smart Home App")
|
_LOGGER.warning("Please check for software updates in the Bosch Smart Home App")
|
||||||
|
|
||||||
hass.data.setdefault(DOMAIN, {})
|
entry.runtime_data = session
|
||||||
hass.data[DOMAIN][entry.entry_id] = {
|
|
||||||
DATA_SESSION: session,
|
|
||||||
}
|
|
||||||
|
|
||||||
device_registry = dr.async_get(hass)
|
device_registry = dr.async_get(hass)
|
||||||
device_registry.async_get_or_create(
|
device_registry.async_get_or_create(
|
||||||
@ -76,23 +70,15 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
await hass.async_add_executor_job(session.stop_polling)
|
await hass.async_add_executor_job(session.stop_polling)
|
||||||
|
|
||||||
await hass.async_add_executor_job(session.start_polling)
|
await hass.async_add_executor_job(session.start_polling)
|
||||||
hass.data[DOMAIN][entry.entry_id][DATA_POLLING_HANDLER] = (
|
entry.async_on_unload(
|
||||||
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, stop_polling)
|
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, stop_polling)
|
||||||
)
|
)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_unload_entry(hass: HomeAssistant, entry: BoschConfigEntry) -> bool:
|
||||||
"""Unload a config entry."""
|
"""Unload a config entry."""
|
||||||
session: SHCSession = hass.data[DOMAIN][entry.entry_id][DATA_SESSION]
|
await hass.async_add_executor_job(entry.runtime_data.stop_polling)
|
||||||
|
|
||||||
hass.data[DOMAIN][entry.entry_id][DATA_POLLING_HANDLER]()
|
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||||
hass.data[DOMAIN][entry.entry_id].pop(DATA_POLLING_HANDLER)
|
|
||||||
await hass.async_add_executor_job(session.stop_polling)
|
|
||||||
|
|
||||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
|
||||||
if unload_ok:
|
|
||||||
hass.data[DOMAIN].pop(entry.entry_id)
|
|
||||||
|
|
||||||
return unload_ok
|
|
||||||
|
@ -2,28 +2,27 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from boschshcpy import SHCBatteryDevice, SHCSession, SHCShutterContact
|
from boschshcpy import SHCBatteryDevice, SHCShutterContact
|
||||||
from boschshcpy.device import SHCDevice
|
from boschshcpy.device import SHCDevice
|
||||||
|
|
||||||
from homeassistant.components.binary_sensor import (
|
from homeassistant.components.binary_sensor import (
|
||||||
BinarySensorDeviceClass,
|
BinarySensorDeviceClass,
|
||||||
BinarySensorEntity,
|
BinarySensorEntity,
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
from .const import DATA_SESSION, DOMAIN
|
from . import BoschConfigEntry
|
||||||
from .entity import SHCEntity
|
from .entity import SHCEntity
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
config_entry: ConfigEntry,
|
config_entry: BoschConfigEntry,
|
||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the SHC binary sensor platform."""
|
"""Set up the SHC binary sensor platform."""
|
||||||
session: SHCSession = hass.data[DOMAIN][config_entry.entry_id][DATA_SESSION]
|
session = config_entry.runtime_data
|
||||||
|
|
||||||
entities: list[BinarySensorEntity] = [
|
entities: list[BinarySensorEntity] = [
|
||||||
ShutterContactSensor(
|
ShutterContactSensor(
|
||||||
|
@ -6,7 +6,4 @@ CONF_SHC_KEY = "bosch_shc-key.pem"
|
|||||||
CONF_SSL_CERTIFICATE = "ssl_certificate"
|
CONF_SSL_CERTIFICATE = "ssl_certificate"
|
||||||
CONF_SSL_KEY = "ssl_key"
|
CONF_SSL_KEY = "ssl_key"
|
||||||
|
|
||||||
DATA_SESSION = "session"
|
|
||||||
DATA_POLLING_HANDLER = "polling_handler"
|
|
||||||
|
|
||||||
DOMAIN = "bosch_shc"
|
DOMAIN = "bosch_shc"
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from boschshcpy import SHCSession, SHCShutterControl
|
from boschshcpy import SHCShutterControl
|
||||||
|
|
||||||
from homeassistant.components.cover import (
|
from homeassistant.components.cover import (
|
||||||
ATTR_POSITION,
|
ATTR_POSITION,
|
||||||
@ -10,22 +10,20 @@ from homeassistant.components.cover import (
|
|||||||
CoverEntity,
|
CoverEntity,
|
||||||
CoverEntityFeature,
|
CoverEntityFeature,
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
from .const import DATA_SESSION, DOMAIN
|
from . import BoschConfigEntry
|
||||||
from .entity import SHCEntity
|
from .entity import SHCEntity
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
config_entry: ConfigEntry,
|
config_entry: BoschConfigEntry,
|
||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the SHC cover platform."""
|
"""Set up the SHC cover platform."""
|
||||||
|
session = config_entry.runtime_data
|
||||||
session: SHCSession = hass.data[DOMAIN][config_entry.entry_id][DATA_SESSION]
|
|
||||||
|
|
||||||
async_add_entities(
|
async_add_entities(
|
||||||
ShutterControlCover(
|
ShutterControlCover(
|
||||||
|
@ -6,7 +6,6 @@ from collections.abc import Callable
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from boschshcpy import SHCSession
|
|
||||||
from boschshcpy.device import SHCDevice
|
from boschshcpy.device import SHCDevice
|
||||||
|
|
||||||
from homeassistant.components.sensor import (
|
from homeassistant.components.sensor import (
|
||||||
@ -15,7 +14,6 @@ from homeassistant.components.sensor import (
|
|||||||
SensorEntityDescription,
|
SensorEntityDescription,
|
||||||
SensorStateClass,
|
SensorStateClass,
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONCENTRATION_PARTS_PER_MILLION,
|
CONCENTRATION_PARTS_PER_MILLION,
|
||||||
PERCENTAGE,
|
PERCENTAGE,
|
||||||
@ -27,7 +25,7 @@ from homeassistant.core import HomeAssistant
|
|||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.typing import StateType
|
from homeassistant.helpers.typing import StateType
|
||||||
|
|
||||||
from .const import DATA_SESSION, DOMAIN
|
from . import BoschConfigEntry
|
||||||
from .entity import SHCEntity
|
from .entity import SHCEntity
|
||||||
|
|
||||||
|
|
||||||
@ -127,11 +125,11 @@ SENSOR_DESCRIPTIONS: dict[str, SHCSensorEntityDescription] = {
|
|||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
config_entry: ConfigEntry,
|
config_entry: BoschConfigEntry,
|
||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the SHC sensor platform."""
|
"""Set up the SHC sensor platform."""
|
||||||
session: SHCSession = hass.data[DOMAIN][config_entry.entry_id][DATA_SESSION]
|
session = config_entry.runtime_data
|
||||||
|
|
||||||
entities: list[SensorEntity] = [
|
entities: list[SensorEntity] = [
|
||||||
SHCSensor(
|
SHCSensor(
|
||||||
|
@ -9,7 +9,6 @@ from boschshcpy import (
|
|||||||
SHCCamera360,
|
SHCCamera360,
|
||||||
SHCCameraEyes,
|
SHCCameraEyes,
|
||||||
SHCLightSwitch,
|
SHCLightSwitch,
|
||||||
SHCSession,
|
|
||||||
SHCSmartPlug,
|
SHCSmartPlug,
|
||||||
SHCSmartPlugCompact,
|
SHCSmartPlugCompact,
|
||||||
)
|
)
|
||||||
@ -20,13 +19,12 @@ from homeassistant.components.switch import (
|
|||||||
SwitchEntity,
|
SwitchEntity,
|
||||||
SwitchEntityDescription,
|
SwitchEntityDescription,
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.const import EntityCategory
|
from homeassistant.const import EntityCategory
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.typing import StateType
|
from homeassistant.helpers.typing import StateType
|
||||||
|
|
||||||
from .const import DATA_SESSION, DOMAIN
|
from . import BoschConfigEntry
|
||||||
from .entity import SHCEntity
|
from .entity import SHCEntity
|
||||||
|
|
||||||
|
|
||||||
@ -80,11 +78,11 @@ SWITCH_TYPES: dict[str, SHCSwitchEntityDescription] = {
|
|||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
config_entry: ConfigEntry,
|
config_entry: BoschConfigEntry,
|
||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the SHC switch platform."""
|
"""Set up the SHC switch platform."""
|
||||||
session: SHCSession = hass.data[DOMAIN][config_entry.entry_id][DATA_SESSION]
|
session = config_entry.runtime_data
|
||||||
|
|
||||||
entities: list[SwitchEntity] = [
|
entities: list[SwitchEntity] = [
|
||||||
SHCSwitch(
|
SHCSwitch(
|
||||||
|
@ -11,7 +11,7 @@ from requests.exceptions import ConnectTimeout, HTTPError
|
|||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.camera import DOMAIN as CAMERA_DOMAIN
|
from homeassistant.components.camera import DOMAIN as CAMERA_DOMAIN
|
||||||
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
|
from homeassistant.config_entries import SOURCE_IMPORT
|
||||||
from homeassistant.const import CONF_PASSWORD, CONF_TIMEOUT, CONF_USERNAME, Platform
|
from homeassistant.const import CONF_PASSWORD, CONF_TIMEOUT, CONF_USERNAME, Platform
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.exceptions import ConfigEntryNotReady
|
from homeassistant.exceptions import ConfigEntryNotReady
|
||||||
@ -20,13 +20,11 @@ from homeassistant.helpers.typing import ConfigType
|
|||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
CONF_FFMPEG_ARGUMENTS,
|
CONF_FFMPEG_ARGUMENTS,
|
||||||
DATA_COORDINATOR,
|
|
||||||
DATA_UNDO_UPDATE_LISTENER,
|
|
||||||
DEFAULT_FFMPEG_ARGUMENTS,
|
DEFAULT_FFMPEG_ARGUMENTS,
|
||||||
DEFAULT_TIMEOUT,
|
DEFAULT_TIMEOUT,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
)
|
)
|
||||||
from .coordinator import CanaryDataUpdateCoordinator
|
from .coordinator import CanaryConfigEntry, CanaryDataUpdateCoordinator
|
||||||
|
|
||||||
_LOGGER: Final = logging.getLogger(__name__)
|
_LOGGER: Final = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -59,8 +57,6 @@ PLATFORMS: Final[list[Platform]] = [
|
|||||||
|
|
||||||
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||||
"""Set up the Canary integration."""
|
"""Set up the Canary integration."""
|
||||||
hass.data.setdefault(DOMAIN, {})
|
|
||||||
|
|
||||||
if hass.config_entries.async_entries(DOMAIN):
|
if hass.config_entries.async_entries(DOMAIN):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ -90,7 +86,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_setup_entry(hass: HomeAssistant, entry: CanaryConfigEntry) -> bool:
|
||||||
"""Set up Canary from a config entry."""
|
"""Set up Canary from a config entry."""
|
||||||
if not entry.options:
|
if not entry.options:
|
||||||
options = {
|
options = {
|
||||||
@ -107,38 +103,29 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
_LOGGER.error("Unable to connect to Canary service: %s", str(error))
|
_LOGGER.error("Unable to connect to Canary service: %s", str(error))
|
||||||
raise ConfigEntryNotReady from error
|
raise ConfigEntryNotReady from error
|
||||||
|
|
||||||
coordinator = CanaryDataUpdateCoordinator(hass, api=canary_api)
|
coordinator = CanaryDataUpdateCoordinator(hass, entry, api=canary_api)
|
||||||
await coordinator.async_config_entry_first_refresh()
|
await coordinator.async_config_entry_first_refresh()
|
||||||
|
|
||||||
undo_listener = entry.add_update_listener(_async_update_listener)
|
entry.async_on_unload(entry.add_update_listener(_async_update_listener))
|
||||||
|
|
||||||
hass.data[DOMAIN][entry.entry_id] = {
|
entry.runtime_data = coordinator
|
||||||
DATA_COORDINATOR: coordinator,
|
|
||||||
DATA_UNDO_UPDATE_LISTENER: undo_listener,
|
|
||||||
}
|
|
||||||
|
|
||||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_unload_entry(hass: HomeAssistant, entry: CanaryConfigEntry) -> bool:
|
||||||
"""Unload a config entry."""
|
"""Unload a config entry."""
|
||||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||||
|
|
||||||
if unload_ok:
|
|
||||||
hass.data[DOMAIN][entry.entry_id][DATA_UNDO_UPDATE_LISTENER]()
|
|
||||||
hass.data[DOMAIN].pop(entry.entry_id)
|
|
||||||
|
|
||||||
return unload_ok
|
|
||||||
|
|
||||||
|
|
||||||
async def _async_update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
async def _async_update_listener(hass: HomeAssistant, entry: CanaryConfigEntry) -> None:
|
||||||
"""Handle options update."""
|
"""Handle options update."""
|
||||||
await hass.config_entries.async_reload(entry.entry_id)
|
await hass.config_entries.async_reload(entry.entry_id)
|
||||||
|
|
||||||
|
|
||||||
def _get_canary_api_instance(entry: ConfigEntry) -> Api:
|
def _get_canary_api_instance(entry: CanaryConfigEntry) -> Api:
|
||||||
"""Initialize a new instance of CanaryApi."""
|
"""Initialize a new instance of CanaryApi."""
|
||||||
return Api(
|
return Api(
|
||||||
entry.data[CONF_USERNAME],
|
entry.data[CONF_USERNAME],
|
||||||
|
@ -12,24 +12,20 @@ from homeassistant.components.alarm_control_panel import (
|
|||||||
AlarmControlPanelEntityFeature,
|
AlarmControlPanelEntityFeature,
|
||||||
AlarmControlPanelState,
|
AlarmControlPanelState,
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||||
|
|
||||||
from .const import DATA_COORDINATOR, DOMAIN
|
from .coordinator import CanaryConfigEntry, CanaryDataUpdateCoordinator
|
||||||
from .coordinator import CanaryDataUpdateCoordinator
|
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
entry: ConfigEntry,
|
entry: CanaryConfigEntry,
|
||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up Canary alarm control panels based on a config entry."""
|
"""Set up Canary alarm control panels based on a config entry."""
|
||||||
coordinator: CanaryDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id][
|
coordinator = entry.runtime_data
|
||||||
DATA_COORDINATOR
|
|
||||||
]
|
|
||||||
alarms = [
|
alarms = [
|
||||||
CanaryAlarm(coordinator, location)
|
CanaryAlarm(coordinator, location)
|
||||||
for location_id, location in coordinator.data["locations"].items()
|
for location_id, location in coordinator.data["locations"].items()
|
||||||
|
@ -18,7 +18,6 @@ from homeassistant.components.camera import (
|
|||||||
Camera,
|
Camera,
|
||||||
)
|
)
|
||||||
from homeassistant.components.ffmpeg import FFmpegManager, get_ffmpeg_manager
|
from homeassistant.components.ffmpeg import FFmpegManager, get_ffmpeg_manager
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers import config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
from homeassistant.helpers.aiohttp_client import async_aiohttp_proxy_stream
|
from homeassistant.helpers.aiohttp_client import async_aiohttp_proxy_stream
|
||||||
@ -27,14 +26,8 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|||||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||||
from homeassistant.util import dt as dt_util
|
from homeassistant.util import dt as dt_util
|
||||||
|
|
||||||
from .const import (
|
from .const import CONF_FFMPEG_ARGUMENTS, DEFAULT_FFMPEG_ARGUMENTS, DOMAIN, MANUFACTURER
|
||||||
CONF_FFMPEG_ARGUMENTS,
|
from .coordinator import CanaryConfigEntry, CanaryDataUpdateCoordinator
|
||||||
DATA_COORDINATOR,
|
|
||||||
DEFAULT_FFMPEG_ARGUMENTS,
|
|
||||||
DOMAIN,
|
|
||||||
MANUFACTURER,
|
|
||||||
)
|
|
||||||
from .coordinator import CanaryDataUpdateCoordinator
|
|
||||||
|
|
||||||
FORCE_CAMERA_REFRESH_INTERVAL: Final = timedelta(minutes=15)
|
FORCE_CAMERA_REFRESH_INTERVAL: Final = timedelta(minutes=15)
|
||||||
|
|
||||||
@ -54,13 +47,11 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
entry: ConfigEntry,
|
entry: CanaryConfigEntry,
|
||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up Canary sensors based on a config entry."""
|
"""Set up Canary sensors based on a config entry."""
|
||||||
coordinator: CanaryDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id][
|
coordinator = entry.runtime_data
|
||||||
DATA_COORDINATOR
|
|
||||||
]
|
|
||||||
ffmpeg_arguments: str = entry.options.get(
|
ffmpeg_arguments: str = entry.options.get(
|
||||||
CONF_FFMPEG_ARGUMENTS, DEFAULT_FFMPEG_ARGUMENTS
|
CONF_FFMPEG_ARGUMENTS, DEFAULT_FFMPEG_ARGUMENTS
|
||||||
)
|
)
|
||||||
|
@ -9,10 +9,6 @@ MANUFACTURER: Final = "Canary Connect, Inc"
|
|||||||
# Configuration
|
# Configuration
|
||||||
CONF_FFMPEG_ARGUMENTS: Final = "ffmpeg_arguments"
|
CONF_FFMPEG_ARGUMENTS: Final = "ffmpeg_arguments"
|
||||||
|
|
||||||
# Data
|
|
||||||
DATA_COORDINATOR: Final = "coordinator"
|
|
||||||
DATA_UNDO_UPDATE_LISTENER: Final = "undo_update_listener"
|
|
||||||
|
|
||||||
# Defaults
|
# Defaults
|
||||||
DEFAULT_FFMPEG_ARGUMENTS: Final = "-pred 1"
|
DEFAULT_FFMPEG_ARGUMENTS: Final = "-pred 1"
|
||||||
DEFAULT_TIMEOUT: Final = 10
|
DEFAULT_TIMEOUT: Final = 10
|
||||||
|
@ -11,6 +11,7 @@ from canary.api import Api
|
|||||||
from canary.model import Location, Reading
|
from canary.model import Location, Reading
|
||||||
from requests.exceptions import ConnectTimeout, HTTPError
|
from requests.exceptions import ConnectTimeout, HTTPError
|
||||||
|
|
||||||
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||||
|
|
||||||
@ -20,10 +21,15 @@ from .model import CanaryData
|
|||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
type CanaryConfigEntry = ConfigEntry[CanaryDataUpdateCoordinator]
|
||||||
|
|
||||||
|
|
||||||
class CanaryDataUpdateCoordinator(DataUpdateCoordinator[CanaryData]):
|
class CanaryDataUpdateCoordinator(DataUpdateCoordinator[CanaryData]):
|
||||||
"""Class to manage fetching Canary data."""
|
"""Class to manage fetching Canary data."""
|
||||||
|
|
||||||
def __init__(self, hass: HomeAssistant, *, api: Api) -> None:
|
def __init__(
|
||||||
|
self, hass: HomeAssistant, config_entry: CanaryConfigEntry, *, api: Api
|
||||||
|
) -> None:
|
||||||
"""Initialize global Canary data updater."""
|
"""Initialize global Canary data updater."""
|
||||||
self.canary = api
|
self.canary = api
|
||||||
update_interval = timedelta(seconds=30)
|
update_interval = timedelta(seconds=30)
|
||||||
@ -31,6 +37,7 @@ class CanaryDataUpdateCoordinator(DataUpdateCoordinator[CanaryData]):
|
|||||||
super().__init__(
|
super().__init__(
|
||||||
hass,
|
hass,
|
||||||
_LOGGER,
|
_LOGGER,
|
||||||
|
config_entry=config_entry,
|
||||||
name=DOMAIN,
|
name=DOMAIN,
|
||||||
update_interval=update_interval,
|
update_interval=update_interval,
|
||||||
)
|
)
|
||||||
|
@ -7,7 +7,6 @@ from typing import Final
|
|||||||
from canary.model import Device, Location, SensorType
|
from canary.model import Device, Location, SensorType
|
||||||
|
|
||||||
from homeassistant.components.sensor import SensorDeviceClass, SensorEntity
|
from homeassistant.components.sensor import SensorDeviceClass, SensorEntity
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
PERCENTAGE,
|
PERCENTAGE,
|
||||||
SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
|
SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
|
||||||
@ -18,8 +17,8 @@ from homeassistant.helpers.device_registry import DeviceInfo
|
|||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||||
|
|
||||||
from .const import DATA_COORDINATOR, DOMAIN, MANUFACTURER
|
from .const import DOMAIN, MANUFACTURER
|
||||||
from .coordinator import CanaryDataUpdateCoordinator
|
from .coordinator import CanaryConfigEntry, CanaryDataUpdateCoordinator
|
||||||
|
|
||||||
type SensorTypeItem = tuple[
|
type SensorTypeItem = tuple[
|
||||||
str, str | None, str | None, SensorDeviceClass | None, list[str]
|
str, str | None, str | None, SensorDeviceClass | None, list[str]
|
||||||
@ -64,13 +63,11 @@ STATE_AIR_QUALITY_VERY_ABNORMAL: Final = "very_abnormal"
|
|||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
entry: ConfigEntry,
|
entry: CanaryConfigEntry,
|
||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up Canary sensors based on a config entry."""
|
"""Set up Canary sensors based on a config entry."""
|
||||||
coordinator: CanaryDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id][
|
coordinator = entry.runtime_data
|
||||||
DATA_COORDINATOR
|
|
||||||
]
|
|
||||||
sensors: list[CanarySensor] = []
|
sensors: list[CanarySensor] = []
|
||||||
|
|
||||||
for location in coordinator.data["locations"].values():
|
for location in coordinator.data["locations"].values():
|
||||||
|
@ -2,34 +2,30 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.const import CONF_HOST, CONF_PORT, Platform
|
from homeassistant.const import CONF_HOST, CONF_PORT, Platform
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .coordinator import CCM15ConfigEntry, CCM15Coordinator
|
||||||
from .coordinator import CCM15Coordinator
|
|
||||||
|
|
||||||
PLATFORMS: list[Platform] = [Platform.CLIMATE]
|
PLATFORMS: list[Platform] = [Platform.CLIMATE]
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_setup_entry(hass: HomeAssistant, entry: CCM15ConfigEntry) -> bool:
|
||||||
"""Set up Midea ccm15 AC Controller from a config entry."""
|
"""Set up Midea ccm15 AC Controller from a config entry."""
|
||||||
|
|
||||||
coordinator = CCM15Coordinator(
|
coordinator = CCM15Coordinator(
|
||||||
hass,
|
hass,
|
||||||
|
entry,
|
||||||
entry.data[CONF_HOST],
|
entry.data[CONF_HOST],
|
||||||
entry.data[CONF_PORT],
|
entry.data[CONF_PORT],
|
||||||
)
|
)
|
||||||
await coordinator.async_config_entry_first_refresh()
|
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)
|
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_unload_entry(hass: HomeAssistant, entry: CCM15ConfigEntry) -> bool:
|
||||||
"""Unload a config entry."""
|
"""Unload a config entry."""
|
||||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||||
hass.data[DOMAIN].pop(entry.entry_id)
|
|
||||||
|
|
||||||
return unload_ok
|
|
||||||
|
@ -17,7 +17,6 @@ from homeassistant.components.climate import (
|
|||||||
ClimateEntityFeature,
|
ClimateEntityFeature,
|
||||||
HVACMode,
|
HVACMode,
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature
|
from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.device_registry import DeviceInfo
|
from homeassistant.helpers.device_registry import DeviceInfo
|
||||||
@ -25,18 +24,18 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|||||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||||
|
|
||||||
from .const import CONST_CMD_FAN_MAP, CONST_CMD_STATE_MAP, DOMAIN
|
from .const import CONST_CMD_FAN_MAP, CONST_CMD_STATE_MAP, DOMAIN
|
||||||
from .coordinator import CCM15Coordinator
|
from .coordinator import CCM15ConfigEntry, CCM15Coordinator
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
config_entry: ConfigEntry,
|
config_entry: CCM15ConfigEntry,
|
||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up all climate."""
|
"""Set up all climate."""
|
||||||
coordinator: CCM15Coordinator = hass.data[DOMAIN][config_entry.entry_id]
|
coordinator = config_entry.runtime_data
|
||||||
|
|
||||||
ac_data: CCM15DeviceState = coordinator.data
|
ac_data: CCM15DeviceState = coordinator.data
|
||||||
entities = [
|
entities = [
|
||||||
|
@ -7,6 +7,7 @@ from ccm15 import CCM15Device, CCM15DeviceState, CCM15SlaveDevice
|
|||||||
import httpx
|
import httpx
|
||||||
|
|
||||||
from homeassistant.components.climate import HVACMode
|
from homeassistant.components.climate import HVACMode
|
||||||
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||||
|
|
||||||
@ -19,15 +20,20 @@ from .const import (
|
|||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
type CCM15ConfigEntry = ConfigEntry[CCM15Coordinator]
|
||||||
|
|
||||||
|
|
||||||
class CCM15Coordinator(DataUpdateCoordinator[CCM15DeviceState]):
|
class CCM15Coordinator(DataUpdateCoordinator[CCM15DeviceState]):
|
||||||
"""Class to coordinate multiple CCM15Climate devices."""
|
"""Class to coordinate multiple CCM15Climate devices."""
|
||||||
|
|
||||||
def __init__(self, hass: HomeAssistant, host: str, port: int) -> None:
|
def __init__(
|
||||||
|
self, hass: HomeAssistant, entry: CCM15ConfigEntry, host: str, port: int
|
||||||
|
) -> None:
|
||||||
"""Initialize the coordinator."""
|
"""Initialize the coordinator."""
|
||||||
super().__init__(
|
super().__init__(
|
||||||
hass,
|
hass,
|
||||||
_LOGGER,
|
_LOGGER,
|
||||||
|
config_entry=entry,
|
||||||
name=host,
|
name=host,
|
||||||
update_interval=datetime.timedelta(seconds=DEFAULT_INTERVAL),
|
update_interval=datetime.timedelta(seconds=DEFAULT_INTERVAL),
|
||||||
)
|
)
|
||||||
|
@ -4,18 +4,16 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .coordinator import CCM15ConfigEntry
|
||||||
from .coordinator import CCM15Coordinator
|
|
||||||
|
|
||||||
|
|
||||||
async def async_get_config_entry_diagnostics(
|
async def async_get_config_entry_diagnostics(
|
||||||
hass: HomeAssistant, config_entry: ConfigEntry
|
hass: HomeAssistant, config_entry: CCM15ConfigEntry
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
"""Return diagnostics for a config entry."""
|
"""Return diagnostics for a config entry."""
|
||||||
coordinator: CCM15Coordinator = hass.data[DOMAIN][config_entry.entry_id]
|
coordinator = config_entry.runtime_data
|
||||||
|
|
||||||
return {
|
return {
|
||||||
str(device_id): {
|
str(device_id): {
|
||||||
|
@ -74,9 +74,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
async_track_time_interval(hass, update_records, update_interval)
|
async_track_time_interval(hass, update_records, update_interval)
|
||||||
)
|
)
|
||||||
|
|
||||||
hass.data.setdefault(DOMAIN, {})
|
|
||||||
hass.data[DOMAIN][entry.entry_id] = {}
|
|
||||||
|
|
||||||
hass.services.async_register(DOMAIN, SERVICE_UPDATE_RECORDS, update_records_service)
|
hass.services.async_register(DOMAIN, SERVICE_UPDATE_RECORDS, update_records_service)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
@ -84,7 +81,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
|
|
||||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
"""Unload Cloudflare config entry."""
|
"""Unload Cloudflare config entry."""
|
||||||
hass.data[DOMAIN].pop(entry.entry_id)
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -37,7 +37,6 @@ from .const import (
|
|||||||
CONF_CURRENCIES,
|
CONF_CURRENCIES,
|
||||||
CONF_EXCHANGE_BASE,
|
CONF_EXCHANGE_BASE,
|
||||||
CONF_EXCHANGE_RATES,
|
CONF_EXCHANGE_RATES,
|
||||||
DOMAIN,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
@ -45,33 +44,29 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
PLATFORMS = [Platform.SENSOR]
|
PLATFORMS = [Platform.SENSOR]
|
||||||
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=1)
|
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=1)
|
||||||
|
|
||||||
|
type CoinbaseConfigEntry = ConfigEntry[CoinbaseData]
|
||||||
|
|
||||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|
||||||
|
async def async_setup_entry(hass: HomeAssistant, entry: CoinbaseConfigEntry) -> bool:
|
||||||
"""Set up Coinbase from a config entry."""
|
"""Set up Coinbase from a config entry."""
|
||||||
|
|
||||||
instance = await hass.async_add_executor_job(create_and_update_instance, entry)
|
instance = await hass.async_add_executor_job(create_and_update_instance, entry)
|
||||||
|
|
||||||
entry.async_on_unload(entry.add_update_listener(update_listener))
|
entry.async_on_unload(entry.add_update_listener(update_listener))
|
||||||
|
|
||||||
hass.data.setdefault(DOMAIN, {})
|
entry.runtime_data = instance
|
||||||
|
|
||||||
hass.data[DOMAIN][entry.entry_id] = instance
|
|
||||||
|
|
||||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_unload_entry(hass: HomeAssistant, entry: CoinbaseConfigEntry) -> bool:
|
||||||
"""Unload a config entry."""
|
"""Unload a config entry."""
|
||||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||||
if unload_ok:
|
|
||||||
hass.data[DOMAIN].pop(entry.entry_id)
|
|
||||||
|
|
||||||
return unload_ok
|
|
||||||
|
|
||||||
|
|
||||||
def create_and_update_instance(entry: ConfigEntry) -> CoinbaseData:
|
def create_and_update_instance(entry: CoinbaseConfigEntry) -> CoinbaseData:
|
||||||
"""Create and update a Coinbase Data instance."""
|
"""Create and update a Coinbase Data instance."""
|
||||||
if "organizations" not in entry.data[CONF_API_KEY]:
|
if "organizations" not in entry.data[CONF_API_KEY]:
|
||||||
client = LegacyClient(entry.data[CONF_API_KEY], entry.data[CONF_API_TOKEN])
|
client = LegacyClient(entry.data[CONF_API_KEY], entry.data[CONF_API_TOKEN])
|
||||||
@ -87,7 +82,9 @@ def create_and_update_instance(entry: ConfigEntry) -> CoinbaseData:
|
|||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
|
||||||
async def update_listener(hass: HomeAssistant, config_entry: ConfigEntry) -> None:
|
async def update_listener(
|
||||||
|
hass: HomeAssistant, config_entry: CoinbaseConfigEntry
|
||||||
|
) -> None:
|
||||||
"""Handle options update."""
|
"""Handle options update."""
|
||||||
|
|
||||||
await hass.config_entries.async_reload(config_entry.entry_id)
|
await hass.config_entries.async_reload(config_entry.entry_id)
|
||||||
|
@ -11,18 +11,13 @@ from coinbase.wallet.client import Client as LegacyClient
|
|||||||
from coinbase.wallet.error import AuthenticationError
|
from coinbase.wallet.error import AuthenticationError
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.config_entries import (
|
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult, OptionsFlow
|
||||||
ConfigEntry,
|
|
||||||
ConfigFlow,
|
|
||||||
ConfigFlowResult,
|
|
||||||
OptionsFlow,
|
|
||||||
)
|
|
||||||
from homeassistant.const import CONF_API_KEY, CONF_API_TOKEN, CONF_API_VERSION
|
from homeassistant.const import CONF_API_KEY, CONF_API_TOKEN, CONF_API_VERSION
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
|
||||||
from . import get_accounts
|
from . import CoinbaseConfigEntry, get_accounts
|
||||||
from .const import (
|
from .const import (
|
||||||
ACCOUNT_IS_VAULT,
|
ACCOUNT_IS_VAULT,
|
||||||
API_ACCOUNT_CURRENCY,
|
API_ACCOUNT_CURRENCY,
|
||||||
@ -83,10 +78,12 @@ async def validate_api(hass: HomeAssistant, data):
|
|||||||
return {"title": user, "api_version": api_version}
|
return {"title": user, "api_version": api_version}
|
||||||
|
|
||||||
|
|
||||||
async def validate_options(hass: HomeAssistant, config_entry: ConfigEntry, options):
|
async def validate_options(
|
||||||
|
hass: HomeAssistant, config_entry: CoinbaseConfigEntry, options
|
||||||
|
):
|
||||||
"""Validate the requested resources are provided by API."""
|
"""Validate the requested resources are provided by API."""
|
||||||
|
|
||||||
client = hass.data[DOMAIN][config_entry.entry_id].client
|
client = config_entry.runtime_data.client
|
||||||
|
|
||||||
accounts = await hass.async_add_executor_job(
|
accounts = await hass.async_add_executor_job(
|
||||||
get_accounts, client, config_entry.data.get("api_version", "v2")
|
get_accounts, client, config_entry.data.get("api_version", "v2")
|
||||||
@ -155,7 +152,7 @@ class CoinbaseConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
@callback
|
@callback
|
||||||
def async_get_options_flow(
|
def async_get_options_flow(
|
||||||
config_entry: ConfigEntry,
|
config_entry: CoinbaseConfigEntry,
|
||||||
) -> OptionsFlowHandler:
|
) -> OptionsFlowHandler:
|
||||||
"""Get the options flow for this handler."""
|
"""Get the options flow for this handler."""
|
||||||
return OptionsFlowHandler()
|
return OptionsFlowHandler()
|
||||||
|
@ -3,12 +3,11 @@
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from homeassistant.components.diagnostics import async_redact_data
|
from homeassistant.components.diagnostics import async_redact_data
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.const import CONF_API_KEY, CONF_API_TOKEN, CONF_ID
|
from homeassistant.const import CONF_API_KEY, CONF_API_TOKEN, CONF_ID
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
from . import CoinbaseData
|
from . import CoinbaseConfigEntry
|
||||||
from .const import API_ACCOUNT_AMOUNT, API_RESOURCE_PATH, CONF_TITLE, DOMAIN
|
from .const import API_ACCOUNT_AMOUNT, API_RESOURCE_PATH, CONF_TITLE
|
||||||
|
|
||||||
TO_REDACT = {
|
TO_REDACT = {
|
||||||
API_ACCOUNT_AMOUNT,
|
API_ACCOUNT_AMOUNT,
|
||||||
@ -21,15 +20,13 @@ TO_REDACT = {
|
|||||||
|
|
||||||
|
|
||||||
async def async_get_config_entry_diagnostics(
|
async def async_get_config_entry_diagnostics(
|
||||||
hass: HomeAssistant, entry: ConfigEntry
|
hass: HomeAssistant, entry: CoinbaseConfigEntry
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
"""Return diagnostics for a config entry."""
|
"""Return diagnostics for a config entry."""
|
||||||
instance: CoinbaseData = hass.data[DOMAIN][entry.entry_id]
|
|
||||||
|
|
||||||
return async_redact_data(
|
return async_redact_data(
|
||||||
{
|
{
|
||||||
"entry": entry.as_dict(),
|
"entry": entry.as_dict(),
|
||||||
"accounts": instance.accounts,
|
"accounts": entry.runtime_data.accounts,
|
||||||
},
|
},
|
||||||
TO_REDACT,
|
TO_REDACT,
|
||||||
)
|
)
|
||||||
|
@ -5,12 +5,11 @@ from __future__ import annotations
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from homeassistant.components.sensor import SensorEntity, SensorStateClass
|
from homeassistant.components.sensor import SensorEntity, SensorStateClass
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
|
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
from . import CoinbaseData
|
from . import CoinbaseConfigEntry, CoinbaseData
|
||||||
from .const import (
|
from .const import (
|
||||||
ACCOUNT_IS_VAULT,
|
ACCOUNT_IS_VAULT,
|
||||||
API_ACCOUNT_AMOUNT,
|
API_ACCOUNT_AMOUNT,
|
||||||
@ -45,11 +44,11 @@ ATTRIBUTION = "Data provided by coinbase.com"
|
|||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
config_entry: ConfigEntry,
|
config_entry: CoinbaseConfigEntry,
|
||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up Coinbase sensor platform."""
|
"""Set up Coinbase sensor platform."""
|
||||||
instance: CoinbaseData = hass.data[DOMAIN][config_entry.entry_id]
|
instance = config_entry.runtime_data
|
||||||
|
|
||||||
entities: list[SensorEntity] = []
|
entities: list[SensorEntity] = []
|
||||||
|
|
||||||
|
@ -2,12 +2,16 @@
|
|||||||
|
|
||||||
from aiocomelit.const import BRIDGE
|
from aiocomelit.const import BRIDGE
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.const import CONF_HOST, CONF_PIN, CONF_PORT, CONF_TYPE, Platform
|
from homeassistant.const import CONF_HOST, CONF_PIN, CONF_PORT, CONF_TYPE, Platform
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
from .const import DEFAULT_PORT, DOMAIN
|
from .const import DEFAULT_PORT
|
||||||
from .coordinator import ComelitBaseCoordinator, ComelitSerialBridge, ComelitVedoSystem
|
from .coordinator import (
|
||||||
|
ComelitBaseCoordinator,
|
||||||
|
ComelitConfigEntry,
|
||||||
|
ComelitSerialBridge,
|
||||||
|
ComelitVedoSystem,
|
||||||
|
)
|
||||||
|
|
||||||
BRIDGE_PLATFORMS = [
|
BRIDGE_PLATFORMS = [
|
||||||
Platform.CLIMATE,
|
Platform.CLIMATE,
|
||||||
@ -24,13 +28,14 @@ VEDO_PLATFORMS = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_setup_entry(hass: HomeAssistant, entry: ComelitConfigEntry) -> bool:
|
||||||
"""Set up Comelit platform."""
|
"""Set up Comelit platform."""
|
||||||
|
|
||||||
coordinator: ComelitBaseCoordinator
|
coordinator: ComelitBaseCoordinator
|
||||||
if entry.data.get(CONF_TYPE, BRIDGE) == BRIDGE:
|
if entry.data.get(CONF_TYPE, BRIDGE) == BRIDGE:
|
||||||
coordinator = ComelitSerialBridge(
|
coordinator = ComelitSerialBridge(
|
||||||
hass,
|
hass,
|
||||||
|
entry,
|
||||||
entry.data[CONF_HOST],
|
entry.data[CONF_HOST],
|
||||||
entry.data.get(CONF_PORT, DEFAULT_PORT),
|
entry.data.get(CONF_PORT, DEFAULT_PORT),
|
||||||
entry.data[CONF_PIN],
|
entry.data[CONF_PIN],
|
||||||
@ -39,6 +44,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
else:
|
else:
|
||||||
coordinator = ComelitVedoSystem(
|
coordinator = ComelitVedoSystem(
|
||||||
hass,
|
hass,
|
||||||
|
entry,
|
||||||
entry.data[CONF_HOST],
|
entry.data[CONF_HOST],
|
||||||
entry.data.get(CONF_PORT, DEFAULT_PORT),
|
entry.data.get(CONF_PORT, DEFAULT_PORT),
|
||||||
entry.data[CONF_PIN],
|
entry.data[CONF_PIN],
|
||||||
@ -47,14 +53,14 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
|
|
||||||
await coordinator.async_config_entry_first_refresh()
|
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)
|
await hass.config_entries.async_forward_entry_setups(entry, platforms)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_unload_entry(hass: HomeAssistant, entry: ComelitConfigEntry) -> bool:
|
||||||
"""Unload a config entry."""
|
"""Unload a config entry."""
|
||||||
|
|
||||||
if entry.data.get(CONF_TYPE, BRIDGE) == BRIDGE:
|
if entry.data.get(CONF_TYPE, BRIDGE) == BRIDGE:
|
||||||
@ -62,10 +68,9 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
else:
|
else:
|
||||||
platforms = VEDO_PLATFORMS
|
platforms = VEDO_PLATFORMS
|
||||||
|
|
||||||
coordinator: ComelitBaseCoordinator = hass.data[DOMAIN][entry.entry_id]
|
coordinator = entry.runtime_data
|
||||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, platforms):
|
if unload_ok := await hass.config_entries.async_unload_platforms(entry, platforms):
|
||||||
await coordinator.api.logout()
|
await coordinator.api.logout()
|
||||||
await coordinator.api.close()
|
await coordinator.api.close()
|
||||||
hass.data[DOMAIN].pop(entry.entry_id)
|
|
||||||
|
|
||||||
return unload_ok
|
return unload_ok
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
from typing import cast
|
||||||
|
|
||||||
from aiocomelit.api import ComelitVedoAreaObject
|
from aiocomelit.api import ComelitVedoAreaObject
|
||||||
from aiocomelit.const import ALARM_AREAS, AlarmAreaState
|
from aiocomelit.const import ALARM_AREAS, AlarmAreaState
|
||||||
@ -13,13 +14,11 @@ from homeassistant.components.alarm_control_panel import (
|
|||||||
AlarmControlPanelState,
|
AlarmControlPanelState,
|
||||||
CodeFormat,
|
CodeFormat,
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .coordinator import ComelitConfigEntry, ComelitVedoSystem
|
||||||
from .coordinator import ComelitVedoSystem
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -48,12 +47,12 @@ ALARM_AREA_ARMED_STATUS: dict[str, int] = {
|
|||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
config_entry: ConfigEntry,
|
config_entry: ComelitConfigEntry,
|
||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the Comelit VEDO system alarm control panel devices."""
|
"""Set up the Comelit VEDO system alarm control panel devices."""
|
||||||
|
|
||||||
coordinator: ComelitVedoSystem = hass.data[DOMAIN][config_entry.entry_id]
|
coordinator = cast(ComelitVedoSystem, config_entry.runtime_data)
|
||||||
|
|
||||||
async_add_entities(
|
async_add_entities(
|
||||||
ComelitAlarmEntity(coordinator, device, config_entry.entry_id)
|
ComelitAlarmEntity(coordinator, device, config_entry.entry_id)
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import cast
|
||||||
|
|
||||||
from aiocomelit import ComelitVedoZoneObject
|
from aiocomelit import ComelitVedoZoneObject
|
||||||
from aiocomelit.const import ALARM_ZONES
|
from aiocomelit.const import ALARM_ZONES
|
||||||
|
|
||||||
@ -9,23 +11,21 @@ from homeassistant.components.binary_sensor import (
|
|||||||
BinarySensorDeviceClass,
|
BinarySensorDeviceClass,
|
||||||
BinarySensorEntity,
|
BinarySensorEntity,
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .coordinator import ComelitConfigEntry, ComelitVedoSystem
|
||||||
from .coordinator import ComelitVedoSystem
|
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
config_entry: ConfigEntry,
|
config_entry: ComelitConfigEntry,
|
||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up Comelit VEDO presence sensors."""
|
"""Set up Comelit VEDO presence sensors."""
|
||||||
|
|
||||||
coordinator: ComelitVedoSystem = hass.data[DOMAIN][config_entry.entry_id]
|
coordinator = cast(ComelitVedoSystem, config_entry.runtime_data)
|
||||||
|
|
||||||
async_add_entities(
|
async_add_entities(
|
||||||
ComelitVedoBinarySensorEntity(coordinator, device, config_entry.entry_id)
|
ComelitVedoBinarySensorEntity(coordinator, device, config_entry.entry_id)
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from enum import StrEnum
|
from enum import StrEnum
|
||||||
from typing import Any
|
from typing import Any, cast
|
||||||
|
|
||||||
from aiocomelit import ComelitSerialBridgeObject
|
from aiocomelit import ComelitSerialBridgeObject
|
||||||
from aiocomelit.const import CLIMATE
|
from aiocomelit.const import CLIMATE
|
||||||
@ -15,14 +15,12 @@ from homeassistant.components.climate import (
|
|||||||
HVACMode,
|
HVACMode,
|
||||||
UnitOfTemperature,
|
UnitOfTemperature,
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.const import ATTR_TEMPERATURE, PRECISION_TENTHS
|
from homeassistant.const import ATTR_TEMPERATURE, PRECISION_TENTHS
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .coordinator import ComelitConfigEntry, ComelitSerialBridge
|
||||||
from .coordinator import ComelitSerialBridge
|
|
||||||
|
|
||||||
|
|
||||||
class ClimaComelitMode(StrEnum):
|
class ClimaComelitMode(StrEnum):
|
||||||
@ -72,12 +70,12 @@ MODE_TO_ACTION: dict[HVACMode, ClimaComelitCommand] = {
|
|||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
config_entry: ConfigEntry,
|
config_entry: ComelitConfigEntry,
|
||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up Comelit climates."""
|
"""Set up Comelit climates."""
|
||||||
|
|
||||||
coordinator: ComelitSerialBridge = hass.data[DOMAIN][config_entry.entry_id]
|
coordinator = cast(ComelitSerialBridge, config_entry.runtime_data)
|
||||||
|
|
||||||
async_add_entities(
|
async_add_entities(
|
||||||
ComelitClimateEntity(coordinator, device, config_entry.entry_id)
|
ComelitClimateEntity(coordinator, device, config_entry.entry_id)
|
||||||
|
@ -23,15 +23,19 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, Upda
|
|||||||
|
|
||||||
from .const import _LOGGER, DOMAIN
|
from .const import _LOGGER, DOMAIN
|
||||||
|
|
||||||
|
type ComelitConfigEntry = ConfigEntry[ComelitBaseCoordinator]
|
||||||
|
|
||||||
|
|
||||||
class ComelitBaseCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
class ComelitBaseCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
||||||
"""Base coordinator for Comelit Devices."""
|
"""Base coordinator for Comelit Devices."""
|
||||||
|
|
||||||
_hw_version: str
|
_hw_version: str
|
||||||
config_entry: ConfigEntry
|
config_entry: ComelitConfigEntry
|
||||||
api: ComelitCommonApi
|
api: ComelitCommonApi
|
||||||
|
|
||||||
def __init__(self, hass: HomeAssistant, device: str, host: str) -> None:
|
def __init__(
|
||||||
|
self, hass: HomeAssistant, entry: ComelitConfigEntry, device: str, host: str
|
||||||
|
) -> None:
|
||||||
"""Initialize the scanner."""
|
"""Initialize the scanner."""
|
||||||
|
|
||||||
self._device = device
|
self._device = device
|
||||||
@ -40,13 +44,14 @@ class ComelitBaseCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
|||||||
super().__init__(
|
super().__init__(
|
||||||
hass=hass,
|
hass=hass,
|
||||||
logger=_LOGGER,
|
logger=_LOGGER,
|
||||||
|
config_entry=entry,
|
||||||
name=f"{DOMAIN}-{host}-coordinator",
|
name=f"{DOMAIN}-{host}-coordinator",
|
||||||
update_interval=timedelta(seconds=5),
|
update_interval=timedelta(seconds=5),
|
||||||
)
|
)
|
||||||
device_registry = dr.async_get(self.hass)
|
device_registry = dr.async_get(self.hass)
|
||||||
device_registry.async_get_or_create(
|
device_registry.async_get_or_create(
|
||||||
config_entry_id=self.config_entry.entry_id,
|
config_entry_id=entry.entry_id,
|
||||||
identifiers={(DOMAIN, self.config_entry.entry_id)},
|
identifiers={(DOMAIN, entry.entry_id)},
|
||||||
model=device,
|
model=device,
|
||||||
name=f"{device} ({self._host})",
|
name=f"{device} ({self._host})",
|
||||||
manufacturer="Comelit",
|
manufacturer="Comelit",
|
||||||
@ -98,10 +103,17 @@ class ComelitSerialBridge(ComelitBaseCoordinator):
|
|||||||
_hw_version = "20003101"
|
_hw_version = "20003101"
|
||||||
api: ComeliteSerialBridgeApi
|
api: ComeliteSerialBridgeApi
|
||||||
|
|
||||||
def __init__(self, hass: HomeAssistant, host: str, port: int, pin: int) -> None:
|
def __init__(
|
||||||
|
self,
|
||||||
|
hass: HomeAssistant,
|
||||||
|
entry: ComelitConfigEntry,
|
||||||
|
host: str,
|
||||||
|
port: int,
|
||||||
|
pin: int,
|
||||||
|
) -> None:
|
||||||
"""Initialize the scanner."""
|
"""Initialize the scanner."""
|
||||||
self.api = ComeliteSerialBridgeApi(host, port, pin)
|
self.api = ComeliteSerialBridgeApi(host, port, pin)
|
||||||
super().__init__(hass, BRIDGE, host)
|
super().__init__(hass, entry, BRIDGE, host)
|
||||||
|
|
||||||
async def _async_update_system_data(self) -> dict[str, Any]:
|
async def _async_update_system_data(self) -> dict[str, Any]:
|
||||||
"""Specific method for updating data."""
|
"""Specific method for updating data."""
|
||||||
@ -114,10 +126,17 @@ class ComelitVedoSystem(ComelitBaseCoordinator):
|
|||||||
_hw_version = "VEDO IP"
|
_hw_version = "VEDO IP"
|
||||||
api: ComelitVedoApi
|
api: ComelitVedoApi
|
||||||
|
|
||||||
def __init__(self, hass: HomeAssistant, host: str, port: int, pin: int) -> None:
|
def __init__(
|
||||||
|
self,
|
||||||
|
hass: HomeAssistant,
|
||||||
|
entry: ComelitConfigEntry,
|
||||||
|
host: str,
|
||||||
|
port: int,
|
||||||
|
pin: int,
|
||||||
|
) -> None:
|
||||||
"""Initialize the scanner."""
|
"""Initialize the scanner."""
|
||||||
self.api = ComelitVedoApi(host, port, pin)
|
self.api = ComelitVedoApi(host, port, pin)
|
||||||
super().__init__(hass, VEDO, host)
|
super().__init__(hass, entry, VEDO, host)
|
||||||
|
|
||||||
async def _async_update_system_data(self) -> dict[str, Any]:
|
async def _async_update_system_data(self) -> dict[str, Any]:
|
||||||
"""Specific method for updating data."""
|
"""Specific method for updating data."""
|
||||||
|
@ -2,30 +2,28 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Any
|
from typing import Any, cast
|
||||||
|
|
||||||
from aiocomelit import ComelitSerialBridgeObject
|
from aiocomelit import ComelitSerialBridgeObject
|
||||||
from aiocomelit.const import COVER, STATE_COVER, STATE_OFF, STATE_ON
|
from aiocomelit.const import COVER, STATE_COVER, STATE_OFF, STATE_ON
|
||||||
|
|
||||||
from homeassistant.components.cover import CoverDeviceClass, CoverEntity, CoverState
|
from homeassistant.components.cover import CoverDeviceClass, CoverEntity, CoverState
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.restore_state import RestoreEntity
|
from homeassistant.helpers.restore_state import RestoreEntity
|
||||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .coordinator import ComelitConfigEntry, ComelitSerialBridge
|
||||||
from .coordinator import ComelitSerialBridge
|
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
config_entry: ConfigEntry,
|
config_entry: ComelitConfigEntry,
|
||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up Comelit covers."""
|
"""Set up Comelit covers."""
|
||||||
|
|
||||||
coordinator: ComelitSerialBridge = hass.data[DOMAIN][config_entry.entry_id]
|
coordinator = cast(ComelitSerialBridge, config_entry.runtime_data)
|
||||||
|
|
||||||
async_add_entities(
|
async_add_entities(
|
||||||
ComelitCoverEntity(coordinator, device, config_entry.entry_id)
|
ComelitCoverEntity(coordinator, device, config_entry.entry_id)
|
||||||
|
@ -12,22 +12,20 @@ from aiocomelit import (
|
|||||||
from aiocomelit.const import BRIDGE
|
from aiocomelit.const import BRIDGE
|
||||||
|
|
||||||
from homeassistant.components.diagnostics import async_redact_data
|
from homeassistant.components.diagnostics import async_redact_data
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.const import CONF_PIN, CONF_TYPE
|
from homeassistant.const import CONF_PIN, CONF_TYPE
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .coordinator import ComelitConfigEntry
|
||||||
from .coordinator import ComelitBaseCoordinator
|
|
||||||
|
|
||||||
TO_REDACT = {CONF_PIN}
|
TO_REDACT = {CONF_PIN}
|
||||||
|
|
||||||
|
|
||||||
async def async_get_config_entry_diagnostics(
|
async def async_get_config_entry_diagnostics(
|
||||||
hass: HomeAssistant, entry: ConfigEntry
|
hass: HomeAssistant, entry: ComelitConfigEntry
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
"""Return diagnostics for a config entry."""
|
"""Return diagnostics for a config entry."""
|
||||||
|
|
||||||
coordinator: ComelitBaseCoordinator = hass.data[DOMAIN][entry.entry_id]
|
coordinator = entry.runtime_data
|
||||||
|
|
||||||
dev_list: list[dict[str, Any]] = []
|
dev_list: list[dict[str, Any]] = []
|
||||||
dev_type_list: list[dict[int, Any]] = []
|
dev_type_list: list[dict[int, Any]] = []
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from enum import StrEnum
|
from enum import StrEnum
|
||||||
from typing import Any
|
from typing import Any, cast
|
||||||
|
|
||||||
from aiocomelit import ComelitSerialBridgeObject
|
from aiocomelit import ComelitSerialBridgeObject
|
||||||
from aiocomelit.const import CLIMATE
|
from aiocomelit.const import CLIMATE
|
||||||
@ -16,14 +16,13 @@ from homeassistant.components.humidifier import (
|
|||||||
HumidifierEntity,
|
HumidifierEntity,
|
||||||
HumidifierEntityFeature,
|
HumidifierEntityFeature,
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.exceptions import ServiceValidationError
|
from homeassistant.exceptions import ServiceValidationError
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
from .coordinator import ComelitSerialBridge
|
from .coordinator import ComelitConfigEntry, ComelitSerialBridge
|
||||||
|
|
||||||
|
|
||||||
class HumidifierComelitMode(StrEnum):
|
class HumidifierComelitMode(StrEnum):
|
||||||
@ -55,12 +54,12 @@ MODE_TO_ACTION: dict[str, HumidifierComelitCommand] = {
|
|||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
config_entry: ConfigEntry,
|
config_entry: ComelitConfigEntry,
|
||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up Comelit humidifiers."""
|
"""Set up Comelit humidifiers."""
|
||||||
|
|
||||||
coordinator: ComelitSerialBridge = hass.data[DOMAIN][config_entry.entry_id]
|
coordinator = cast(ComelitSerialBridge, config_entry.runtime_data)
|
||||||
|
|
||||||
entities: list[ComelitHumidifierEntity] = []
|
entities: list[ComelitHumidifierEntity] = []
|
||||||
for device in coordinator.data[CLIMATE].values():
|
for device in coordinator.data[CLIMATE].values():
|
||||||
|
@ -2,29 +2,27 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Any
|
from typing import Any, cast
|
||||||
|
|
||||||
from aiocomelit import ComelitSerialBridgeObject
|
from aiocomelit import ComelitSerialBridgeObject
|
||||||
from aiocomelit.const import LIGHT, STATE_OFF, STATE_ON
|
from aiocomelit.const import LIGHT, STATE_OFF, STATE_ON
|
||||||
|
|
||||||
from homeassistant.components.light import ColorMode, LightEntity
|
from homeassistant.components.light import ColorMode, LightEntity
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .coordinator import ComelitConfigEntry, ComelitSerialBridge
|
||||||
from .coordinator import ComelitSerialBridge
|
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
config_entry: ConfigEntry,
|
config_entry: ComelitConfigEntry,
|
||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up Comelit lights."""
|
"""Set up Comelit lights."""
|
||||||
|
|
||||||
coordinator: ComelitSerialBridge = hass.data[DOMAIN][config_entry.entry_id]
|
coordinator = cast(ComelitSerialBridge, config_entry.runtime_data)
|
||||||
|
|
||||||
async_add_entities(
|
async_add_entities(
|
||||||
ComelitLightEntity(coordinator, device, config_entry.entry_id)
|
ComelitLightEntity(coordinator, device, config_entry.entry_id)
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Final
|
from typing import Final, cast
|
||||||
|
|
||||||
from aiocomelit import ComelitSerialBridgeObject, ComelitVedoZoneObject
|
from aiocomelit import ComelitSerialBridgeObject, ComelitVedoZoneObject
|
||||||
from aiocomelit.const import ALARM_ZONES, BRIDGE, OTHER, AlarmZoneState
|
from aiocomelit.const import ALARM_ZONES, BRIDGE, OTHER, AlarmZoneState
|
||||||
@ -12,15 +12,13 @@ from homeassistant.components.sensor import (
|
|||||||
SensorEntity,
|
SensorEntity,
|
||||||
SensorEntityDescription,
|
SensorEntityDescription,
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.const import CONF_TYPE, UnitOfPower
|
from homeassistant.const import CONF_TYPE, UnitOfPower
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.typing import StateType
|
from homeassistant.helpers.typing import StateType
|
||||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .coordinator import ComelitConfigEntry, ComelitSerialBridge, ComelitVedoSystem
|
||||||
from .coordinator import ComelitSerialBridge, ComelitVedoSystem
|
|
||||||
|
|
||||||
SENSOR_BRIDGE_TYPES: Final = (
|
SENSOR_BRIDGE_TYPES: Final = (
|
||||||
SensorEntityDescription(
|
SensorEntityDescription(
|
||||||
@ -43,7 +41,7 @@ SENSOR_VEDO_TYPES: Final = (
|
|||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
config_entry: ConfigEntry,
|
config_entry: ComelitConfigEntry,
|
||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up Comelit sensors."""
|
"""Set up Comelit sensors."""
|
||||||
@ -56,12 +54,12 @@ async def async_setup_entry(
|
|||||||
|
|
||||||
async def async_setup_bridge_entry(
|
async def async_setup_bridge_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
config_entry: ConfigEntry,
|
config_entry: ComelitConfigEntry,
|
||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up Comelit Bridge sensors."""
|
"""Set up Comelit Bridge sensors."""
|
||||||
|
|
||||||
coordinator: ComelitSerialBridge = hass.data[DOMAIN][config_entry.entry_id]
|
coordinator = cast(ComelitSerialBridge, config_entry.runtime_data)
|
||||||
|
|
||||||
entities: list[ComelitBridgeSensorEntity] = []
|
entities: list[ComelitBridgeSensorEntity] = []
|
||||||
for device in coordinator.data[OTHER].values():
|
for device in coordinator.data[OTHER].values():
|
||||||
@ -76,12 +74,12 @@ async def async_setup_bridge_entry(
|
|||||||
|
|
||||||
async def async_setup_vedo_entry(
|
async def async_setup_vedo_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
config_entry: ConfigEntry,
|
config_entry: ComelitConfigEntry,
|
||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up Comelit VEDO sensors."""
|
"""Set up Comelit VEDO sensors."""
|
||||||
|
|
||||||
coordinator: ComelitVedoSystem = hass.data[DOMAIN][config_entry.entry_id]
|
coordinator = cast(ComelitVedoSystem, config_entry.runtime_data)
|
||||||
|
|
||||||
entities: list[ComelitVedoSensorEntity] = []
|
entities: list[ComelitVedoSensorEntity] = []
|
||||||
for device in coordinator.data[ALARM_ZONES].values():
|
for device in coordinator.data[ALARM_ZONES].values():
|
||||||
|
@ -2,29 +2,27 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Any
|
from typing import Any, cast
|
||||||
|
|
||||||
from aiocomelit import ComelitSerialBridgeObject
|
from aiocomelit import ComelitSerialBridgeObject
|
||||||
from aiocomelit.const import IRRIGATION, OTHER, STATE_OFF, STATE_ON
|
from aiocomelit.const import IRRIGATION, OTHER, STATE_OFF, STATE_ON
|
||||||
|
|
||||||
from homeassistant.components.switch import SwitchDeviceClass, SwitchEntity
|
from homeassistant.components.switch import SwitchDeviceClass, SwitchEntity
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .coordinator import ComelitConfigEntry, ComelitSerialBridge
|
||||||
from .coordinator import ComelitSerialBridge
|
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
config_entry: ConfigEntry,
|
config_entry: ComelitConfigEntry,
|
||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up Comelit switches."""
|
"""Set up Comelit switches."""
|
||||||
|
|
||||||
coordinator: ComelitSerialBridge = hass.data[DOMAIN][config_entry.entry_id]
|
coordinator = cast(ComelitSerialBridge, config_entry.runtime_data)
|
||||||
|
|
||||||
entities: list[ComelitSwitchEntity] = []
|
entities: list[ComelitSwitchEntity] = []
|
||||||
entities.extend(
|
entities.extend(
|
||||||
|
@ -798,36 +798,13 @@ class DefaultAgent(ConversationEntity):
|
|||||||
intent_response: intent.IntentResponse,
|
intent_response: intent.IntentResponse,
|
||||||
recognize_result: RecognizeResult,
|
recognize_result: RecognizeResult,
|
||||||
) -> str:
|
) -> str:
|
||||||
# Make copies of the states here so we can add translated names for responses.
|
|
||||||
matched = [
|
|
||||||
state_copy
|
|
||||||
for state in intent_response.matched_states
|
|
||||||
if (state_copy := core.State.from_dict(state.as_dict()))
|
|
||||||
]
|
|
||||||
unmatched = [
|
|
||||||
state_copy
|
|
||||||
for state in intent_response.unmatched_states
|
|
||||||
if (state_copy := core.State.from_dict(state.as_dict()))
|
|
||||||
]
|
|
||||||
all_states = matched + unmatched
|
|
||||||
domains = {state.domain for state in all_states}
|
|
||||||
translations = await translation.async_get_translations(
|
|
||||||
self.hass, language, "entity_component", domains
|
|
||||||
)
|
|
||||||
|
|
||||||
# Use translated state names
|
|
||||||
for state in all_states:
|
|
||||||
device_class = state.attributes.get("device_class", "_")
|
|
||||||
key = f"component.{state.domain}.entity_component.{device_class}.state.{state.state}"
|
|
||||||
state.state = translations.get(key, state.state)
|
|
||||||
|
|
||||||
# Get first matched or unmatched state.
|
# Get first matched or unmatched state.
|
||||||
# This is available in the response template as "state".
|
# This is available in the response template as "state".
|
||||||
state1: core.State | None = None
|
state1: core.State | None = None
|
||||||
if intent_response.matched_states:
|
if intent_response.matched_states:
|
||||||
state1 = matched[0]
|
state1 = intent_response.matched_states[0]
|
||||||
elif intent_response.unmatched_states:
|
elif intent_response.unmatched_states:
|
||||||
state1 = unmatched[0]
|
state1 = intent_response.unmatched_states[0]
|
||||||
|
|
||||||
# Render response template
|
# Render response template
|
||||||
speech_slots = {
|
speech_slots = {
|
||||||
@ -849,11 +826,13 @@ class DefaultAgent(ConversationEntity):
|
|||||||
"query": {
|
"query": {
|
||||||
# Entity states that matched the query (e.g, "on")
|
# Entity states that matched the query (e.g, "on")
|
||||||
"matched": [
|
"matched": [
|
||||||
template.TemplateState(self.hass, state) for state in matched
|
template.TemplateState(self.hass, state)
|
||||||
|
for state in intent_response.matched_states
|
||||||
],
|
],
|
||||||
# Entity states that did not match the query
|
# Entity states that did not match the query
|
||||||
"unmatched": [
|
"unmatched": [
|
||||||
template.TemplateState(self.hass, state) for state in unmatched
|
template.TemplateState(self.hass, state)
|
||||||
|
for state in intent_response.unmatched_states
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -1506,12 +1485,6 @@ def _get_match_error_response(
|
|||||||
# Entity is not in correct state
|
# Entity is not in correct state
|
||||||
assert constraints.states
|
assert constraints.states
|
||||||
state = next(iter(constraints.states))
|
state = next(iter(constraints.states))
|
||||||
if constraints.domains:
|
|
||||||
# Translate if domain is available
|
|
||||||
domain = next(iter(constraints.domains))
|
|
||||||
state = translation.async_translate_state(
|
|
||||||
hass, state, domain, None, None, None
|
|
||||||
)
|
|
||||||
|
|
||||||
return ErrorKey.ENTITY_WRONG_STATE, {"state": state}
|
return ErrorKey.ENTITY_WRONG_STATE, {"state": state}
|
||||||
|
|
||||||
|
@ -2,18 +2,17 @@
|
|||||||
|
|
||||||
from pycoolmasternet_async import CoolMasterNet
|
from pycoolmasternet_async import CoolMasterNet
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.const import CONF_HOST, CONF_PORT, Platform
|
from homeassistant.const import CONF_HOST, CONF_PORT, Platform
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.exceptions import ConfigEntryNotReady
|
from homeassistant.exceptions import ConfigEntryNotReady
|
||||||
|
|
||||||
from .const import CONF_SWING_SUPPORT, DATA_COORDINATOR, DATA_INFO, DOMAIN
|
from .const import CONF_SWING_SUPPORT
|
||||||
from .coordinator import CoolmasterDataUpdateCoordinator
|
from .coordinator import CoolmasterConfigEntry, CoolmasterDataUpdateCoordinator
|
||||||
|
|
||||||
PLATFORMS = [Platform.BINARY_SENSOR, Platform.BUTTON, Platform.CLIMATE, Platform.SENSOR]
|
PLATFORMS = [Platform.BINARY_SENSOR, Platform.BUTTON, Platform.CLIMATE, Platform.SENSOR]
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_setup_entry(hass: HomeAssistant, entry: CoolmasterConfigEntry) -> bool:
|
||||||
"""Set up Coolmaster from a config entry."""
|
"""Set up Coolmaster from a config entry."""
|
||||||
host = entry.data[CONF_HOST]
|
host = entry.data[CONF_HOST]
|
||||||
port = entry.data[CONF_PORT]
|
port = entry.data[CONF_PORT]
|
||||||
@ -38,21 +37,14 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
raise ConfigEntryNotReady
|
raise ConfigEntryNotReady
|
||||||
except OSError as error:
|
except OSError as error:
|
||||||
raise ConfigEntryNotReady from error
|
raise ConfigEntryNotReady from error
|
||||||
coordinator = CoolmasterDataUpdateCoordinator(hass, coolmaster)
|
coordinator = CoolmasterDataUpdateCoordinator(hass, entry, coolmaster, info)
|
||||||
hass.data.setdefault(DOMAIN, {})
|
|
||||||
await coordinator.async_config_entry_first_refresh()
|
await coordinator.async_config_entry_first_refresh()
|
||||||
hass.data[DOMAIN][entry.entry_id] = {
|
entry.runtime_data = coordinator
|
||||||
DATA_INFO: info,
|
|
||||||
DATA_COORDINATOR: coordinator,
|
|
||||||
}
|
|
||||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_unload_entry(hass: HomeAssistant, entry: CoolmasterConfigEntry) -> bool:
|
||||||
"""Unload a Coolmaster config entry."""
|
"""Unload a Coolmaster config entry."""
|
||||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||||
if unload_ok:
|
|
||||||
hass.data[DOMAIN].pop(entry.entry_id)
|
|
||||||
return unload_ok
|
|
||||||
|
@ -7,26 +7,23 @@ from homeassistant.components.binary_sensor import (
|
|||||||
BinarySensorEntity,
|
BinarySensorEntity,
|
||||||
BinarySensorEntityDescription,
|
BinarySensorEntityDescription,
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.const import EntityCategory
|
from homeassistant.const import EntityCategory
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
from .const import DATA_COORDINATOR, DATA_INFO, DOMAIN
|
from .coordinator import CoolmasterConfigEntry
|
||||||
from .entity import CoolmasterEntity
|
from .entity import CoolmasterEntity
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
config_entry: ConfigEntry,
|
config_entry: CoolmasterConfigEntry,
|
||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the CoolMasterNet binary_sensor platform."""
|
"""Set up the CoolMasterNet binary_sensor platform."""
|
||||||
info = hass.data[DOMAIN][config_entry.entry_id][DATA_INFO]
|
coordinator = config_entry.runtime_data
|
||||||
coordinator = hass.data[DOMAIN][config_entry.entry_id][DATA_COORDINATOR]
|
|
||||||
async_add_entities(
|
async_add_entities(
|
||||||
CoolmasterCleanFilter(coordinator, unit_id, info)
|
CoolmasterCleanFilter(coordinator, unit_id) for unit_id in coordinator.data
|
||||||
for unit_id in coordinator.data
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -3,26 +3,23 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from homeassistant.components.button import ButtonEntity, ButtonEntityDescription
|
from homeassistant.components.button import ButtonEntity, ButtonEntityDescription
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.const import EntityCategory
|
from homeassistant.const import EntityCategory
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
from .const import DATA_COORDINATOR, DATA_INFO, DOMAIN
|
from .coordinator import CoolmasterConfigEntry
|
||||||
from .entity import CoolmasterEntity
|
from .entity import CoolmasterEntity
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
config_entry: ConfigEntry,
|
config_entry: CoolmasterConfigEntry,
|
||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the CoolMasterNet button platform."""
|
"""Set up the CoolMasterNet button platform."""
|
||||||
info = hass.data[DOMAIN][config_entry.entry_id][DATA_INFO]
|
coordinator = config_entry.runtime_data
|
||||||
coordinator = hass.data[DOMAIN][config_entry.entry_id][DATA_COORDINATOR]
|
|
||||||
async_add_entities(
|
async_add_entities(
|
||||||
CoolmasterResetFilter(coordinator, unit_id, info)
|
CoolmasterResetFilter(coordinator, unit_id) for unit_id in coordinator.data
|
||||||
for unit_id in coordinator.data
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -12,13 +12,13 @@ from homeassistant.components.climate import (
|
|||||||
ClimateEntityFeature,
|
ClimateEntityFeature,
|
||||||
HVACMode,
|
HVACMode,
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature
|
from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
from .const import CONF_SUPPORTED_MODES, DATA_COORDINATOR, DATA_INFO, DOMAIN
|
from .const import CONF_SUPPORTED_MODES
|
||||||
|
from .coordinator import CoolmasterConfigEntry, CoolmasterDataUpdateCoordinator
|
||||||
from .entity import CoolmasterEntity
|
from .entity import CoolmasterEntity
|
||||||
|
|
||||||
CM_TO_HA_STATE = {
|
CM_TO_HA_STATE = {
|
||||||
@ -38,15 +38,16 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
config_entry: ConfigEntry,
|
config_entry: CoolmasterConfigEntry,
|
||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the CoolMasterNet climate platform."""
|
"""Set up the CoolMasterNet climate platform."""
|
||||||
info = hass.data[DOMAIN][config_entry.entry_id][DATA_INFO]
|
coordinator = config_entry.runtime_data
|
||||||
coordinator = hass.data[DOMAIN][config_entry.entry_id][DATA_COORDINATOR]
|
supported_modes: list[str] = config_entry.data[CONF_SUPPORTED_MODES]
|
||||||
supported_modes = config_entry.data.get(CONF_SUPPORTED_MODES)
|
|
||||||
async_add_entities(
|
async_add_entities(
|
||||||
CoolmasterClimate(coordinator, unit_id, info, supported_modes)
|
CoolmasterClimate(
|
||||||
|
coordinator, unit_id, [HVACMode(mode) for mode in supported_modes]
|
||||||
|
)
|
||||||
for unit_id in coordinator.data
|
for unit_id in coordinator.data
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -56,9 +57,14 @@ class CoolmasterClimate(CoolmasterEntity, ClimateEntity):
|
|||||||
|
|
||||||
_attr_name = None
|
_attr_name = None
|
||||||
|
|
||||||
def __init__(self, coordinator, unit_id, info, supported_modes):
|
def __init__(
|
||||||
|
self,
|
||||||
|
coordinator: CoolmasterDataUpdateCoordinator,
|
||||||
|
unit_id: str,
|
||||||
|
supported_modes: list[HVACMode],
|
||||||
|
) -> None:
|
||||||
"""Initialize the climate device."""
|
"""Initialize the climate device."""
|
||||||
super().__init__(coordinator, unit_id, info)
|
super().__init__(coordinator, unit_id)
|
||||||
self._attr_hvac_modes = supported_modes
|
self._attr_hvac_modes = supported_modes
|
||||||
self._attr_unique_id = unit_id
|
self._attr_unique_id = unit_id
|
||||||
|
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
"""Constants for the Coolmaster integration."""
|
"""Constants for the Coolmaster integration."""
|
||||||
|
|
||||||
DATA_INFO = "info"
|
|
||||||
DATA_COORDINATOR = "coordinator"
|
|
||||||
|
|
||||||
DOMAIN = "coolmaster"
|
DOMAIN = "coolmaster"
|
||||||
|
|
||||||
DEFAULT_PORT = 10102
|
DEFAULT_PORT = 10102
|
||||||
|
@ -1,8 +1,15 @@
|
|||||||
"""DataUpdateCoordinator for coolmaster integration."""
|
"""DataUpdateCoordinator for coolmaster integration."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from pycoolmasternet_async import CoolMasterNet
|
||||||
|
from pycoolmasternet_async.coolmasternet import CoolMasterNetUnit
|
||||||
|
|
||||||
from homeassistant.components.climate import SCAN_INTERVAL
|
from homeassistant.components.climate import SCAN_INTERVAL
|
||||||
|
from homeassistant.config_entries import ConfigEntry
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
@ -10,21 +17,34 @@ from .const import DOMAIN
|
|||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class CoolmasterDataUpdateCoordinator(DataUpdateCoordinator):
|
type CoolmasterConfigEntry = ConfigEntry[CoolmasterDataUpdateCoordinator]
|
||||||
|
|
||||||
|
|
||||||
|
class CoolmasterDataUpdateCoordinator(
|
||||||
|
DataUpdateCoordinator[dict[str, CoolMasterNetUnit]]
|
||||||
|
):
|
||||||
"""Class to manage fetching Coolmaster data."""
|
"""Class to manage fetching Coolmaster data."""
|
||||||
|
|
||||||
def __init__(self, hass, coolmaster):
|
def __init__(
|
||||||
|
self,
|
||||||
|
hass: HomeAssistant,
|
||||||
|
entry: CoolmasterConfigEntry,
|
||||||
|
coolmaster: CoolMasterNet,
|
||||||
|
info: dict[str, str],
|
||||||
|
) -> None:
|
||||||
"""Initialize global Coolmaster data updater."""
|
"""Initialize global Coolmaster data updater."""
|
||||||
self._coolmaster = coolmaster
|
self._coolmaster = coolmaster
|
||||||
|
self.info = info
|
||||||
|
|
||||||
super().__init__(
|
super().__init__(
|
||||||
hass,
|
hass,
|
||||||
_LOGGER,
|
_LOGGER,
|
||||||
|
config_entry=entry,
|
||||||
name=DOMAIN,
|
name=DOMAIN,
|
||||||
update_interval=SCAN_INTERVAL,
|
update_interval=SCAN_INTERVAL,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def _async_update_data(self):
|
async def _async_update_data(self) -> dict[str, CoolMasterNetUnit]:
|
||||||
"""Fetch data from Coolmaster."""
|
"""Fetch data from Coolmaster."""
|
||||||
try:
|
try:
|
||||||
return await self._coolmaster.status()
|
return await self._coolmaster.status()
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
"""Base entity for Coolmaster integration."""
|
"""Base entity for Coolmaster integration."""
|
||||||
|
|
||||||
from pycoolmasternet_async.coolmasternet import CoolMasterNetUnit
|
|
||||||
|
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
from homeassistant.helpers.device_registry import DeviceInfo
|
from homeassistant.helpers.device_registry import DeviceInfo
|
||||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||||
@ -19,18 +17,17 @@ class CoolmasterEntity(CoordinatorEntity[CoolmasterDataUpdateCoordinator]):
|
|||||||
self,
|
self,
|
||||||
coordinator: CoolmasterDataUpdateCoordinator,
|
coordinator: CoolmasterDataUpdateCoordinator,
|
||||||
unit_id: str,
|
unit_id: str,
|
||||||
info: dict[str, str],
|
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initiate CoolmasterEntity."""
|
"""Initiate CoolmasterEntity."""
|
||||||
super().__init__(coordinator)
|
super().__init__(coordinator)
|
||||||
self._unit_id: str = unit_id
|
self._unit_id: str = unit_id
|
||||||
self._unit: CoolMasterNetUnit = coordinator.data[self._unit_id]
|
self._unit = coordinator.data[self._unit_id]
|
||||||
self._attr_device_info: DeviceInfo = DeviceInfo(
|
self._attr_device_info: DeviceInfo = DeviceInfo(
|
||||||
identifiers={(DOMAIN, unit_id)},
|
identifiers={(DOMAIN, unit_id)},
|
||||||
manufacturer="CoolAutomation",
|
manufacturer="CoolAutomation",
|
||||||
model="CoolMasterNet",
|
model="CoolMasterNet",
|
||||||
name=unit_id,
|
name=unit_id,
|
||||||
sw_version=info["version"],
|
sw_version=coordinator.info["version"],
|
||||||
)
|
)
|
||||||
if hasattr(self, "entity_description"):
|
if hasattr(self, "entity_description"):
|
||||||
self._attr_unique_id: str = f"{unit_id}-{self.entity_description.key}"
|
self._attr_unique_id: str = f"{unit_id}-{self.entity_description.key}"
|
||||||
|
@ -3,26 +3,23 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from homeassistant.components.sensor import SensorEntity, SensorEntityDescription
|
from homeassistant.components.sensor import SensorEntity, SensorEntityDescription
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.const import EntityCategory
|
from homeassistant.const import EntityCategory
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
from .const import DATA_COORDINATOR, DATA_INFO, DOMAIN
|
from .coordinator import CoolmasterConfigEntry
|
||||||
from .entity import CoolmasterEntity
|
from .entity import CoolmasterEntity
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
config_entry: ConfigEntry,
|
config_entry: CoolmasterConfigEntry,
|
||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the CoolMasterNet sensor platform."""
|
"""Set up the CoolMasterNet sensor platform."""
|
||||||
info = hass.data[DOMAIN][config_entry.entry_id][DATA_INFO]
|
coordinator = config_entry.runtime_data
|
||||||
coordinator = hass.data[DOMAIN][config_entry.entry_id][DATA_COORDINATOR]
|
|
||||||
async_add_entities(
|
async_add_entities(
|
||||||
CoolmasterCleanFilter(coordinator, unit_id, info)
|
CoolmasterCleanFilter(coordinator, unit_id) for unit_id in coordinator.data
|
||||||
for unit_id in coordinator.data
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -9,7 +9,6 @@ from aiohttp import ClientConnectionError
|
|||||||
from pydaikin.daikin_base import Appliance
|
from pydaikin.daikin_base import Appliance
|
||||||
from pydaikin.factory import DaikinFactory
|
from pydaikin.factory import DaikinFactory
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_API_KEY,
|
CONF_API_KEY,
|
||||||
CONF_HOST,
|
CONF_HOST,
|
||||||
@ -23,8 +22,8 @@ from homeassistant.helpers import device_registry as dr, entity_registry as er
|
|||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
|
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
|
||||||
|
|
||||||
from .const import DOMAIN, KEY_MAC, TIMEOUT
|
from .const import KEY_MAC, TIMEOUT
|
||||||
from .coordinator import DaikinCoordinator
|
from .coordinator import DaikinConfigEntry, DaikinCoordinator
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -32,7 +31,7 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
PLATFORMS = [Platform.CLIMATE, Platform.SENSOR, Platform.SWITCH]
|
PLATFORMS = [Platform.CLIMATE, Platform.SENSOR, Platform.SWITCH]
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_setup_entry(hass: HomeAssistant, entry: DaikinConfigEntry) -> bool:
|
||||||
"""Establish connection with Daikin."""
|
"""Establish connection with Daikin."""
|
||||||
conf = entry.data
|
conf = entry.data
|
||||||
# For backwards compat, set unique ID
|
# For backwards compat, set unique ID
|
||||||
@ -58,29 +57,24 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
_LOGGER.debug("ClientConnectionError to %s", host)
|
_LOGGER.debug("ClientConnectionError to %s", host)
|
||||||
raise ConfigEntryNotReady from err
|
raise ConfigEntryNotReady from err
|
||||||
|
|
||||||
coordinator = DaikinCoordinator(hass, device)
|
coordinator = DaikinCoordinator(hass, entry, device)
|
||||||
|
|
||||||
await coordinator.async_config_entry_first_refresh()
|
await coordinator.async_config_entry_first_refresh()
|
||||||
|
|
||||||
await async_migrate_unique_id(hass, entry, device)
|
await async_migrate_unique_id(hass, entry, device)
|
||||||
|
|
||||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator
|
entry.runtime_data = coordinator
|
||||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_unload_entry(hass: HomeAssistant, entry: DaikinConfigEntry) -> bool:
|
||||||
"""Unload a config entry."""
|
"""Unload a config entry."""
|
||||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||||
if unload_ok:
|
|
||||||
hass.data[DOMAIN].pop(entry.entry_id)
|
|
||||||
if not hass.data[DOMAIN]:
|
|
||||||
hass.data.pop(DOMAIN)
|
|
||||||
return unload_ok
|
|
||||||
|
|
||||||
|
|
||||||
async def async_migrate_unique_id(
|
async def async_migrate_unique_id(
|
||||||
hass: HomeAssistant, config_entry: ConfigEntry, device: Appliance
|
hass: HomeAssistant, config_entry: DaikinConfigEntry, device: Appliance
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Migrate old entry."""
|
"""Migrate old entry."""
|
||||||
dev_reg = dr.async_get(hass)
|
dev_reg = dr.async_get(hass)
|
||||||
|
@ -19,12 +19,10 @@ from homeassistant.components.climate import (
|
|||||||
HVACAction,
|
HVACAction,
|
||||||
HVACMode,
|
HVACMode,
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature
|
from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
from . import DOMAIN as DAIKIN_DOMAIN
|
|
||||||
from .const import (
|
from .const import (
|
||||||
ATTR_INSIDE_TEMPERATURE,
|
ATTR_INSIDE_TEMPERATURE,
|
||||||
ATTR_OUTSIDE_TEMPERATURE,
|
ATTR_OUTSIDE_TEMPERATURE,
|
||||||
@ -32,7 +30,7 @@ from .const import (
|
|||||||
ATTR_STATE_ON,
|
ATTR_STATE_ON,
|
||||||
ATTR_TARGET_TEMPERATURE,
|
ATTR_TARGET_TEMPERATURE,
|
||||||
)
|
)
|
||||||
from .coordinator import DaikinCoordinator
|
from .coordinator import DaikinConfigEntry, DaikinCoordinator
|
||||||
from .entity import DaikinEntity
|
from .entity import DaikinEntity
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
@ -83,10 +81,12 @@ DAIKIN_ATTR_ADVANCED = "adv"
|
|||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
hass: HomeAssistant,
|
||||||
|
entry: DaikinConfigEntry,
|
||||||
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up Daikin climate based on config_entry."""
|
"""Set up Daikin climate based on config_entry."""
|
||||||
daikin_api = hass.data[DAIKIN_DOMAIN].get(entry.entry_id)
|
daikin_api = entry.runtime_data
|
||||||
async_add_entities([DaikinClimate(daikin_api)])
|
async_add_entities([DaikinClimate(daikin_api)])
|
||||||
|
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import logging
|
|||||||
|
|
||||||
from pydaikin.daikin_base import Appliance
|
from pydaikin.daikin_base import Appliance
|
||||||
|
|
||||||
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||||
|
|
||||||
@ -12,15 +13,20 @@ from .const import DOMAIN
|
|||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
type DaikinConfigEntry = ConfigEntry[DaikinCoordinator]
|
||||||
|
|
||||||
|
|
||||||
class DaikinCoordinator(DataUpdateCoordinator[None]):
|
class DaikinCoordinator(DataUpdateCoordinator[None]):
|
||||||
"""Class to manage fetching Daikin data."""
|
"""Class to manage fetching Daikin data."""
|
||||||
|
|
||||||
def __init__(self, hass: HomeAssistant, device: Appliance) -> None:
|
def __init__(
|
||||||
|
self, hass: HomeAssistant, entry: DaikinConfigEntry, device: Appliance
|
||||||
|
) -> None:
|
||||||
"""Initialize global Daikin data updater."""
|
"""Initialize global Daikin data updater."""
|
||||||
super().__init__(
|
super().__init__(
|
||||||
hass,
|
hass,
|
||||||
_LOGGER,
|
_LOGGER,
|
||||||
|
config_entry=entry,
|
||||||
name=device.values.get("name", DOMAIN),
|
name=device.values.get("name", DOMAIN),
|
||||||
update_interval=timedelta(seconds=60),
|
update_interval=timedelta(seconds=60),
|
||||||
)
|
)
|
||||||
|
@ -13,7 +13,6 @@ from homeassistant.components.sensor import (
|
|||||||
SensorEntityDescription,
|
SensorEntityDescription,
|
||||||
SensorStateClass,
|
SensorStateClass,
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
PERCENTAGE,
|
PERCENTAGE,
|
||||||
UnitOfEnergy,
|
UnitOfEnergy,
|
||||||
@ -24,7 +23,6 @@ from homeassistant.const import (
|
|||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
from . import DOMAIN as DAIKIN_DOMAIN
|
|
||||||
from .const import (
|
from .const import (
|
||||||
ATTR_COMPRESSOR_FREQUENCY,
|
ATTR_COMPRESSOR_FREQUENCY,
|
||||||
ATTR_COOL_ENERGY,
|
ATTR_COOL_ENERGY,
|
||||||
@ -37,7 +35,7 @@ from .const import (
|
|||||||
ATTR_TOTAL_ENERGY_TODAY,
|
ATTR_TOTAL_ENERGY_TODAY,
|
||||||
ATTR_TOTAL_POWER,
|
ATTR_TOTAL_POWER,
|
||||||
)
|
)
|
||||||
from .coordinator import DaikinCoordinator
|
from .coordinator import DaikinConfigEntry, DaikinCoordinator
|
||||||
from .entity import DaikinEntity
|
from .entity import DaikinEntity
|
||||||
|
|
||||||
|
|
||||||
@ -134,10 +132,12 @@ SENSOR_TYPES: tuple[DaikinSensorEntityDescription, ...] = (
|
|||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
hass: HomeAssistant,
|
||||||
|
entry: DaikinConfigEntry,
|
||||||
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up Daikin climate based on config_entry."""
|
"""Set up Daikin climate based on config_entry."""
|
||||||
daikin_api = hass.data[DAIKIN_DOMAIN].get(entry.entry_id)
|
daikin_api = entry.runtime_data
|
||||||
sensors = [ATTR_INSIDE_TEMPERATURE]
|
sensors = [ATTR_INSIDE_TEMPERATURE]
|
||||||
if daikin_api.device.support_outside_temperature:
|
if daikin_api.device.support_outside_temperature:
|
||||||
sensors.append(ATTR_OUTSIDE_TEMPERATURE)
|
sensors.append(ATTR_OUTSIDE_TEMPERATURE)
|
||||||
|
@ -5,12 +5,10 @@ from __future__ import annotations
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from homeassistant.components.switch import SwitchEntity
|
from homeassistant.components.switch import SwitchEntity
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
from . import DOMAIN
|
from .coordinator import DaikinConfigEntry, DaikinCoordinator
|
||||||
from .coordinator import DaikinCoordinator
|
|
||||||
from .entity import DaikinEntity
|
from .entity import DaikinEntity
|
||||||
|
|
||||||
DAIKIN_ATTR_ADVANCED = "adv"
|
DAIKIN_ATTR_ADVANCED = "adv"
|
||||||
@ -19,10 +17,12 @@ DAIKIN_ATTR_MODE = "mode"
|
|||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
hass: HomeAssistant,
|
||||||
|
entry: DaikinConfigEntry,
|
||||||
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up Daikin climate based on config_entry."""
|
"""Set up Daikin climate based on config_entry."""
|
||||||
daikin_api: DaikinCoordinator = hass.data[DOMAIN][entry.entry_id]
|
daikin_api = entry.runtime_data
|
||||||
switches: list[SwitchEntity] = []
|
switches: list[SwitchEntity] = []
|
||||||
if zones := daikin_api.device.zones:
|
if zones := daikin_api.device.zones:
|
||||||
switches.extend(
|
switches.extend(
|
||||||
|
@ -9,12 +9,12 @@ from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
|
|||||||
from homeassistant.helpers import config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
from homeassistant.helpers.typing import ConfigType
|
from homeassistant.helpers.typing import ConfigType
|
||||||
|
|
||||||
from .config_flow import get_master_hub
|
|
||||||
from .const import CONF_MASTER_GATEWAY, DOMAIN, PLATFORMS
|
from .const import CONF_MASTER_GATEWAY, DOMAIN, PLATFORMS
|
||||||
from .deconz_event import async_setup_events, async_unload_events
|
from .deconz_event import async_setup_events, async_unload_events
|
||||||
from .errors import AuthenticationRequired, CannotConnect
|
from .errors import AuthenticationRequired, CannotConnect
|
||||||
from .hub import DeconzHub, get_deconz_api
|
from .hub import DeconzHub, get_deconz_api
|
||||||
from .services import async_setup_services
|
from .services import async_setup_services
|
||||||
|
from .util import get_master_hub
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
|
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
|
||||||
|
|
||||||
@ -46,7 +46,9 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
|
|||||||
hub = hass.data[DOMAIN][config_entry.entry_id] = DeconzHub(hass, config_entry, api)
|
hub = hass.data[DOMAIN][config_entry.entry_id] = DeconzHub(hass, config_entry, api)
|
||||||
await hub.async_update_device_registry()
|
await hub.async_update_device_registry()
|
||||||
|
|
||||||
config_entry.add_update_listener(hub.async_config_entry_updated)
|
config_entry.async_on_unload(
|
||||||
|
config_entry.add_update_listener(hub.async_config_entry_updated)
|
||||||
|
)
|
||||||
|
|
||||||
await async_setup_events(hub)
|
await async_setup_events(hub)
|
||||||
await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS)
|
await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS)
|
||||||
|
@ -27,7 +27,7 @@ from homeassistant.config_entries import (
|
|||||||
OptionsFlow,
|
OptionsFlow,
|
||||||
)
|
)
|
||||||
from homeassistant.const import CONF_API_KEY, CONF_HOST, CONF_PORT
|
from homeassistant.const import CONF_API_KEY, CONF_HOST, CONF_PORT
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import callback
|
||||||
from homeassistant.helpers import aiohttp_client
|
from homeassistant.helpers import aiohttp_client
|
||||||
from homeassistant.helpers.service_info.hassio import HassioServiceInfo
|
from homeassistant.helpers.service_info.hassio import HassioServiceInfo
|
||||||
from homeassistant.helpers.service_info.ssdp import ATTR_UPNP_SERIAL, SsdpServiceInfo
|
from homeassistant.helpers.service_info.ssdp import ATTR_UPNP_SERIAL, SsdpServiceInfo
|
||||||
@ -51,15 +51,6 @@ CONF_SERIAL = "serial"
|
|||||||
CONF_MANUAL_INPUT = "Manually define gateway"
|
CONF_MANUAL_INPUT = "Manually define gateway"
|
||||||
|
|
||||||
|
|
||||||
@callback
|
|
||||||
def get_master_hub(hass: HomeAssistant) -> DeconzHub:
|
|
||||||
"""Return the gateway which is marked as master."""
|
|
||||||
for hub in hass.data[DOMAIN].values():
|
|
||||||
if hub.master:
|
|
||||||
return cast(DeconzHub, hub)
|
|
||||||
raise ValueError
|
|
||||||
|
|
||||||
|
|
||||||
class DeconzFlowHandler(ConfigFlow, domain=DOMAIN):
|
class DeconzFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||||
"""Handle a deCONZ config flow."""
|
"""Handle a deCONZ config flow."""
|
||||||
|
|
||||||
|
@ -12,9 +12,9 @@ from homeassistant.helpers import (
|
|||||||
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
|
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
|
||||||
from homeassistant.util.read_only_dict import ReadOnlyDict
|
from homeassistant.util.read_only_dict import ReadOnlyDict
|
||||||
|
|
||||||
from .config_flow import get_master_hub
|
|
||||||
from .const import CONF_BRIDGE_ID, DOMAIN, LOGGER
|
from .const import CONF_BRIDGE_ID, DOMAIN, LOGGER
|
||||||
from .hub import DeconzHub
|
from .hub import DeconzHub
|
||||||
|
from .util import get_master_hub
|
||||||
|
|
||||||
DECONZ_SERVICES = "deconz_services"
|
DECONZ_SERVICES = "deconz_services"
|
||||||
|
|
||||||
|
@ -2,9 +2,24 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from homeassistant.core import HomeAssistant, callback
|
||||||
|
|
||||||
|
from .const import DOMAIN
|
||||||
|
from .hub import DeconzHub
|
||||||
|
|
||||||
|
|
||||||
def serial_from_unique_id(unique_id: str | None) -> str | None:
|
def serial_from_unique_id(unique_id: str | None) -> str | None:
|
||||||
"""Get a device serial number from a unique ID, if possible."""
|
"""Get a device serial number from a unique ID, if possible."""
|
||||||
if not unique_id or unique_id.count(":") != 7:
|
if not unique_id or unique_id.count(":") != 7:
|
||||||
return None
|
return None
|
||||||
return unique_id.partition("-")[0]
|
return unique_id.partition("-")[0]
|
||||||
|
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def get_master_hub(hass: HomeAssistant) -> DeconzHub:
|
||||||
|
"""Return the gateway which is marked as master."""
|
||||||
|
hub: DeconzHub
|
||||||
|
for hub in hass.data[DOMAIN].values():
|
||||||
|
if hub.master:
|
||||||
|
return hub
|
||||||
|
raise ValueError
|
||||||
|
@ -12,7 +12,7 @@ from homeassistant.exceptions import ConfigEntryNotReady
|
|||||||
from homeassistant.helpers import entity_registry as er
|
from homeassistant.helpers import entity_registry as er
|
||||||
from homeassistant.helpers.httpx_client import get_async_client
|
from homeassistant.helpers.httpx_client import get_async_client
|
||||||
|
|
||||||
from .config_flow import (
|
from .const import (
|
||||||
CONF_SHOW_ALL_SOURCES,
|
CONF_SHOW_ALL_SOURCES,
|
||||||
CONF_UPDATE_AUDYSSEY,
|
CONF_UPDATE_AUDYSSEY,
|
||||||
CONF_USE_TELNET,
|
CONF_USE_TELNET,
|
||||||
@ -24,21 +24,18 @@ from .config_flow import (
|
|||||||
DEFAULT_USE_TELNET,
|
DEFAULT_USE_TELNET,
|
||||||
DEFAULT_ZONE2,
|
DEFAULT_ZONE2,
|
||||||
DEFAULT_ZONE3,
|
DEFAULT_ZONE3,
|
||||||
DOMAIN,
|
|
||||||
)
|
)
|
||||||
from .receiver import ConnectDenonAVR
|
from .receiver import ConnectDenonAVR
|
||||||
|
|
||||||
CONF_RECEIVER = "receiver"
|
|
||||||
UNDO_UPDATE_LISTENER = "undo_update_listener"
|
|
||||||
PLATFORMS = [Platform.MEDIA_PLAYER]
|
PLATFORMS = [Platform.MEDIA_PLAYER]
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
type DenonavrConfigEntry = ConfigEntry[DenonAVR]
|
||||||
|
|
||||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|
||||||
|
async def async_setup_entry(hass: HomeAssistant, entry: DenonavrConfigEntry) -> bool:
|
||||||
"""Set up the denonavr components from a config entry."""
|
"""Set up the denonavr components from a config entry."""
|
||||||
hass.data.setdefault(DOMAIN, {})
|
|
||||||
|
|
||||||
# Connect to receiver
|
# Connect to receiver
|
||||||
connect_denonavr = ConnectDenonAVR(
|
connect_denonavr = ConnectDenonAVR(
|
||||||
entry.data[CONF_HOST],
|
entry.data[CONF_HOST],
|
||||||
@ -56,12 +53,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
raise ConfigEntryNotReady from ex
|
raise ConfigEntryNotReady from ex
|
||||||
receiver = connect_denonavr.receiver
|
receiver = connect_denonavr.receiver
|
||||||
|
|
||||||
undo_listener = entry.add_update_listener(update_listener)
|
entry.async_on_unload(entry.add_update_listener(update_listener))
|
||||||
|
|
||||||
hass.data[DOMAIN][entry.entry_id] = {
|
entry.runtime_data = receiver
|
||||||
CONF_RECEIVER: receiver,
|
|
||||||
UNDO_UPDATE_LISTENER: undo_listener,
|
|
||||||
}
|
|
||||||
|
|
||||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||||
use_telnet = entry.options.get(CONF_USE_TELNET, DEFAULT_USE_TELNET)
|
use_telnet = entry.options.get(CONF_USE_TELNET, DEFAULT_USE_TELNET)
|
||||||
@ -79,18 +73,18 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
|
async def async_unload_entry(
|
||||||
|
hass: HomeAssistant, config_entry: DenonavrConfigEntry
|
||||||
|
) -> bool:
|
||||||
"""Unload a config entry."""
|
"""Unload a config entry."""
|
||||||
unload_ok = await hass.config_entries.async_unload_platforms(
|
unload_ok = await hass.config_entries.async_unload_platforms(
|
||||||
config_entry, PLATFORMS
|
config_entry, PLATFORMS
|
||||||
)
|
)
|
||||||
|
|
||||||
if config_entry.options.get(CONF_USE_TELNET, DEFAULT_USE_TELNET):
|
if config_entry.options.get(CONF_USE_TELNET, DEFAULT_USE_TELNET):
|
||||||
receiver: DenonAVR = hass.data[DOMAIN][config_entry.entry_id][CONF_RECEIVER]
|
receiver = config_entry.runtime_data
|
||||||
await receiver.async_telnet_disconnect()
|
await receiver.async_telnet_disconnect()
|
||||||
|
|
||||||
hass.data[DOMAIN][config_entry.entry_id][UNDO_UPDATE_LISTENER]()
|
|
||||||
|
|
||||||
# Remove zone2 and zone3 entities if needed
|
# Remove zone2 and zone3 entities if needed
|
||||||
entity_registry = er.async_get(hass)
|
entity_registry = er.async_get(hass)
|
||||||
entries = er.async_entries_for_config_entry(entity_registry, config_entry.entry_id)
|
entries = er.async_entries_for_config_entry(entity_registry, config_entry.entry_id)
|
||||||
@ -105,12 +99,11 @@ async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) ->
|
|||||||
entity_registry.async_remove(entry.entity_id)
|
entity_registry.async_remove(entry.entity_id)
|
||||||
_LOGGER.debug("Removing zone3 from DenonAvr")
|
_LOGGER.debug("Removing zone3 from DenonAvr")
|
||||||
|
|
||||||
if unload_ok:
|
|
||||||
hass.data[DOMAIN].pop(config_entry.entry_id)
|
|
||||||
|
|
||||||
return unload_ok
|
return unload_ok
|
||||||
|
|
||||||
|
|
||||||
async def update_listener(hass: HomeAssistant, config_entry: ConfigEntry) -> None:
|
async def update_listener(
|
||||||
|
hass: HomeAssistant, config_entry: DenonavrConfigEntry
|
||||||
|
) -> None:
|
||||||
"""Handle options update."""
|
"""Handle options update."""
|
||||||
await hass.config_entries.async_reload(config_entry.entry_id)
|
await hass.config_entries.async_reload(config_entry.entry_id)
|
||||||
|
@ -10,12 +10,7 @@ import denonavr
|
|||||||
from denonavr.exceptions import AvrNetworkError, AvrTimoutError
|
from denonavr.exceptions import AvrNetworkError, AvrTimoutError
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.config_entries import (
|
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult, OptionsFlow
|
||||||
ConfigEntry,
|
|
||||||
ConfigFlow,
|
|
||||||
ConfigFlowResult,
|
|
||||||
OptionsFlow,
|
|
||||||
)
|
|
||||||
from homeassistant.const import CONF_HOST, CONF_MODEL, CONF_TYPE
|
from homeassistant.const import CONF_HOST, CONF_MODEL, CONF_TYPE
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
from homeassistant.helpers.httpx_client import get_async_client
|
from homeassistant.helpers.httpx_client import get_async_client
|
||||||
@ -27,29 +22,30 @@ from homeassistant.helpers.service_info.ssdp import (
|
|||||||
SsdpServiceInfo,
|
SsdpServiceInfo,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from . import DenonavrConfigEntry
|
||||||
|
from .const import (
|
||||||
|
CONF_MANUFACTURER,
|
||||||
|
CONF_SERIAL_NUMBER,
|
||||||
|
CONF_SHOW_ALL_SOURCES,
|
||||||
|
CONF_UPDATE_AUDYSSEY,
|
||||||
|
CONF_USE_TELNET,
|
||||||
|
CONF_ZONE2,
|
||||||
|
CONF_ZONE3,
|
||||||
|
DEFAULT_SHOW_SOURCES,
|
||||||
|
DEFAULT_TIMEOUT,
|
||||||
|
DEFAULT_UPDATE_AUDYSSEY,
|
||||||
|
DEFAULT_USE_TELNET,
|
||||||
|
DEFAULT_ZONE2,
|
||||||
|
DEFAULT_ZONE3,
|
||||||
|
DOMAIN,
|
||||||
|
)
|
||||||
from .receiver import ConnectDenonAVR
|
from .receiver import ConnectDenonAVR
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
DOMAIN = "denonavr"
|
|
||||||
|
|
||||||
SUPPORTED_MANUFACTURERS = ["Denon", "DENON", "DENON PROFESSIONAL", "Marantz"]
|
SUPPORTED_MANUFACTURERS = ["Denon", "DENON", "DENON PROFESSIONAL", "Marantz"]
|
||||||
IGNORED_MODELS = ["HEOS 1", "HEOS 3", "HEOS 5", "HEOS 7"]
|
IGNORED_MODELS = ["HEOS 1", "HEOS 3", "HEOS 5", "HEOS 7"]
|
||||||
|
|
||||||
CONF_SHOW_ALL_SOURCES = "show_all_sources"
|
|
||||||
CONF_ZONE2 = "zone2"
|
|
||||||
CONF_ZONE3 = "zone3"
|
|
||||||
CONF_MANUFACTURER = "manufacturer"
|
|
||||||
CONF_SERIAL_NUMBER = "serial_number"
|
|
||||||
CONF_UPDATE_AUDYSSEY = "update_audyssey"
|
|
||||||
CONF_USE_TELNET = "use_telnet"
|
|
||||||
|
|
||||||
DEFAULT_SHOW_SOURCES = False
|
|
||||||
DEFAULT_TIMEOUT = 5
|
|
||||||
DEFAULT_ZONE2 = False
|
|
||||||
DEFAULT_ZONE3 = False
|
|
||||||
DEFAULT_UPDATE_AUDYSSEY = False
|
|
||||||
DEFAULT_USE_TELNET = False
|
|
||||||
DEFAULT_USE_TELNET_NEW_INSTALL = True
|
DEFAULT_USE_TELNET_NEW_INSTALL = True
|
||||||
|
|
||||||
CONFIG_SCHEMA = vol.Schema({vol.Optional(CONF_HOST): str})
|
CONFIG_SCHEMA = vol.Schema({vol.Optional(CONF_HOST): str})
|
||||||
@ -118,7 +114,7 @@ class DenonAvrFlowHandler(ConfigFlow, domain=DOMAIN):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
@callback
|
@callback
|
||||||
def async_get_options_flow(
|
def async_get_options_flow(
|
||||||
config_entry: ConfigEntry,
|
config_entry: DenonavrConfigEntry,
|
||||||
) -> OptionsFlowHandler:
|
) -> OptionsFlowHandler:
|
||||||
"""Get the options flow."""
|
"""Get the options flow."""
|
||||||
return OptionsFlowHandler()
|
return OptionsFlowHandler()
|
||||||
|
19
homeassistant/components/denonavr/const.py
Normal file
19
homeassistant/components/denonavr/const.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
"""Constants for Denon AVR."""
|
||||||
|
|
||||||
|
DOMAIN = "denonavr"
|
||||||
|
|
||||||
|
|
||||||
|
CONF_SHOW_ALL_SOURCES = "show_all_sources"
|
||||||
|
CONF_ZONE2 = "zone2"
|
||||||
|
CONF_ZONE3 = "zone3"
|
||||||
|
CONF_MANUFACTURER = "manufacturer"
|
||||||
|
CONF_SERIAL_NUMBER = "serial_number"
|
||||||
|
CONF_UPDATE_AUDYSSEY = "update_audyssey"
|
||||||
|
CONF_USE_TELNET = "use_telnet"
|
||||||
|
|
||||||
|
DEFAULT_SHOW_SOURCES = False
|
||||||
|
DEFAULT_TIMEOUT = 5
|
||||||
|
DEFAULT_ZONE2 = False
|
||||||
|
DEFAULT_ZONE3 = False
|
||||||
|
DEFAULT_UPDATE_AUDYSSEY = False
|
||||||
|
DEFAULT_USE_TELNET = False
|
@ -35,18 +35,16 @@ from homeassistant.components.media_player import (
|
|||||||
MediaPlayerState,
|
MediaPlayerState,
|
||||||
MediaType,
|
MediaType,
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.const import ATTR_COMMAND, CONF_HOST, CONF_MODEL, CONF_TYPE
|
||||||
from homeassistant.const import ATTR_COMMAND, CONF_HOST, CONF_MODEL
|
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers import config_validation as cv, entity_platform
|
from homeassistant.helpers import config_validation as cv, entity_platform
|
||||||
from homeassistant.helpers.device_registry import DeviceInfo
|
from homeassistant.helpers.device_registry import DeviceInfo
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
from . import CONF_RECEIVER
|
from . import DenonavrConfigEntry
|
||||||
from .config_flow import (
|
from .const import (
|
||||||
CONF_MANUFACTURER,
|
CONF_MANUFACTURER,
|
||||||
CONF_SERIAL_NUMBER,
|
CONF_SERIAL_NUMBER,
|
||||||
CONF_TYPE,
|
|
||||||
CONF_UPDATE_AUDYSSEY,
|
CONF_UPDATE_AUDYSSEY,
|
||||||
DEFAULT_UPDATE_AUDYSSEY,
|
DEFAULT_UPDATE_AUDYSSEY,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
@ -110,13 +108,12 @@ DENON_STATE_MAPPING = {
|
|||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
config_entry: ConfigEntry,
|
config_entry: DenonavrConfigEntry,
|
||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the DenonAVR receiver from a config entry."""
|
"""Set up the DenonAVR receiver from a config entry."""
|
||||||
entities = []
|
entities = []
|
||||||
data = hass.data[DOMAIN][config_entry.entry_id]
|
receiver = config_entry.runtime_data
|
||||||
receiver = data[CONF_RECEIVER]
|
|
||||||
update_audyssey = config_entry.options.get(
|
update_audyssey = config_entry.options.get(
|
||||||
CONF_UPDATE_AUDYSSEY, DEFAULT_UPDATE_AUDYSSEY
|
CONF_UPDATE_AUDYSSEY, DEFAULT_UPDATE_AUDYSSEY
|
||||||
)
|
)
|
||||||
@ -253,7 +250,7 @@ class DenonDevice(MediaPlayerEntity):
|
|||||||
self,
|
self,
|
||||||
receiver: DenonAVR,
|
receiver: DenonAVR,
|
||||||
unique_id: str,
|
unique_id: str,
|
||||||
config_entry: ConfigEntry,
|
config_entry: DenonavrConfigEntry,
|
||||||
update_audyssey: bool,
|
update_audyssey: bool,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize the device."""
|
"""Initialize the device."""
|
||||||
|
@ -1,24 +1,16 @@
|
|||||||
"""The Dexcom integration."""
|
"""The Dexcom integration."""
|
||||||
|
|
||||||
from datetime import timedelta
|
from pydexcom import AccountError, Dexcom, SessionError
|
||||||
import logging
|
|
||||||
|
|
||||||
from pydexcom import AccountError, Dexcom, GlucoseReading, SessionError
|
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
|
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.exceptions import ConfigEntryNotReady
|
from homeassistant.exceptions import ConfigEntryNotReady
|
||||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
|
||||||
|
|
||||||
from .const import CONF_SERVER, DOMAIN, PLATFORMS, SERVER_OUS
|
from .const import CONF_SERVER, PLATFORMS, SERVER_OUS
|
||||||
|
from .coordinator import DexcomConfigEntry, DexcomCoordinator
|
||||||
_LOGGER = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
SCAN_INTERVAL = timedelta(seconds=180)
|
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_setup_entry(hass: HomeAssistant, entry: DexcomConfigEntry) -> bool:
|
||||||
"""Set up Dexcom from a config entry."""
|
"""Set up Dexcom from a config entry."""
|
||||||
try:
|
try:
|
||||||
dexcom = await hass.async_add_executor_job(
|
dexcom = await hass.async_add_executor_job(
|
||||||
@ -32,31 +24,16 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
except SessionError as error:
|
except SessionError as error:
|
||||||
raise ConfigEntryNotReady from error
|
raise ConfigEntryNotReady from error
|
||||||
|
|
||||||
async def async_update_data():
|
coordinator = DexcomCoordinator(hass, entry=entry, dexcom=dexcom)
|
||||||
try:
|
|
||||||
return await hass.async_add_executor_job(dexcom.get_current_glucose_reading)
|
|
||||||
except SessionError as error:
|
|
||||||
raise UpdateFailed(error) from error
|
|
||||||
|
|
||||||
coordinator = DataUpdateCoordinator[GlucoseReading](
|
|
||||||
hass,
|
|
||||||
_LOGGER,
|
|
||||||
config_entry=entry,
|
|
||||||
name=DOMAIN,
|
|
||||||
update_method=async_update_data,
|
|
||||||
update_interval=SCAN_INTERVAL,
|
|
||||||
)
|
|
||||||
await coordinator.async_config_entry_first_refresh()
|
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)
|
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_unload_entry(hass: HomeAssistant, entry: DexcomConfigEntry) -> bool:
|
||||||
"""Unload a config entry."""
|
"""Unload a config entry."""
|
||||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||||
hass.data[DOMAIN].pop(entry.entry_id)
|
|
||||||
return unload_ok
|
|
||||||
|
44
homeassistant/components/dexcom/coordinator.py
Normal file
44
homeassistant/components/dexcom/coordinator.py
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
"""Coordinator for the Dexcom integration."""
|
||||||
|
|
||||||
|
from datetime import timedelta
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from pydexcom import Dexcom, GlucoseReading
|
||||||
|
|
||||||
|
from homeassistant.config_entries import ConfigEntry
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||||
|
|
||||||
|
from .const import DOMAIN
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
_SCAN_INTERVAL = timedelta(seconds=180)
|
||||||
|
|
||||||
|
type DexcomConfigEntry = ConfigEntry[DexcomCoordinator]
|
||||||
|
|
||||||
|
|
||||||
|
class DexcomCoordinator(DataUpdateCoordinator[GlucoseReading]):
|
||||||
|
"""Dexcom Coordinator."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
hass: HomeAssistant,
|
||||||
|
entry: DexcomConfigEntry,
|
||||||
|
dexcom: Dexcom,
|
||||||
|
) -> None:
|
||||||
|
"""Initialize the coordinator."""
|
||||||
|
super().__init__(
|
||||||
|
hass,
|
||||||
|
_LOGGER,
|
||||||
|
config_entry=entry,
|
||||||
|
name=DOMAIN,
|
||||||
|
update_interval=_SCAN_INTERVAL,
|
||||||
|
)
|
||||||
|
self.dexcom = dexcom
|
||||||
|
|
||||||
|
async def _async_update_data(self) -> GlucoseReading:
|
||||||
|
"""Fetch data from API endpoint."""
|
||||||
|
return await self.hass.async_add_executor_job(
|
||||||
|
self.dexcom.get_current_glucose_reading
|
||||||
|
)
|
@ -2,20 +2,15 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from pydexcom import GlucoseReading
|
|
||||||
|
|
||||||
from homeassistant.components.sensor import SensorDeviceClass, SensorEntity
|
from homeassistant.components.sensor import SensorDeviceClass, SensorEntity
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.const import CONF_USERNAME, UnitOfBloodGlucoseConcentration
|
from homeassistant.const import CONF_USERNAME, UnitOfBloodGlucoseConcentration
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.device_registry import DeviceInfo
|
from homeassistant.helpers.device_registry import DeviceInfo
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.update_coordinator import (
|
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||||
CoordinatorEntity,
|
|
||||||
DataUpdateCoordinator,
|
|
||||||
)
|
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
|
from .coordinator import DexcomConfigEntry, DexcomCoordinator
|
||||||
|
|
||||||
TRENDS = {
|
TRENDS = {
|
||||||
1: "rising_quickly",
|
1: "rising_quickly",
|
||||||
@ -30,11 +25,11 @@ TRENDS = {
|
|||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
config_entry: ConfigEntry,
|
config_entry: DexcomConfigEntry,
|
||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the Dexcom sensors."""
|
"""Set up the Dexcom sensors."""
|
||||||
coordinator = hass.data[DOMAIN][config_entry.entry_id]
|
coordinator = config_entry.runtime_data
|
||||||
username = config_entry.data[CONF_USERNAME]
|
username = config_entry.data[CONF_USERNAME]
|
||||||
async_add_entities(
|
async_add_entities(
|
||||||
[
|
[
|
||||||
@ -44,16 +39,14 @@ async def async_setup_entry(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class DexcomSensorEntity(
|
class DexcomSensorEntity(CoordinatorEntity[DexcomCoordinator], SensorEntity):
|
||||||
CoordinatorEntity[DataUpdateCoordinator[GlucoseReading]], SensorEntity
|
|
||||||
):
|
|
||||||
"""Base Dexcom sensor entity."""
|
"""Base Dexcom sensor entity."""
|
||||||
|
|
||||||
_attr_has_entity_name = True
|
_attr_has_entity_name = True
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
coordinator: DataUpdateCoordinator[GlucoseReading],
|
coordinator: DexcomCoordinator,
|
||||||
username: str,
|
username: str,
|
||||||
entry_id: str,
|
entry_id: str,
|
||||||
key: str,
|
key: str,
|
||||||
@ -78,7 +71,7 @@ class DexcomGlucoseValueSensor(DexcomSensorEntity):
|
|||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
coordinator: DataUpdateCoordinator,
|
coordinator: DexcomCoordinator,
|
||||||
username: str,
|
username: str,
|
||||||
entry_id: str,
|
entry_id: str,
|
||||||
) -> None:
|
) -> None:
|
||||||
@ -101,7 +94,7 @@ class DexcomGlucoseTrendSensor(DexcomSensorEntity):
|
|||||||
_attr_options = list(TRENDS.values())
|
_attr_options = list(TRENDS.values())
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, coordinator: DataUpdateCoordinator, username: str, entry_id: str
|
self, coordinator: DexcomCoordinator, username: str, entry_id: str
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize the sensor."""
|
"""Initialize the sensor."""
|
||||||
super().__init__(coordinator, username, entry_id, "trend")
|
super().__init__(coordinator, username, entry_id, "trend")
|
||||||
|
@ -12,13 +12,14 @@ from homeassistant.core import HomeAssistant
|
|||||||
from homeassistant.exceptions import ConfigEntryNotReady
|
from homeassistant.exceptions import ConfigEntryNotReady
|
||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
|
|
||||||
from .const import DOMAIN
|
|
||||||
|
|
||||||
PLATFORMS = [Platform.MEDIA_PLAYER, Platform.REMOTE]
|
PLATFORMS = [Platform.MEDIA_PLAYER, Platform.REMOTE]
|
||||||
SCAN_INTERVAL = timedelta(seconds=30)
|
SCAN_INTERVAL = timedelta(seconds=30)
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
type DirecTVConfigEntry = ConfigEntry[DIRECTV]
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_entry(hass: HomeAssistant, entry: DirecTVConfigEntry) -> bool:
|
||||||
"""Set up DirecTV from a config entry."""
|
"""Set up DirecTV from a config entry."""
|
||||||
dtv = DIRECTV(entry.data[CONF_HOST], session=async_get_clientsession(hass))
|
dtv = DIRECTV(entry.data[CONF_HOST], session=async_get_clientsession(hass))
|
||||||
|
|
||||||
@ -27,18 +28,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
except DIRECTVError as err:
|
except DIRECTVError as err:
|
||||||
raise ConfigEntryNotReady from err
|
raise ConfigEntryNotReady from err
|
||||||
|
|
||||||
hass.data.setdefault(DOMAIN, {})
|
entry.runtime_data = dtv
|
||||||
hass.data[DOMAIN][entry.entry_id] = dtv
|
|
||||||
|
|
||||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_unload_entry(hass: HomeAssistant, entry: DirecTVConfigEntry) -> bool:
|
||||||
"""Unload a config entry."""
|
"""Unload a config entry."""
|
||||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||||
if unload_ok:
|
|
||||||
hass.data[DOMAIN].pop(entry.entry_id)
|
|
||||||
|
|
||||||
return unload_ok
|
|
||||||
|
@ -14,17 +14,16 @@ from homeassistant.components.media_player import (
|
|||||||
MediaPlayerState,
|
MediaPlayerState,
|
||||||
MediaType,
|
MediaType,
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.util import dt as dt_util
|
from homeassistant.util import dt as dt_util
|
||||||
|
|
||||||
|
from . import DirecTVConfigEntry
|
||||||
from .const import (
|
from .const import (
|
||||||
ATTR_MEDIA_CURRENTLY_RECORDING,
|
ATTR_MEDIA_CURRENTLY_RECORDING,
|
||||||
ATTR_MEDIA_RATING,
|
ATTR_MEDIA_RATING,
|
||||||
ATTR_MEDIA_RECORDED,
|
ATTR_MEDIA_RECORDED,
|
||||||
ATTR_MEDIA_START_TIME,
|
ATTR_MEDIA_START_TIME,
|
||||||
DOMAIN,
|
|
||||||
)
|
)
|
||||||
from .entity import DIRECTVEntity
|
from .entity import DIRECTVEntity
|
||||||
|
|
||||||
@ -55,11 +54,11 @@ SUPPORT_DTV_CLIENT = (
|
|||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
entry: ConfigEntry,
|
entry: DirecTVConfigEntry,
|
||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the DirecTV config entry."""
|
"""Set up the DirecTV config entry."""
|
||||||
dtv = hass.data[DOMAIN][entry.entry_id]
|
dtv = entry.runtime_data
|
||||||
|
|
||||||
async_add_entities(
|
async_add_entities(
|
||||||
(
|
(
|
||||||
|
@ -10,11 +10,10 @@ from typing import Any
|
|||||||
from directv import DIRECTV, DIRECTVError
|
from directv import DIRECTV, DIRECTVError
|
||||||
|
|
||||||
from homeassistant.components.remote import ATTR_NUM_REPEATS, RemoteEntity
|
from homeassistant.components.remote import ATTR_NUM_REPEATS, RemoteEntity
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
from .const import DOMAIN
|
from . import DirecTVConfigEntry
|
||||||
from .entity import DIRECTVEntity
|
from .entity import DIRECTVEntity
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
@ -24,11 +23,11 @@ SCAN_INTERVAL = timedelta(minutes=2)
|
|||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
entry: ConfigEntry,
|
entry: DirecTVConfigEntry,
|
||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Load DirecTV remote based on a config entry."""
|
"""Load DirecTV remote based on a config entry."""
|
||||||
dtv = hass.data[DOMAIN][entry.entry_id]
|
dtv = entry.runtime_data
|
||||||
|
|
||||||
async_add_entities(
|
async_add_entities(
|
||||||
(
|
(
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
"documentation": "https://www.home-assistant.io/integrations/dlna_dmr",
|
"documentation": "https://www.home-assistant.io/integrations/dlna_dmr",
|
||||||
"iot_class": "local_push",
|
"iot_class": "local_push",
|
||||||
"loggers": ["async_upnp_client"],
|
"loggers": ["async_upnp_client"],
|
||||||
"requirements": ["async-upnp-client==0.42.0", "getmac==0.9.5"],
|
"requirements": ["async-upnp-client==0.43.0", "getmac==0.9.5"],
|
||||||
"ssdp": [
|
"ssdp": [
|
||||||
{
|
{
|
||||||
"deviceType": "urn:schemas-upnp-org:device:MediaRenderer:1",
|
"deviceType": "urn:schemas-upnp-org:device:MediaRenderer:1",
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
"dependencies": ["ssdp"],
|
"dependencies": ["ssdp"],
|
||||||
"documentation": "https://www.home-assistant.io/integrations/dlna_dms",
|
"documentation": "https://www.home-assistant.io/integrations/dlna_dms",
|
||||||
"iot_class": "local_polling",
|
"iot_class": "local_polling",
|
||||||
"requirements": ["async-upnp-client==0.42.0"],
|
"requirements": ["async-upnp-client==0.43.0"],
|
||||||
"ssdp": [
|
"ssdp": [
|
||||||
{
|
{
|
||||||
"deviceType": "urn:schemas-upnp-org:device:MediaServer:1",
|
"deviceType": "urn:schemas-upnp-org:device:MediaServer:1",
|
||||||
|
@ -2,30 +2,24 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from datetime import timedelta
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from py_dormakaba_dkey import DKEYLock
|
from py_dormakaba_dkey import DKEYLock
|
||||||
from py_dormakaba_dkey.errors import DKEY_EXCEPTIONS, NotAssociated
|
|
||||||
from py_dormakaba_dkey.models import AssociationData
|
from py_dormakaba_dkey.models import AssociationData
|
||||||
|
|
||||||
from homeassistant.components import bluetooth
|
from homeassistant.components import bluetooth
|
||||||
from homeassistant.components.bluetooth.match import ADDRESS, BluetoothCallbackMatcher
|
from homeassistant.components.bluetooth.match import ADDRESS, BluetoothCallbackMatcher
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.const import CONF_ADDRESS, EVENT_HOMEASSISTANT_STOP, Platform
|
from homeassistant.const import CONF_ADDRESS, EVENT_HOMEASSISTANT_STOP, Platform
|
||||||
from homeassistant.core import Event, HomeAssistant, callback
|
from homeassistant.core import Event, HomeAssistant, callback
|
||||||
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
|
from homeassistant.exceptions import ConfigEntryNotReady
|
||||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
|
||||||
|
|
||||||
from .const import CONF_ASSOCIATION_DATA, DOMAIN, UPDATE_SECONDS
|
from .const import CONF_ASSOCIATION_DATA
|
||||||
from .models import DormakabaDkeyData
|
from .coordinator import DormakabaDkeyConfigEntry, DormakabaDkeyCoordinator
|
||||||
|
|
||||||
PLATFORMS: list[Platform] = [Platform.BINARY_SENSOR, Platform.LOCK, Platform.SENSOR]
|
PLATFORMS: list[Platform] = [Platform.BINARY_SENSOR, Platform.LOCK, Platform.SENSOR]
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
async def async_setup_entry(
|
||||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
hass: HomeAssistant, entry: DormakabaDkeyConfigEntry
|
||||||
|
) -> bool:
|
||||||
"""Set up Dormakaba dKey from a config entry."""
|
"""Set up Dormakaba dKey from a config entry."""
|
||||||
address: str = entry.data[CONF_ADDRESS]
|
address: str = entry.data[CONF_ADDRESS]
|
||||||
ble_device = bluetooth.async_ble_device_from_address(hass, address.upper(), True)
|
ble_device = bluetooth.async_ble_device_from_address(hass, address.upper(), True)
|
||||||
@ -56,29 +50,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
async def _async_update() -> None:
|
coordinator = DormakabaDkeyCoordinator(hass, entry, lock)
|
||||||
"""Update the device state."""
|
|
||||||
try:
|
|
||||||
await lock.update()
|
|
||||||
await lock.disconnect()
|
|
||||||
except NotAssociated as ex:
|
|
||||||
raise ConfigEntryAuthFailed("Not associated") from ex
|
|
||||||
except DKEY_EXCEPTIONS as ex:
|
|
||||||
raise UpdateFailed(str(ex)) from ex
|
|
||||||
|
|
||||||
coordinator = DataUpdateCoordinator(
|
|
||||||
hass,
|
|
||||||
_LOGGER,
|
|
||||||
config_entry=entry,
|
|
||||||
name=lock.name,
|
|
||||||
update_method=_async_update,
|
|
||||||
update_interval=timedelta(seconds=UPDATE_SECONDS),
|
|
||||||
)
|
|
||||||
await coordinator.async_config_entry_first_refresh()
|
await coordinator.async_config_entry_first_refresh()
|
||||||
|
|
||||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = DormakabaDkeyData(
|
entry.runtime_data = coordinator
|
||||||
lock, coordinator
|
|
||||||
)
|
|
||||||
|
|
||||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||||
|
|
||||||
@ -89,13 +64,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
entry.async_on_unload(
|
entry.async_on_unload(
|
||||||
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _async_stop)
|
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _async_stop)
|
||||||
)
|
)
|
||||||
|
entry.async_on_unload(coordinator.lock.disconnect)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_unload_entry(
|
||||||
|
hass: HomeAssistant, entry: DormakabaDkeyConfigEntry
|
||||||
|
) -> bool:
|
||||||
"""Unload a config entry."""
|
"""Unload a config entry."""
|
||||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||||
data: DormakabaDkeyData = hass.data[DOMAIN].pop(entry.entry_id)
|
|
||||||
await data.lock.disconnect()
|
|
||||||
|
|
||||||
return unload_ok
|
|
||||||
|
@ -5,7 +5,6 @@ from __future__ import annotations
|
|||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
from py_dormakaba_dkey import DKEYLock
|
|
||||||
from py_dormakaba_dkey.commands import DoorPosition, Notifications, UnlockStatus
|
from py_dormakaba_dkey.commands import DoorPosition, Notifications, UnlockStatus
|
||||||
|
|
||||||
from homeassistant.components.binary_sensor import (
|
from homeassistant.components.binary_sensor import (
|
||||||
@ -13,14 +12,11 @@ from homeassistant.components.binary_sensor import (
|
|||||||
BinarySensorEntity,
|
BinarySensorEntity,
|
||||||
BinarySensorEntityDescription,
|
BinarySensorEntityDescription,
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .coordinator import DormakabaDkeyConfigEntry, DormakabaDkeyCoordinator
|
||||||
from .entity import DormakabaDkeyEntity
|
from .entity import DormakabaDkeyEntity
|
||||||
from .models import DormakabaDkeyData
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True, kw_only=True)
|
@dataclass(frozen=True, kw_only=True)
|
||||||
@ -48,13 +44,13 @@ BINARY_SENSOR_DESCRIPTIONS = (
|
|||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
entry: ConfigEntry,
|
entry: DormakabaDkeyConfigEntry,
|
||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the binary sensor platform for Dormakaba dKey."""
|
"""Set up the binary sensor platform for Dormakaba dKey."""
|
||||||
data: DormakabaDkeyData = hass.data[DOMAIN][entry.entry_id]
|
coordinator = entry.runtime_data
|
||||||
async_add_entities(
|
async_add_entities(
|
||||||
DormakabaDkeyBinarySensor(data.coordinator, data.lock, description)
|
DormakabaDkeyBinarySensor(coordinator, description)
|
||||||
for description in BINARY_SENSOR_DESCRIPTIONS
|
for description in BINARY_SENSOR_DESCRIPTIONS
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -67,16 +63,15 @@ class DormakabaDkeyBinarySensor(DormakabaDkeyEntity, BinarySensorEntity):
|
|||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
coordinator: DataUpdateCoordinator[None],
|
coordinator: DormakabaDkeyCoordinator,
|
||||||
lock: DKEYLock,
|
|
||||||
description: DormakabaDkeyBinarySensorDescription,
|
description: DormakabaDkeyBinarySensorDescription,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize a Dormakaba dKey binary sensor."""
|
"""Initialize a Dormakaba dKey binary sensor."""
|
||||||
self.entity_description = description
|
self.entity_description = description
|
||||||
self._attr_unique_id = f"{lock.address}_{description.key}"
|
self._attr_unique_id = f"{coordinator.lock.address}_{description.key}"
|
||||||
super().__init__(coordinator, lock)
|
super().__init__(coordinator)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_update_attrs(self) -> None:
|
def _async_update_attrs(self) -> None:
|
||||||
"""Handle updating _attr values."""
|
"""Handle updating _attr values."""
|
||||||
self._attr_is_on = self.entity_description.is_on(self._lock.state)
|
self._attr_is_on = self.entity_description.is_on(self.coordinator.lock.state)
|
||||||
|
50
homeassistant/components/dormakaba_dkey/coordinator.py
Normal file
50
homeassistant/components/dormakaba_dkey/coordinator.py
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
"""Coordinator for the Dormakaba dKey integration."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from datetime import timedelta
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from py_dormakaba_dkey import DKEYLock
|
||||||
|
from py_dormakaba_dkey.errors import DKEY_EXCEPTIONS, NotAssociated
|
||||||
|
|
||||||
|
from homeassistant.config_entries import ConfigEntry
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.exceptions import ConfigEntryAuthFailed
|
||||||
|
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||||
|
|
||||||
|
from .const import UPDATE_SECONDS
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
type DormakabaDkeyConfigEntry = ConfigEntry[DormakabaDkeyCoordinator]
|
||||||
|
|
||||||
|
|
||||||
|
class DormakabaDkeyCoordinator(DataUpdateCoordinator[None]):
|
||||||
|
"""DormakabaDkey coordinator."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
hass: HomeAssistant,
|
||||||
|
entry: DormakabaDkeyConfigEntry,
|
||||||
|
lock: DKEYLock,
|
||||||
|
) -> None:
|
||||||
|
"""Initialize the coordinator."""
|
||||||
|
super().__init__(
|
||||||
|
hass,
|
||||||
|
_LOGGER,
|
||||||
|
config_entry=entry,
|
||||||
|
name=lock.name,
|
||||||
|
update_interval=timedelta(seconds=UPDATE_SECONDS),
|
||||||
|
)
|
||||||
|
self.lock = lock
|
||||||
|
|
||||||
|
async def _async_update_data(self) -> None:
|
||||||
|
"""Update the device state."""
|
||||||
|
try:
|
||||||
|
await self.lock.update()
|
||||||
|
await self.lock.disconnect()
|
||||||
|
except NotAssociated as ex:
|
||||||
|
raise ConfigEntryAuthFailed("Not associated") from ex
|
||||||
|
except DKEY_EXCEPTIONS as ex:
|
||||||
|
raise UpdateFailed(str(ex)) from ex
|
@ -4,29 +4,25 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import abc
|
import abc
|
||||||
|
|
||||||
from py_dormakaba_dkey import DKEYLock
|
|
||||||
from py_dormakaba_dkey.commands import Notifications
|
from py_dormakaba_dkey.commands import Notifications
|
||||||
|
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
from homeassistant.helpers import device_registry as dr
|
from homeassistant.helpers import device_registry as dr
|
||||||
from homeassistant.helpers.device_registry import DeviceInfo
|
from homeassistant.helpers.device_registry import DeviceInfo
|
||||||
from homeassistant.helpers.update_coordinator import (
|
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||||
CoordinatorEntity,
|
|
||||||
DataUpdateCoordinator,
|
from .coordinator import DormakabaDkeyCoordinator
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class DormakabaDkeyEntity(CoordinatorEntity[DataUpdateCoordinator[None]]):
|
class DormakabaDkeyEntity(CoordinatorEntity[DormakabaDkeyCoordinator]):
|
||||||
"""Dormakaba dKey base entity."""
|
"""Dormakaba dKey base entity."""
|
||||||
|
|
||||||
_attr_has_entity_name = True
|
_attr_has_entity_name = True
|
||||||
|
|
||||||
def __init__(
|
def __init__(self, coordinator: DormakabaDkeyCoordinator) -> None:
|
||||||
self, coordinator: DataUpdateCoordinator[None], lock: DKEYLock
|
|
||||||
) -> None:
|
|
||||||
"""Initialize a Dormakaba dKey entity."""
|
"""Initialize a Dormakaba dKey entity."""
|
||||||
super().__init__(coordinator)
|
super().__init__(coordinator)
|
||||||
self._lock = lock
|
lock = coordinator.lock
|
||||||
self._attr_device_info = DeviceInfo(
|
self._attr_device_info = DeviceInfo(
|
||||||
name=lock.device_info.device_name or lock.device_info.device_id,
|
name=lock.device_info.device_name or lock.device_info.device_id,
|
||||||
model="MTL 9291",
|
model="MTL 9291",
|
||||||
@ -53,5 +49,7 @@ class DormakabaDkeyEntity(CoordinatorEntity[DataUpdateCoordinator[None]]):
|
|||||||
|
|
||||||
async def async_added_to_hass(self) -> None:
|
async def async_added_to_hass(self) -> None:
|
||||||
"""Register callbacks."""
|
"""Register callbacks."""
|
||||||
self.async_on_remove(self._lock.register_callback(self._handle_state_update))
|
self.async_on_remove(
|
||||||
|
self.coordinator.lock.register_callback(self._handle_state_update)
|
||||||
|
)
|
||||||
return await super().async_added_to_hass()
|
return await super().async_added_to_hass()
|
||||||
|
@ -4,28 +4,23 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from py_dormakaba_dkey import DKEYLock
|
|
||||||
from py_dormakaba_dkey.commands import UnlockStatus
|
from py_dormakaba_dkey.commands import UnlockStatus
|
||||||
|
|
||||||
from homeassistant.components.lock import LockEntity
|
from homeassistant.components.lock import LockEntity
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .coordinator import DormakabaDkeyConfigEntry, DormakabaDkeyCoordinator
|
||||||
from .entity import DormakabaDkeyEntity
|
from .entity import DormakabaDkeyEntity
|
||||||
from .models import DormakabaDkeyData
|
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
entry: ConfigEntry,
|
entry: DormakabaDkeyConfigEntry,
|
||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the lock platform for Dormakaba dKey."""
|
"""Set up the lock platform for Dormakaba dKey."""
|
||||||
data: DormakabaDkeyData = hass.data[DOMAIN][entry.entry_id]
|
async_add_entities([DormakabaDkeyLock(entry.runtime_data)])
|
||||||
async_add_entities([DormakabaDkeyLock(data.coordinator, data.lock)])
|
|
||||||
|
|
||||||
|
|
||||||
class DormakabaDkeyLock(DormakabaDkeyEntity, LockEntity):
|
class DormakabaDkeyLock(DormakabaDkeyEntity, LockEntity):
|
||||||
@ -33,25 +28,23 @@ class DormakabaDkeyLock(DormakabaDkeyEntity, LockEntity):
|
|||||||
|
|
||||||
_attr_has_entity_name = True
|
_attr_has_entity_name = True
|
||||||
|
|
||||||
def __init__(
|
def __init__(self, coordinator: DormakabaDkeyCoordinator) -> None:
|
||||||
self, coordinator: DataUpdateCoordinator[None], lock: DKEYLock
|
|
||||||
) -> None:
|
|
||||||
"""Initialize a Dormakaba dKey lock."""
|
"""Initialize a Dormakaba dKey lock."""
|
||||||
self._attr_unique_id = lock.address
|
self._attr_unique_id = coordinator.lock.address
|
||||||
super().__init__(coordinator, lock)
|
super().__init__(coordinator)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_update_attrs(self) -> None:
|
def _async_update_attrs(self) -> None:
|
||||||
"""Handle updating _attr values."""
|
"""Handle updating _attr values."""
|
||||||
self._attr_is_locked = self._lock.state.unlock_status in (
|
self._attr_is_locked = self.coordinator.lock.state.unlock_status in (
|
||||||
UnlockStatus.LOCKED,
|
UnlockStatus.LOCKED,
|
||||||
UnlockStatus.SECURITY_LOCKED,
|
UnlockStatus.SECURITY_LOCKED,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_lock(self, **kwargs: Any) -> None:
|
async def async_lock(self, **kwargs: Any) -> None:
|
||||||
"""Lock the lock."""
|
"""Lock the lock."""
|
||||||
await self._lock.lock()
|
await self.coordinator.lock.lock()
|
||||||
|
|
||||||
async def async_unlock(self, **kwargs: Any) -> None:
|
async def async_unlock(self, **kwargs: Any) -> None:
|
||||||
"""Unlock the lock."""
|
"""Unlock the lock."""
|
||||||
await self._lock.unlock()
|
await self.coordinator.lock.unlock()
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
"""The Dormakaba dKey integration models."""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from dataclasses import dataclass
|
|
||||||
|
|
||||||
from py_dormakaba_dkey import DKEYLock
|
|
||||||
|
|
||||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class DormakabaDkeyData:
|
|
||||||
"""Data for the Dormakaba dKey integration."""
|
|
||||||
|
|
||||||
lock: DKEYLock
|
|
||||||
coordinator: DataUpdateCoordinator[None]
|
|
@ -2,23 +2,18 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from py_dormakaba_dkey import DKEYLock
|
|
||||||
|
|
||||||
from homeassistant.components.sensor import (
|
from homeassistant.components.sensor import (
|
||||||
SensorDeviceClass,
|
SensorDeviceClass,
|
||||||
SensorEntity,
|
SensorEntity,
|
||||||
SensorEntityDescription,
|
SensorEntityDescription,
|
||||||
SensorStateClass,
|
SensorStateClass,
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.const import PERCENTAGE
|
from homeassistant.const import PERCENTAGE
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .coordinator import DormakabaDkeyConfigEntry, DormakabaDkeyCoordinator
|
||||||
from .entity import DormakabaDkeyEntity
|
from .entity import DormakabaDkeyEntity
|
||||||
from .models import DormakabaDkeyData
|
|
||||||
|
|
||||||
BINARY_SENSOR_DESCRIPTIONS = (
|
BINARY_SENSOR_DESCRIPTIONS = (
|
||||||
SensorEntityDescription(
|
SensorEntityDescription(
|
||||||
@ -32,13 +27,13 @@ BINARY_SENSOR_DESCRIPTIONS = (
|
|||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
entry: ConfigEntry,
|
entry: DormakabaDkeyConfigEntry,
|
||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the lock platform for Dormakaba dKey."""
|
"""Set up the lock platform for Dormakaba dKey."""
|
||||||
data: DormakabaDkeyData = hass.data[DOMAIN][entry.entry_id]
|
coordinator = entry.runtime_data
|
||||||
async_add_entities(
|
async_add_entities(
|
||||||
DormakabaDkeySensor(data.coordinator, data.lock, description)
|
DormakabaDkeySensor(coordinator, description)
|
||||||
for description in BINARY_SENSOR_DESCRIPTIONS
|
for description in BINARY_SENSOR_DESCRIPTIONS
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -50,16 +45,17 @@ class DormakabaDkeySensor(DormakabaDkeyEntity, SensorEntity):
|
|||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
coordinator: DataUpdateCoordinator[None],
|
coordinator: DormakabaDkeyCoordinator,
|
||||||
lock: DKEYLock,
|
|
||||||
description: SensorEntityDescription,
|
description: SensorEntityDescription,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize a Dormakaba dKey binary sensor."""
|
"""Initialize a Dormakaba dKey binary sensor."""
|
||||||
self.entity_description = description
|
self.entity_description = description
|
||||||
self._attr_unique_id = f"{lock.address}_{description.key}"
|
self._attr_unique_id = f"{coordinator.lock.address}_{description.key}"
|
||||||
super().__init__(coordinator, lock)
|
super().__init__(coordinator)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_update_attrs(self) -> None:
|
def _async_update_attrs(self) -> None:
|
||||||
"""Handle updating _attr values."""
|
"""Handle updating _attr values."""
|
||||||
self._attr_native_value = getattr(self._lock, self.entity_description.key)
|
self._attr_native_value = getattr(
|
||||||
|
self.coordinator.lock, self.entity_description.key
|
||||||
|
)
|
||||||
|
@ -10,29 +10,21 @@ from homeassistant.config_entries import ConfigEntry
|
|||||||
from homeassistant.const import CONF_HOST, Platform
|
from homeassistant.const import CONF_HOST, Platform
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
from .const import DOMAIN
|
|
||||||
|
|
||||||
PLATFORMS: Final[list[Platform]] = [Platform.MEDIA_PLAYER]
|
PLATFORMS: Final[list[Platform]] = [Platform.MEDIA_PLAYER]
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
type DuneHDConfigEntry = ConfigEntry[DuneHDPlayer]
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_entry(hass: HomeAssistant, entry: DuneHDConfigEntry) -> bool:
|
||||||
"""Set up a config entry."""
|
"""Set up a config entry."""
|
||||||
host: str = entry.data[CONF_HOST]
|
entry.runtime_data = DuneHDPlayer(entry.data[CONF_HOST])
|
||||||
|
|
||||||
player = DuneHDPlayer(host)
|
|
||||||
|
|
||||||
hass.data.setdefault(DOMAIN, {})
|
|
||||||
hass.data[DOMAIN][entry.entry_id] = player
|
|
||||||
|
|
||||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_unload_entry(hass: HomeAssistant, entry: DuneHDConfigEntry) -> bool:
|
||||||
"""Unload a config entry."""
|
"""Unload a config entry."""
|
||||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||||
if unload_ok:
|
|
||||||
hass.data[DOMAIN].pop(entry.entry_id)
|
|
||||||
|
|
||||||
return unload_ok
|
|
||||||
|
@ -15,11 +15,11 @@ from homeassistant.components.media_player import (
|
|||||||
MediaType,
|
MediaType,
|
||||||
async_process_play_media_url,
|
async_process_play_media_url,
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.device_registry import DeviceInfo
|
from homeassistant.helpers.device_registry import DeviceInfo
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
|
from . import DuneHDConfigEntry
|
||||||
from .const import ATTR_MANUFACTURER, DEFAULT_NAME, DOMAIN
|
from .const import ATTR_MANUFACTURER, DEFAULT_NAME, DOMAIN
|
||||||
|
|
||||||
CONF_SOURCES: Final = "sources"
|
CONF_SOURCES: Final = "sources"
|
||||||
@ -37,14 +37,14 @@ DUNEHD_PLAYER_SUPPORT: Final[MediaPlayerEntityFeature] = (
|
|||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
hass: HomeAssistant,
|
||||||
|
entry: DuneHDConfigEntry,
|
||||||
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Add Dune HD entities from a config_entry."""
|
"""Add Dune HD entities from a config_entry."""
|
||||||
unique_id = entry.entry_id
|
async_add_entities(
|
||||||
|
[DuneHDPlayerEntity(entry.runtime_data, DEFAULT_NAME, entry.entry_id)], True
|
||||||
player: DuneHDPlayer = hass.data[DOMAIN][entry.entry_id]
|
)
|
||||||
|
|
||||||
async_add_entities([DuneHDPlayerEntity(player, DEFAULT_NAME, unique_id)], True)
|
|
||||||
|
|
||||||
|
|
||||||
class DuneHDPlayerEntity(MediaPlayerEntity):
|
class DuneHDPlayerEntity(MediaPlayerEntity):
|
||||||
|
@ -10,8 +10,6 @@ from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT, Platform
|
|||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.exceptions import ConfigEntryNotReady
|
from homeassistant.exceptions import ConfigEntryNotReady
|
||||||
|
|
||||||
from .const import DOMAIN
|
|
||||||
|
|
||||||
PLATFORMS: list[Platform] = [
|
PLATFORMS: list[Platform] = [
|
||||||
Platform.BINARY_SENSOR,
|
Platform.BINARY_SENSOR,
|
||||||
Platform.CLIMATE,
|
Platform.CLIMATE,
|
||||||
@ -21,7 +19,10 @@ PLATFORMS: list[Platform] = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
type DuotecnoConfigEntry = ConfigEntry[PyDuotecno]
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_entry(hass: HomeAssistant, entry: DuotecnoConfigEntry) -> bool:
|
||||||
"""Set up duotecno from a config entry."""
|
"""Set up duotecno from a config entry."""
|
||||||
|
|
||||||
controller = PyDuotecno()
|
controller = PyDuotecno()
|
||||||
@ -31,14 +32,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
)
|
)
|
||||||
except (OSError, InvalidPassword, LoadFailure) as err:
|
except (OSError, InvalidPassword, LoadFailure) as err:
|
||||||
raise ConfigEntryNotReady from err
|
raise ConfigEntryNotReady from err
|
||||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = controller
|
|
||||||
|
entry.runtime_data = controller
|
||||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_unload_entry(hass: HomeAssistant, entry: DuotecnoConfigEntry) -> bool:
|
||||||
"""Unload a config entry."""
|
"""Unload a config entry."""
|
||||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||||
hass.data[DOMAIN].pop(entry.entry_id)
|
|
||||||
|
|
||||||
return unload_ok
|
|
||||||
|
@ -2,28 +2,25 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from duotecno.controller import PyDuotecno
|
|
||||||
from duotecno.unit import ControlUnit, VirtualUnit
|
from duotecno.unit import ControlUnit, VirtualUnit
|
||||||
|
|
||||||
from homeassistant.components.binary_sensor import BinarySensorEntity
|
from homeassistant.components.binary_sensor import BinarySensorEntity
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
from .const import DOMAIN
|
from . import DuotecnoConfigEntry
|
||||||
from .entity import DuotecnoEntity
|
from .entity import DuotecnoEntity
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
entry: ConfigEntry,
|
entry: DuotecnoConfigEntry,
|
||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up Duotecno binary sensor on config_entry."""
|
"""Set up Duotecno binary sensor on config_entry."""
|
||||||
cntrl: PyDuotecno = hass.data[DOMAIN][entry.entry_id]
|
|
||||||
async_add_entities(
|
async_add_entities(
|
||||||
DuotecnoBinarySensor(channel)
|
DuotecnoBinarySensor(channel)
|
||||||
for channel in cntrl.get_units(["ControlUnit", "VirtualUnit"])
|
for channel in entry.runtime_data.get_units(["ControlUnit", "VirtualUnit"])
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -4,7 +4,6 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from typing import Any, Final
|
from typing import Any, Final
|
||||||
|
|
||||||
from duotecno.controller import PyDuotecno
|
|
||||||
from duotecno.unit import SensUnit
|
from duotecno.unit import SensUnit
|
||||||
|
|
||||||
from homeassistant.components.climate import (
|
from homeassistant.components.climate import (
|
||||||
@ -12,12 +11,11 @@ from homeassistant.components.climate import (
|
|||||||
ClimateEntityFeature,
|
ClimateEntityFeature,
|
||||||
HVACMode,
|
HVACMode,
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature
|
from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
from .const import DOMAIN
|
from . import DuotecnoConfigEntry
|
||||||
from .entity import DuotecnoEntity, api_call
|
from .entity import DuotecnoEntity, api_call
|
||||||
|
|
||||||
HVACMODE: Final = {
|
HVACMODE: Final = {
|
||||||
@ -33,13 +31,13 @@ PRESETMODES_REVERSE: Final = {value: key for key, value in PRESETMODES.items()}
|
|||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
entry: ConfigEntry,
|
entry: DuotecnoConfigEntry,
|
||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up Duotecno climate based on config_entry."""
|
"""Set up Duotecno climate based on config_entry."""
|
||||||
cntrl: PyDuotecno = hass.data[DOMAIN][entry.entry_id]
|
|
||||||
async_add_entities(
|
async_add_entities(
|
||||||
DuotecnoClimate(channel) for channel in cntrl.get_units(["SensUnit"])
|
DuotecnoClimate(channel)
|
||||||
|
for channel in entry.runtime_data.get_units(["SensUnit"])
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user