forked from home-assistant/core
Compare commits
108 Commits
2024.1.0b4
...
2024.1.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
99ee57aefc | ||
|
|
9c6cb5347c | ||
|
|
51c75b020d | ||
|
|
061d2d3ccf | ||
|
|
97b596a00d | ||
|
|
e3a44e499c | ||
|
|
48766c08e0 | ||
|
|
f7ad7c4235 | ||
|
|
38f9fd5734 | ||
|
|
05964d6bad | ||
|
|
70d1e6a270 | ||
|
|
28da1ac69e | ||
|
|
504e4a7923 | ||
|
|
40547974fb | ||
|
|
b87bbd1529 | ||
|
|
d89659f196 | ||
|
|
00b899ca3c | ||
|
|
765c520d7a | ||
|
|
2a46f201cb | ||
|
|
7f8a157788 | ||
|
|
f993e923a3 | ||
|
|
3386e0e766 | ||
|
|
0705be607f | ||
|
|
5b84e50dc0 | ||
|
|
26da7402a2 | ||
|
|
f1fc5abbc2 | ||
|
|
644a823c45 | ||
|
|
fcab683cc0 | ||
|
|
7bdabce68f | ||
|
|
cf1a528b7a | ||
|
|
f18ab5e1cc | ||
|
|
bee76db1c3 | ||
|
|
652fa7d693 | ||
|
|
73ba77deb6 | ||
|
|
66307c5acb | ||
|
|
86822018d8 | ||
|
|
b333be8945 | ||
|
|
a58483f93c | ||
|
|
7c06f05108 | ||
|
|
175e07fe3b | ||
|
|
9bc0217738 | ||
|
|
53ab892575 | ||
|
|
409a254fe5 | ||
|
|
54e62b4095 | ||
|
|
4e991388fb | ||
|
|
1acae5a62d | ||
|
|
9fe351f363 | ||
|
|
f9dacedf0f | ||
|
|
4eddbe7b47 | ||
|
|
cab833160d | ||
|
|
003d2be477 | ||
|
|
5a01b55fd1 | ||
|
|
0dbb4105bc | ||
|
|
5ff6284e0f | ||
|
|
97674cee88 | ||
|
|
24a8a512d6 | ||
|
|
658f1cf5c5 | ||
|
|
d012817190 | ||
|
|
056701d218 | ||
|
|
c3963b26e7 | ||
|
|
4ade5e46d9 | ||
|
|
c242dcd1f2 | ||
|
|
4e126d68b7 | ||
|
|
d7e1a4fa20 | ||
|
|
3215dfee6d | ||
|
|
c78d691d30 | ||
|
|
04bf569308 | ||
|
|
b8576b8091 | ||
|
|
a7aa5c0e52 | ||
|
|
d600b76801 | ||
|
|
c56d118e8b | ||
|
|
80b45edb2e | ||
|
|
5529a85a2b | ||
|
|
e2acc70128 | ||
|
|
427077a4c9 | ||
|
|
8c9875c3cc | ||
|
|
ce5455fefc | ||
|
|
aef129afaf | ||
|
|
1c94a94ba2 | ||
|
|
d87baba96f | ||
|
|
5a0997bac0 | ||
|
|
e70af204ee | ||
|
|
fb0cc6c5d0 | ||
|
|
15cecbd4a4 | ||
|
|
8cf47c4925 | ||
|
|
cd8d95a04d | ||
|
|
015752ff11 | ||
|
|
9d697c5026 | ||
|
|
4595c3edaa | ||
|
|
e745542431 | ||
|
|
2be72fd891 | ||
|
|
2b43f5fcda | ||
|
|
3295722e70 | ||
|
|
f98bbf88b1 | ||
|
|
0226b3f10c | ||
|
|
5986967db7 | ||
|
|
95ef2dd7f9 | ||
|
|
527d9fbb6b | ||
|
|
5eb1073b4a | ||
|
|
77cdc10883 | ||
|
|
5100ba252f | ||
|
|
b5b8bc3102 | ||
|
|
6f18a29241 | ||
|
|
596f855eab | ||
|
|
5877fe135c | ||
|
|
26cf30fc3a | ||
|
|
3419b8d082 | ||
|
|
35fc26457b |
@@ -49,6 +49,7 @@ homeassistant.components.aftership.*
|
||||
homeassistant.components.air_quality.*
|
||||
homeassistant.components.airly.*
|
||||
homeassistant.components.airnow.*
|
||||
homeassistant.components.airthings_ble.*
|
||||
homeassistant.components.airvisual.*
|
||||
homeassistant.components.airvisual_pro.*
|
||||
homeassistant.components.airzone.*
|
||||
|
||||
@@ -1550,7 +1550,7 @@ build.json @home-assistant/supervisor
|
||||
/tests/components/zodiac/ @JulienTant
|
||||
/homeassistant/components/zone/ @home-assistant/core
|
||||
/tests/components/zone/ @home-assistant/core
|
||||
/homeassistant/components/zoneminder/ @rohankapoorcom
|
||||
/homeassistant/components/zoneminder/ @rohankapoorcom @nabbi
|
||||
/homeassistant/components/zwave_js/ @home-assistant/z-wave
|
||||
/tests/components/zwave_js/ @home-assistant/z-wave
|
||||
/homeassistant/components/zwave_me/ @lawfulchaos @Z-Wave-Me @PoltoS
|
||||
|
||||
@@ -4,7 +4,8 @@ from __future__ import annotations
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
|
||||
from airthings_ble import AirthingsBluetoothDeviceData
|
||||
from airthings_ble import AirthingsBluetoothDeviceData, AirthingsDevice
|
||||
from bleak_retry_connector import close_stale_connections_by_address
|
||||
|
||||
from homeassistant.components import bluetooth
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
@@ -30,6 +31,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
is_metric = hass.config.units is METRIC_SYSTEM
|
||||
assert address is not None
|
||||
|
||||
await close_stale_connections_by_address(address)
|
||||
|
||||
ble_device = bluetooth.async_ble_device_from_address(hass, address)
|
||||
|
||||
if not ble_device:
|
||||
@@ -37,13 +40,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
f"Could not find Airthings device with address {address}"
|
||||
)
|
||||
|
||||
async def _async_update_method():
|
||||
async def _async_update_method() -> AirthingsDevice:
|
||||
"""Get data from Airthings BLE."""
|
||||
ble_device = bluetooth.async_ble_device_from_address(hass, address)
|
||||
airthings = AirthingsBluetoothDeviceData(_LOGGER, elevation, is_metric)
|
||||
|
||||
try:
|
||||
data = await airthings.update_device(ble_device)
|
||||
data = await airthings.update_device(ble_device) # type: ignore[arg-type]
|
||||
except Exception as err:
|
||||
raise UpdateFailed(f"Unable to fetch data: {err}") from err
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ from homeassistant.core import HomeAssistant
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.config_validation import make_entity_service_schema
|
||||
from homeassistant.helpers.deprecation import (
|
||||
all_with_deprecated_constants,
|
||||
check_if_deprecated_constant,
|
||||
dir_with_deprecated_constants,
|
||||
)
|
||||
@@ -52,12 +53,6 @@ if TYPE_CHECKING:
|
||||
else:
|
||||
from homeassistant.backports.functools import cached_property
|
||||
|
||||
# As we import constants of the cost module here, we need to add the following
|
||||
# functions to check for deprecated constants again
|
||||
# Both can be removed if no deprecated constant are in this module anymore
|
||||
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
|
||||
__dir__ = partial(dir_with_deprecated_constants, module_globals=globals())
|
||||
|
||||
_LOGGER: Final = logging.getLogger(__name__)
|
||||
|
||||
SCAN_INTERVAL: Final = timedelta(seconds=30)
|
||||
@@ -249,3 +244,13 @@ class AlarmControlPanelEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_A
|
||||
ATTR_CHANGED_BY: self.changed_by,
|
||||
ATTR_CODE_ARM_REQUIRED: self.code_arm_required,
|
||||
}
|
||||
|
||||
|
||||
# As we import constants of the const module here, we need to add the following
|
||||
# functions to check for deprecated constants again
|
||||
# These can be removed if no deprecated constant are in this module anymore
|
||||
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
|
||||
__dir__ = partial(
|
||||
dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
|
||||
)
|
||||
__all__ = all_with_deprecated_constants(globals())
|
||||
|
||||
@@ -5,6 +5,7 @@ from typing import Final
|
||||
|
||||
from homeassistant.helpers.deprecation import (
|
||||
DeprecatedConstantEnum,
|
||||
all_with_deprecated_constants,
|
||||
check_if_deprecated_constant,
|
||||
dir_with_deprecated_constants,
|
||||
)
|
||||
@@ -60,10 +61,6 @@ _DEPRECATED_SUPPORT_ALARM_ARM_VACATION: Final = DeprecatedConstantEnum(
|
||||
AlarmControlPanelEntityFeature.ARM_VACATION, "2025.1"
|
||||
)
|
||||
|
||||
# Both can be removed if no deprecated constant are in this module anymore
|
||||
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
|
||||
__dir__ = partial(dir_with_deprecated_constants, module_globals=globals())
|
||||
|
||||
CONDITION_TRIGGERED: Final = "is_triggered"
|
||||
CONDITION_DISARMED: Final = "is_disarmed"
|
||||
CONDITION_ARMED_HOME: Final = "is_armed_home"
|
||||
@@ -71,3 +68,10 @@ CONDITION_ARMED_AWAY: Final = "is_armed_away"
|
||||
CONDITION_ARMED_NIGHT: Final = "is_armed_night"
|
||||
CONDITION_ARMED_VACATION: Final = "is_armed_vacation"
|
||||
CONDITION_ARMED_CUSTOM_BYPASS: Final = "is_armed_custom_bypass"
|
||||
|
||||
# These can be removed if no deprecated constant are in this module anymore
|
||||
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
|
||||
__dir__ = partial(
|
||||
dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
|
||||
)
|
||||
__all__ = all_with_deprecated_constants(globals())
|
||||
|
||||
@@ -15,6 +15,9 @@ from homeassistant.helpers import aiohttp_client
|
||||
from homeassistant.helpers.storage import Store
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from .const import STORAGE_ACCESS_TOKEN, STORAGE_REFRESH_TOKEN
|
||||
from .diagnostics import async_redact_lwa_params
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
LWA_TOKEN_URI = "https://api.amazon.com/auth/o2/token"
|
||||
@@ -24,8 +27,6 @@ PREEMPTIVE_REFRESH_TTL_IN_SECONDS = 300
|
||||
STORAGE_KEY = "alexa_auth"
|
||||
STORAGE_VERSION = 1
|
||||
STORAGE_EXPIRE_TIME = "expire_time"
|
||||
STORAGE_ACCESS_TOKEN = "access_token"
|
||||
STORAGE_REFRESH_TOKEN = "refresh_token"
|
||||
|
||||
|
||||
class Auth:
|
||||
@@ -56,7 +57,7 @@ class Auth:
|
||||
}
|
||||
_LOGGER.debug(
|
||||
"Calling LWA to get the access token (first time), with: %s",
|
||||
json.dumps(lwa_params),
|
||||
json.dumps(async_redact_lwa_params(lwa_params)),
|
||||
)
|
||||
|
||||
return await self._async_request_new_token(lwa_params)
|
||||
@@ -133,7 +134,7 @@ class Auth:
|
||||
return None
|
||||
|
||||
response_json = await response.json()
|
||||
_LOGGER.debug("LWA response body : %s", response_json)
|
||||
_LOGGER.debug("LWA response body : %s", async_redact_lwa_params(response_json))
|
||||
|
||||
access_token: str = response_json["access_token"]
|
||||
refresh_token: str = response_json["refresh_token"]
|
||||
|
||||
@@ -1112,13 +1112,17 @@ class AlexaThermostatController(AlexaCapability):
|
||||
"""Return what properties this entity supports."""
|
||||
properties = [{"name": "thermostatMode"}]
|
||||
supported = self.entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
|
||||
if supported & climate.ClimateEntityFeature.TARGET_TEMPERATURE:
|
||||
if self.entity.domain == climate.DOMAIN:
|
||||
if supported & climate.ClimateEntityFeature.TARGET_TEMPERATURE_RANGE:
|
||||
properties.append({"name": "lowerSetpoint"})
|
||||
properties.append({"name": "upperSetpoint"})
|
||||
if supported & climate.ClimateEntityFeature.TARGET_TEMPERATURE:
|
||||
properties.append({"name": "targetSetpoint"})
|
||||
elif (
|
||||
self.entity.domain == water_heater.DOMAIN
|
||||
and supported & water_heater.WaterHeaterEntityFeature.TARGET_TEMPERATURE
|
||||
):
|
||||
properties.append({"name": "targetSetpoint"})
|
||||
if supported & water_heater.WaterHeaterEntityFeature.TARGET_TEMPERATURE:
|
||||
properties.append({"name": "targetSetpoint"})
|
||||
if supported & climate.ClimateEntityFeature.TARGET_TEMPERATURE_RANGE:
|
||||
properties.append({"name": "lowerSetpoint"})
|
||||
properties.append({"name": "upperSetpoint"})
|
||||
return properties
|
||||
|
||||
def properties_proactively_reported(self) -> bool:
|
||||
|
||||
@@ -90,6 +90,9 @@ API_THERMOSTAT_PRESETS = {climate.PRESET_ECO: "ECO"}
|
||||
# we add PRESET_MODE_NA if a fan / humidifier has only one preset_mode
|
||||
PRESET_MODE_NA = "-"
|
||||
|
||||
STORAGE_ACCESS_TOKEN = "access_token"
|
||||
STORAGE_REFRESH_TOKEN = "refresh_token"
|
||||
|
||||
|
||||
class Cause:
|
||||
"""Possible causes for property changes.
|
||||
|
||||
34
homeassistant/components/alexa/diagnostics.py
Normal file
34
homeassistant/components/alexa/diagnostics.py
Normal file
@@ -0,0 +1,34 @@
|
||||
"""Diagnostics helpers for Alexa."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Mapping
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.components.diagnostics import async_redact_data
|
||||
from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET
|
||||
from homeassistant.core import callback
|
||||
|
||||
STORAGE_ACCESS_TOKEN = "access_token"
|
||||
STORAGE_REFRESH_TOKEN = "refresh_token"
|
||||
|
||||
TO_REDACT_LWA = {
|
||||
CONF_CLIENT_ID,
|
||||
CONF_CLIENT_SECRET,
|
||||
STORAGE_ACCESS_TOKEN,
|
||||
STORAGE_REFRESH_TOKEN,
|
||||
}
|
||||
|
||||
TO_REDACT_AUTH = {"correlationToken", "token"}
|
||||
|
||||
|
||||
@callback
|
||||
def async_redact_lwa_params(lwa_params: dict[str, str]) -> dict[str, str]:
|
||||
"""Redact lwa_params."""
|
||||
return async_redact_data(lwa_params, TO_REDACT_LWA)
|
||||
|
||||
|
||||
@callback
|
||||
def async_redact_auth_data(mapping: Mapping[Any, Any]) -> dict[str, str]:
|
||||
"""React auth data."""
|
||||
return async_redact_data(mapping, TO_REDACT_AUTH)
|
||||
@@ -144,7 +144,6 @@ async def async_api_accept_grant(
|
||||
Async friendly.
|
||||
"""
|
||||
auth_code: str = directive.payload["grant"]["code"]
|
||||
_LOGGER.debug("AcceptGrant code: %s", auth_code)
|
||||
|
||||
if config.supports_auth:
|
||||
await config.async_accept_grant(auth_code)
|
||||
|
||||
@@ -25,6 +25,7 @@ from .const import (
|
||||
CONF_LOCALE,
|
||||
EVENT_ALEXA_SMART_HOME,
|
||||
)
|
||||
from .diagnostics import async_redact_auth_data
|
||||
from .errors import AlexaBridgeUnreachableError, AlexaError
|
||||
from .handlers import HANDLERS
|
||||
from .state_report import AlexaDirective
|
||||
@@ -149,12 +150,21 @@ class SmartHomeView(HomeAssistantView):
|
||||
user: User = request["hass_user"]
|
||||
message: dict[str, Any] = await request.json()
|
||||
|
||||
_LOGGER.debug("Received Alexa Smart Home request: %s", message)
|
||||
if _LOGGER.isEnabledFor(logging.DEBUG):
|
||||
_LOGGER.debug(
|
||||
"Received Alexa Smart Home request: %s",
|
||||
async_redact_auth_data(message),
|
||||
)
|
||||
|
||||
response = await async_handle_message(
|
||||
hass, self.smart_home_config, message, context=core.Context(user_id=user.id)
|
||||
)
|
||||
_LOGGER.debug("Sending Alexa Smart Home response: %s", response)
|
||||
if _LOGGER.isEnabledFor(logging.DEBUG):
|
||||
_LOGGER.debug(
|
||||
"Sending Alexa Smart Home response: %s",
|
||||
async_redact_auth_data(response),
|
||||
)
|
||||
|
||||
return b"" if response is None else self.json(response)
|
||||
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ from .const import (
|
||||
DOMAIN,
|
||||
Cause,
|
||||
)
|
||||
from .diagnostics import async_redact_auth_data
|
||||
from .entities import ENTITY_ADAPTERS, AlexaEntity, generate_alexa_id
|
||||
from .errors import AlexaInvalidEndpointError, NoTokenAvailable, RequireRelink
|
||||
|
||||
@@ -43,6 +44,8 @@ if TYPE_CHECKING:
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
DEFAULT_TIMEOUT = 10
|
||||
|
||||
TO_REDACT = {"correlationToken", "token"}
|
||||
|
||||
|
||||
class AlexaDirective:
|
||||
"""An incoming Alexa directive."""
|
||||
@@ -379,7 +382,9 @@ async def async_send_changereport_message(
|
||||
response_text = await response.text()
|
||||
|
||||
if _LOGGER.isEnabledFor(logging.DEBUG):
|
||||
_LOGGER.debug("Sent: %s", json.dumps(message_serialized))
|
||||
_LOGGER.debug(
|
||||
"Sent: %s", json.dumps(async_redact_auth_data(message_serialized))
|
||||
)
|
||||
_LOGGER.debug("Received (%s): %s", response.status, response_text)
|
||||
|
||||
if response.status == HTTPStatus.ACCEPTED:
|
||||
@@ -533,7 +538,9 @@ async def async_send_doorbell_event_message(
|
||||
response_text = await response.text()
|
||||
|
||||
if _LOGGER.isEnabledFor(logging.DEBUG):
|
||||
_LOGGER.debug("Sent: %s", json.dumps(message_serialized))
|
||||
_LOGGER.debug(
|
||||
"Sent: %s", json.dumps(async_redact_auth_data(message_serialized))
|
||||
)
|
||||
_LOGGER.debug("Received (%s): %s", response.status, response_text)
|
||||
|
||||
if response.status == HTTPStatus.ACCEPTED:
|
||||
|
||||
39
homeassistant/components/aosmith/diagnostics.py
Normal file
39
homeassistant/components/aosmith/diagnostics.py
Normal file
@@ -0,0 +1,39 @@
|
||||
"""Diagnostics support for A. O. Smith."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.components.diagnostics import async_redact_data
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from . import AOSmithData
|
||||
from .const import DOMAIN
|
||||
|
||||
TO_REDACT = {
|
||||
"address",
|
||||
"city",
|
||||
"contactId",
|
||||
"dsn",
|
||||
"email",
|
||||
"firstName",
|
||||
"heaterSsid",
|
||||
"id",
|
||||
"lastName",
|
||||
"phone",
|
||||
"postalCode",
|
||||
"registeredOwner",
|
||||
"serial",
|
||||
"ssid",
|
||||
"state",
|
||||
}
|
||||
|
||||
|
||||
async def async_get_config_entry_diagnostics(
|
||||
hass: HomeAssistant, config_entry: ConfigEntry
|
||||
) -> dict[str, Any]:
|
||||
"""Return diagnostics for a config entry."""
|
||||
data: AOSmithData = hass.data[DOMAIN][config_entry.entry_id]
|
||||
|
||||
all_device_info = await data.client.get_all_device_info()
|
||||
return async_redact_data(all_device_info, TO_REDACT)
|
||||
@@ -5,5 +5,5 @@
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/aosmith",
|
||||
"iot_class": "cloud_polling",
|
||||
"requirements": ["py-aosmith==1.0.1"]
|
||||
"requirements": ["py-aosmith==1.0.4"]
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@ from homeassistant.helpers import condition
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.deprecation import (
|
||||
DeprecatedConstant,
|
||||
all_with_deprecated_constants,
|
||||
check_if_deprecated_constant,
|
||||
dir_with_deprecated_constants,
|
||||
)
|
||||
@@ -147,10 +148,6 @@ _DEPRECATED_AutomationTriggerInfo = DeprecatedConstant(
|
||||
TriggerInfo, "TriggerInfo", "2025.1"
|
||||
)
|
||||
|
||||
# Both can be removed if no deprecated constant are in this module anymore
|
||||
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
|
||||
__dir__ = partial(dir_with_deprecated_constants, module_globals=globals())
|
||||
|
||||
|
||||
@bind_hass
|
||||
def is_on(hass: HomeAssistant, entity_id: str) -> bool:
|
||||
@@ -1108,3 +1105,11 @@ def websocket_config(
|
||||
"config": automation.raw_config,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
# These can be removed if no deprecated constant are in this module anymore
|
||||
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
|
||||
__dir__ = partial(
|
||||
dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
|
||||
)
|
||||
__all__ = all_with_deprecated_constants(globals())
|
||||
|
||||
@@ -19,6 +19,7 @@ from homeassistant.helpers.config_validation import ( # noqa: F401
|
||||
)
|
||||
from homeassistant.helpers.deprecation import (
|
||||
DeprecatedConstantEnum,
|
||||
all_with_deprecated_constants,
|
||||
check_if_deprecated_constant,
|
||||
dir_with_deprecated_constants,
|
||||
)
|
||||
@@ -218,10 +219,6 @@ _DEPRECATED_DEVICE_CLASS_WINDOW = DeprecatedConstantEnum(
|
||||
BinarySensorDeviceClass.WINDOW, "2025.1"
|
||||
)
|
||||
|
||||
# Both can be removed if no deprecated constant are in this module anymore
|
||||
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
|
||||
__dir__ = partial(dir_with_deprecated_constants, module_globals=globals())
|
||||
|
||||
# mypy: disallow-any-generics
|
||||
|
||||
|
||||
@@ -303,3 +300,11 @@ class BinarySensorEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_)
|
||||
if (is_on := self.is_on) is None:
|
||||
return None
|
||||
return STATE_ON if is_on else STATE_OFF
|
||||
|
||||
|
||||
# These can be removed if no deprecated constant are in this module anymore
|
||||
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
|
||||
__dir__ = partial(
|
||||
dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
|
||||
)
|
||||
__all__ = all_with_deprecated_constants(globals())
|
||||
|
||||
@@ -13,7 +13,7 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||
from .const import DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
SCAN_INTERVAL = 30
|
||||
SCAN_INTERVAL = 300
|
||||
|
||||
|
||||
class BlinkUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
||||
|
||||
@@ -20,5 +20,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/blink",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["blinkpy"],
|
||||
"requirements": ["blinkpy==0.22.4"]
|
||||
"requirements": ["blinkpy==0.22.5"]
|
||||
}
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
"bleak==0.21.1",
|
||||
"bleak-retry-connector==3.4.0",
|
||||
"bluetooth-adapters==0.16.2",
|
||||
"bluetooth-auto-recovery==1.2.3",
|
||||
"bluetooth-auto-recovery==1.3.0",
|
||||
"bluetooth-data-tools==1.19.0",
|
||||
"dbus-fast==2.21.0",
|
||||
"habluetooth==2.0.1"
|
||||
"habluetooth==2.1.0"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ from homeassistant.helpers.config_validation import ( # noqa: F401
|
||||
)
|
||||
from homeassistant.helpers.deprecation import (
|
||||
DeprecatedConstantEnum,
|
||||
all_with_deprecated_constants,
|
||||
check_if_deprecated_constant,
|
||||
dir_with_deprecated_constants,
|
||||
)
|
||||
@@ -123,10 +124,6 @@ _DEPRECATED_SUPPORT_STREAM: Final = DeprecatedConstantEnum(
|
||||
CameraEntityFeature.STREAM, "2025.1"
|
||||
)
|
||||
|
||||
# Both can be removed if no deprecated constant are in this module anymore
|
||||
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
|
||||
__dir__ = partial(dir_with_deprecated_constants, module_globals=globals())
|
||||
|
||||
RTSP_PREFIXES = {"rtsp://", "rtsps://", "rtmp://"}
|
||||
|
||||
DEFAULT_CONTENT_TYPE: Final = "image/jpeg"
|
||||
@@ -1082,3 +1079,11 @@ async def async_handle_record_service(
|
||||
duration=service_call.data[CONF_DURATION],
|
||||
lookback=service_call.data[CONF_LOOKBACK],
|
||||
)
|
||||
|
||||
|
||||
# These can be removed if no deprecated constant are in this module anymore
|
||||
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
|
||||
__dir__ = partial(
|
||||
dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
|
||||
)
|
||||
__all__ = all_with_deprecated_constants(globals())
|
||||
|
||||
@@ -5,6 +5,7 @@ from typing import Final
|
||||
|
||||
from homeassistant.helpers.deprecation import (
|
||||
DeprecatedConstantEnum,
|
||||
all_with_deprecated_constants,
|
||||
check_if_deprecated_constant,
|
||||
dir_with_deprecated_constants,
|
||||
)
|
||||
@@ -47,6 +48,9 @@ _DEPRECATED_STREAM_TYPE_HLS = DeprecatedConstantEnum(StreamType.HLS, "2025.1")
|
||||
_DEPRECATED_STREAM_TYPE_WEB_RTC = DeprecatedConstantEnum(StreamType.WEB_RTC, "2025.1")
|
||||
|
||||
|
||||
# Both can be removed if no deprecated constant are in this module anymore
|
||||
# These can be removed if no deprecated constant are in this module anymore
|
||||
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
|
||||
__dir__ = partial(dir_with_deprecated_constants, module_globals=globals())
|
||||
__dir__ = partial(
|
||||
dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
|
||||
)
|
||||
__all__ = all_with_deprecated_constants(globals())
|
||||
|
||||
@@ -28,6 +28,7 @@ from homeassistant.helpers.config_validation import ( # noqa: F401
|
||||
make_entity_service_schema,
|
||||
)
|
||||
from homeassistant.helpers.deprecation import (
|
||||
all_with_deprecated_constants,
|
||||
check_if_deprecated_constant,
|
||||
dir_with_deprecated_constants,
|
||||
)
|
||||
@@ -141,12 +142,6 @@ SET_TEMPERATURE_SCHEMA = vol.All(
|
||||
),
|
||||
)
|
||||
|
||||
# As we import deprecated constants from the const module, we need to add these two functions
|
||||
# otherwise this module will be logged for using deprecated constants and not the custom component
|
||||
# Both can be removed if no deprecated constant are in this module anymore
|
||||
__getattr__ = ft.partial(check_if_deprecated_constant, module_globals=globals())
|
||||
__dir__ = ft.partial(dir_with_deprecated_constants, module_globals=globals())
|
||||
|
||||
# mypy: disallow-any-generics
|
||||
|
||||
|
||||
@@ -734,3 +729,13 @@ async def async_service_temperature_set(
|
||||
kwargs[value] = temp
|
||||
|
||||
await entity.async_set_temperature(**kwargs)
|
||||
|
||||
|
||||
# As we import deprecated constants from the const module, we need to add these two functions
|
||||
# otherwise this module will be logged for using deprecated constants and not the custom component
|
||||
# These can be removed if no deprecated constant are in this module anymore
|
||||
__getattr__ = ft.partial(check_if_deprecated_constant, module_globals=globals())
|
||||
__dir__ = ft.partial(
|
||||
dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
|
||||
)
|
||||
__all__ = all_with_deprecated_constants(globals())
|
||||
|
||||
@@ -5,6 +5,7 @@ from functools import partial
|
||||
|
||||
from homeassistant.helpers.deprecation import (
|
||||
DeprecatedConstantEnum,
|
||||
all_with_deprecated_constants,
|
||||
check_if_deprecated_constant,
|
||||
dir_with_deprecated_constants,
|
||||
)
|
||||
@@ -188,6 +189,9 @@ _DEPRECATED_SUPPORT_AUX_HEAT = DeprecatedConstantEnum(
|
||||
ClimateEntityFeature.AUX_HEAT, "2025.1"
|
||||
)
|
||||
|
||||
# Both can be removed if no deprecated constant are in this module anymore
|
||||
# These can be removed if no deprecated constant are in this module anymore
|
||||
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
|
||||
__dir__ = partial(dir_with_deprecated_constants, module_globals=globals())
|
||||
__dir__ = partial(
|
||||
dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
|
||||
)
|
||||
__all__ = all_with_deprecated_constants(globals())
|
||||
|
||||
@@ -5,6 +5,7 @@ import asyncio
|
||||
from collections.abc import Awaitable, Callable
|
||||
from datetime import datetime, timedelta
|
||||
from enum import Enum
|
||||
from typing import cast
|
||||
|
||||
from hass_nabucasa import Cloud
|
||||
import voluptuous as vol
|
||||
@@ -176,6 +177,22 @@ def async_active_subscription(hass: HomeAssistant) -> bool:
|
||||
return async_is_logged_in(hass) and not hass.data[DOMAIN].subscription_expired
|
||||
|
||||
|
||||
async def async_get_or_create_cloudhook(hass: HomeAssistant, webhook_id: str) -> str:
|
||||
"""Get or create a cloudhook."""
|
||||
if not async_is_connected(hass):
|
||||
raise CloudNotConnected
|
||||
|
||||
if not async_is_logged_in(hass):
|
||||
raise CloudNotAvailable
|
||||
|
||||
cloud: Cloud[CloudClient] = hass.data[DOMAIN]
|
||||
cloudhooks = cloud.client.cloudhooks
|
||||
if hook := cloudhooks.get(webhook_id):
|
||||
return cast(str, hook["cloudhook_url"])
|
||||
|
||||
return await async_create_cloudhook(hass, webhook_id)
|
||||
|
||||
|
||||
@bind_hass
|
||||
async def async_create_cloudhook(hass: HomeAssistant, webhook_id: str) -> str:
|
||||
"""Create a cloudhook."""
|
||||
@@ -274,7 +291,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
}
|
||||
|
||||
async def _on_start() -> None:
|
||||
"""Discover platforms."""
|
||||
"""Handle cloud started after login."""
|
||||
nonlocal loaded
|
||||
|
||||
# Prevent multiple discovery
|
||||
@@ -282,14 +299,6 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
return
|
||||
loaded = True
|
||||
|
||||
tts_info = {"platform_loaded": tts_platform_loaded}
|
||||
|
||||
await async_load_platform(hass, Platform.TTS, DOMAIN, tts_info, config)
|
||||
await tts_platform_loaded.wait()
|
||||
|
||||
# The config entry should be loaded after the legacy tts platform is loaded
|
||||
# to make sure that the tts integration is setup before we try to migrate
|
||||
# old assist pipelines in the cloud stt entity.
|
||||
await hass.config_entries.flow.async_init(DOMAIN, context={"source": "system"})
|
||||
|
||||
async def _on_connect() -> None:
|
||||
@@ -318,6 +327,16 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
|
||||
account_link.async_setup(hass)
|
||||
|
||||
hass.async_create_task(
|
||||
async_load_platform(
|
||||
hass,
|
||||
Platform.TTS,
|
||||
DOMAIN,
|
||||
{"platform_loaded": tts_platform_loaded},
|
||||
config,
|
||||
)
|
||||
)
|
||||
|
||||
async_call_later(
|
||||
hass=hass,
|
||||
delay=timedelta(hours=STARTUP_REPAIR_DELAY),
|
||||
|
||||
@@ -72,6 +72,7 @@ class ComelitConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
_reauth_entry: ConfigEntry | None
|
||||
_reauth_host: str
|
||||
_reauth_port: int
|
||||
_reauth_type: str
|
||||
|
||||
async def async_step_user(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
@@ -109,6 +110,7 @@ class ComelitConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
)
|
||||
self._reauth_host = entry_data[CONF_HOST]
|
||||
self._reauth_port = entry_data.get(CONF_PORT, DEFAULT_PORT)
|
||||
self._reauth_type = entry_data.get(CONF_TYPE, BRIDGE)
|
||||
|
||||
self.context["title_placeholders"] = {"host": self._reauth_host}
|
||||
return await self.async_step_reauth_confirm()
|
||||
@@ -127,6 +129,7 @@ class ComelitConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
{
|
||||
CONF_HOST: self._reauth_host,
|
||||
CONF_PORT: self._reauth_port,
|
||||
CONF_TYPE: self._reauth_type,
|
||||
}
|
||||
| user_input,
|
||||
)
|
||||
@@ -144,6 +147,7 @@ class ComelitConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
CONF_HOST: self._reauth_host,
|
||||
CONF_PORT: self._reauth_port,
|
||||
CONF_PIN: user_input[CONF_PIN],
|
||||
CONF_TYPE: self._reauth_type,
|
||||
},
|
||||
)
|
||||
self.hass.async_create_task(
|
||||
|
||||
@@ -81,15 +81,11 @@ class ComelitBaseCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
||||
try:
|
||||
await self.api.login()
|
||||
return await self._async_update_system_data()
|
||||
except exceptions.CannotConnect as err:
|
||||
_LOGGER.warning("Connection error for %s", self._host)
|
||||
await self.api.close()
|
||||
raise UpdateFailed(f"Error fetching data: {repr(err)}") from err
|
||||
except (exceptions.CannotConnect, exceptions.CannotRetrieveData) as err:
|
||||
raise UpdateFailed(repr(err)) from err
|
||||
except exceptions.CannotAuthenticate:
|
||||
raise ConfigEntryAuthFailed
|
||||
|
||||
return {}
|
||||
|
||||
@abstractmethod
|
||||
async def _async_update_system_data(self) -> dict[str, Any]:
|
||||
"""Class method for updating data."""
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/comelit",
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["aiocomelit"],
|
||||
"requirements": ["aiocomelit==0.7.0"]
|
||||
"requirements": ["aiocomelit==0.7.3"]
|
||||
}
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
"integration_type": "system",
|
||||
"iot_class": "local_push",
|
||||
"quality_scale": "internal",
|
||||
"requirements": ["hassil==1.5.1", "home-assistant-intents==2023.12.05"]
|
||||
"requirements": ["hassil==1.5.1", "home-assistant-intents==2024.1.2"]
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ from homeassistant.helpers.config_validation import ( # noqa: F401
|
||||
)
|
||||
from homeassistant.helpers.deprecation import (
|
||||
DeprecatedConstantEnum,
|
||||
all_with_deprecated_constants,
|
||||
check_if_deprecated_constant,
|
||||
dir_with_deprecated_constants,
|
||||
)
|
||||
@@ -143,10 +144,6 @@ _DEPRECATED_SUPPORT_SET_TILT_POSITION = DeprecatedConstantEnum(
|
||||
CoverEntityFeature.SET_TILT_POSITION, "2025.1"
|
||||
)
|
||||
|
||||
# Both can be removed if no deprecated constant are in this module anymore
|
||||
__getattr__ = ft.partial(check_if_deprecated_constant, module_globals=globals())
|
||||
__dir__ = ft.partial(dir_with_deprecated_constants, module_globals=globals())
|
||||
|
||||
ATTR_CURRENT_POSITION = "current_position"
|
||||
ATTR_CURRENT_TILT_POSITION = "current_tilt_position"
|
||||
ATTR_POSITION = "position"
|
||||
@@ -484,7 +481,7 @@ class CoverEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||
def _get_toggle_function(
|
||||
self, fns: dict[str, Callable[_P, _R]]
|
||||
) -> Callable[_P, _R]:
|
||||
if CoverEntityFeature.STOP | self.supported_features and (
|
||||
if self.supported_features & CoverEntityFeature.STOP and (
|
||||
self.is_closing or self.is_opening
|
||||
):
|
||||
return fns["stop"]
|
||||
@@ -493,3 +490,11 @@ class CoverEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||
if self._cover_is_last_toggle_direction_open:
|
||||
return fns["close"]
|
||||
return fns["open"]
|
||||
|
||||
|
||||
# These can be removed if no deprecated constant are in this module anymore
|
||||
__getattr__ = ft.partial(check_if_deprecated_constant, module_globals=globals())
|
||||
__dir__ = ft.partial(
|
||||
dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
|
||||
)
|
||||
__all__ = all_with_deprecated_constants(globals())
|
||||
|
||||
@@ -6,6 +6,7 @@ from functools import partial
|
||||
from homeassistant.const import ATTR_GPS_ACCURACY, STATE_HOME # noqa: F401
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.deprecation import (
|
||||
all_with_deprecated_constants,
|
||||
check_if_deprecated_constant,
|
||||
dir_with_deprecated_constants,
|
||||
)
|
||||
@@ -57,12 +58,6 @@ from .legacy import ( # noqa: F401
|
||||
see,
|
||||
)
|
||||
|
||||
# As we import deprecated constants from the const module, we need to add these two functions
|
||||
# otherwise this module will be logged for using deprecated constants and not the custom component
|
||||
# Both can be removed if no deprecated constant are in this module anymore
|
||||
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
|
||||
__dir__ = partial(dir_with_deprecated_constants, module_globals=globals())
|
||||
|
||||
|
||||
@bind_hass
|
||||
def is_on(hass: HomeAssistant, entity_id: str) -> bool:
|
||||
@@ -83,3 +78,13 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
await async_setup_legacy_integration(hass, config)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
# As we import deprecated constants from the const module, we need to add these two functions
|
||||
# otherwise this module will be logged for using deprecated constants and not the custom component
|
||||
# These can be removed if no deprecated constant are in this module anymore
|
||||
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
|
||||
__dir__ = partial(
|
||||
dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
|
||||
)
|
||||
__all__ = all_with_deprecated_constants(globals())
|
||||
|
||||
@@ -9,6 +9,7 @@ from typing import Final
|
||||
|
||||
from homeassistant.helpers.deprecation import (
|
||||
DeprecatedConstantEnum,
|
||||
all_with_deprecated_constants,
|
||||
check_if_deprecated_constant,
|
||||
dir_with_deprecated_constants,
|
||||
)
|
||||
@@ -44,10 +45,6 @@ _DEPRECATED_SOURCE_TYPE_BLUETOOTH_LE: Final = DeprecatedConstantEnum(
|
||||
SourceType.BLUETOOTH_LE, "2025.1"
|
||||
)
|
||||
|
||||
# Both can be removed if no deprecated constant are in this module anymore
|
||||
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
|
||||
__dir__ = partial(dir_with_deprecated_constants, module_globals=globals())
|
||||
|
||||
CONF_SCAN_INTERVAL: Final = "interval_seconds"
|
||||
SCAN_INTERVAL: Final = timedelta(seconds=12)
|
||||
|
||||
@@ -71,3 +68,10 @@ ATTR_CONSIDER_HOME: Final = "consider_home"
|
||||
ATTR_IP: Final = "ip"
|
||||
|
||||
CONNECTED_DEVICE_REGISTERED: Final = "device_tracker_connected_device_registered"
|
||||
|
||||
# These can be removed if no deprecated constant are in this module anymore
|
||||
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
|
||||
__dir__ = partial(
|
||||
dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
|
||||
)
|
||||
__all__ = all_with_deprecated_constants(globals())
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/drop_connect",
|
||||
"iot_class": "local_push",
|
||||
"mqtt": ["drop_connect/discovery/#"],
|
||||
"requirements": ["dropmqttapi==1.0.1"]
|
||||
"requirements": ["dropmqttapi==1.0.2"]
|
||||
}
|
||||
|
||||
@@ -5,12 +5,23 @@ from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import EnergyZeroDataUpdateCoordinator
|
||||
from .services import async_register_services
|
||||
from .services import async_setup_services
|
||||
|
||||
PLATFORMS = [Platform.SENSOR]
|
||||
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
|
||||
|
||||
|
||||
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
"""Set up EnergyZero services."""
|
||||
|
||||
async_setup_services(hass)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
@@ -27,8 +38,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
async_register_services(hass, coordinator)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ from typing import Final
|
||||
from energyzero import Electricity, Gas, VatOption
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry, ConfigEntryState
|
||||
from homeassistant.core import (
|
||||
HomeAssistant,
|
||||
ServiceCall,
|
||||
@@ -17,11 +18,13 @@ from homeassistant.core import (
|
||||
callback,
|
||||
)
|
||||
from homeassistant.exceptions import ServiceValidationError
|
||||
from homeassistant.helpers import selector
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import EnergyZeroDataUpdateCoordinator
|
||||
|
||||
ATTR_CONFIG_ENTRY: Final = "config_entry"
|
||||
ATTR_START: Final = "start"
|
||||
ATTR_END: Final = "end"
|
||||
ATTR_INCL_VAT: Final = "incl_vat"
|
||||
@@ -30,6 +33,11 @@ GAS_SERVICE_NAME: Final = "get_gas_prices"
|
||||
ENERGY_SERVICE_NAME: Final = "get_energy_prices"
|
||||
SERVICE_SCHEMA: Final = vol.Schema(
|
||||
{
|
||||
vol.Required(ATTR_CONFIG_ENTRY): selector.ConfigEntrySelector(
|
||||
{
|
||||
"integration": DOMAIN,
|
||||
}
|
||||
),
|
||||
vol.Required(ATTR_INCL_VAT): bool,
|
||||
vol.Optional(ATTR_START): str,
|
||||
vol.Optional(ATTR_END): str,
|
||||
@@ -75,12 +83,43 @@ def __serialize_prices(prices: Electricity | Gas) -> ServiceResponse:
|
||||
}
|
||||
|
||||
|
||||
def __get_coordinator(
|
||||
hass: HomeAssistant, call: ServiceCall
|
||||
) -> EnergyZeroDataUpdateCoordinator:
|
||||
"""Get the coordinator from the entry."""
|
||||
entry_id: str = call.data[ATTR_CONFIG_ENTRY]
|
||||
entry: ConfigEntry | None = hass.config_entries.async_get_entry(entry_id)
|
||||
|
||||
if not entry:
|
||||
raise ServiceValidationError(
|
||||
f"Invalid config entry: {entry_id}",
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="invalid_config_entry",
|
||||
translation_placeholders={
|
||||
"config_entry": entry_id,
|
||||
},
|
||||
)
|
||||
if entry.state != ConfigEntryState.LOADED:
|
||||
raise ServiceValidationError(
|
||||
f"{entry.title} is not loaded",
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="unloaded_config_entry",
|
||||
translation_placeholders={
|
||||
"config_entry": entry.title,
|
||||
},
|
||||
)
|
||||
|
||||
return hass.data[DOMAIN][entry_id]
|
||||
|
||||
|
||||
async def __get_prices(
|
||||
call: ServiceCall,
|
||||
*,
|
||||
coordinator: EnergyZeroDataUpdateCoordinator,
|
||||
hass: HomeAssistant,
|
||||
price_type: PriceType,
|
||||
) -> ServiceResponse:
|
||||
coordinator = __get_coordinator(hass, call)
|
||||
|
||||
start = __get_date(call.data.get(ATTR_START))
|
||||
end = __get_date(call.data.get(ATTR_END))
|
||||
|
||||
@@ -108,22 +147,20 @@ async def __get_prices(
|
||||
|
||||
|
||||
@callback
|
||||
def async_register_services(
|
||||
hass: HomeAssistant, coordinator: EnergyZeroDataUpdateCoordinator
|
||||
):
|
||||
def async_setup_services(hass: HomeAssistant) -> None:
|
||||
"""Set up EnergyZero services."""
|
||||
|
||||
hass.services.async_register(
|
||||
DOMAIN,
|
||||
GAS_SERVICE_NAME,
|
||||
partial(__get_prices, coordinator=coordinator, price_type=PriceType.GAS),
|
||||
partial(__get_prices, hass=hass, price_type=PriceType.GAS),
|
||||
schema=SERVICE_SCHEMA,
|
||||
supports_response=SupportsResponse.ONLY,
|
||||
)
|
||||
hass.services.async_register(
|
||||
DOMAIN,
|
||||
ENERGY_SERVICE_NAME,
|
||||
partial(__get_prices, coordinator=coordinator, price_type=PriceType.ENERGY),
|
||||
partial(__get_prices, hass=hass, price_type=PriceType.ENERGY),
|
||||
schema=SERVICE_SCHEMA,
|
||||
supports_response=SupportsResponse.ONLY,
|
||||
)
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
get_gas_prices:
|
||||
fields:
|
||||
config_entry:
|
||||
required: true
|
||||
selector:
|
||||
config_entry:
|
||||
integration: energyzero
|
||||
incl_vat:
|
||||
required: true
|
||||
default: true
|
||||
@@ -17,6 +22,11 @@ get_gas_prices:
|
||||
datetime:
|
||||
get_energy_prices:
|
||||
fields:
|
||||
config_entry:
|
||||
required: true
|
||||
selector:
|
||||
config_entry:
|
||||
integration: energyzero
|
||||
incl_vat:
|
||||
required: true
|
||||
default: true
|
||||
|
||||
@@ -12,6 +12,12 @@
|
||||
"exceptions": {
|
||||
"invalid_date": {
|
||||
"message": "Invalid date provided. Got {date}"
|
||||
},
|
||||
"invalid_config_entry": {
|
||||
"message": "Invalid config entry provided. Got {config_entry}"
|
||||
},
|
||||
"unloaded_config_entry": {
|
||||
"message": "Invalid config entry provided. {config_entry} is not loaded."
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
@@ -50,6 +56,10 @@
|
||||
"name": "Get gas prices",
|
||||
"description": "Request gas prices from EnergyZero.",
|
||||
"fields": {
|
||||
"config_entry": {
|
||||
"name": "Config Entry",
|
||||
"description": "The config entry to use for this service."
|
||||
},
|
||||
"incl_vat": {
|
||||
"name": "Including VAT",
|
||||
"description": "Include VAT in the prices."
|
||||
@@ -68,6 +78,10 @@
|
||||
"name": "Get energy prices",
|
||||
"description": "Request energy prices from EnergyZero.",
|
||||
"fields": {
|
||||
"config_entry": {
|
||||
"name": "[%key:component::energyzero::services::get_gas_prices::fields::config_entry::name%]",
|
||||
"description": "[%key:component::energyzero::services::get_gas_prices::fields::config_entry::description%]"
|
||||
},
|
||||
"incl_vat": {
|
||||
"name": "[%key:component::energyzero::services::get_gas_prices::fields::incl_vat::name%]",
|
||||
"description": "[%key:component::energyzero::services::get_gas_prices::fields::incl_vat::description%]"
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/enigma2",
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["openwebif"],
|
||||
"requirements": ["openwebifpy==4.0.2"]
|
||||
"requirements": ["openwebifpy==4.0.4"]
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""Support for Enigma2 media players."""
|
||||
from __future__ import annotations
|
||||
|
||||
from aiohttp.client_exceptions import ClientConnectorError
|
||||
from openwebif.api import OpenWebIfDevice
|
||||
from openwebif.enums import RemoteControlCodes
|
||||
import voluptuous as vol
|
||||
@@ -20,6 +21,7 @@ from homeassistant.const import (
|
||||
CONF_USERNAME,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import PlatformNotReady
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
@@ -96,9 +98,13 @@ async def async_setup_platform(
|
||||
source_bouquet=config.get(CONF_SOURCE_BOUQUET),
|
||||
)
|
||||
|
||||
async_add_entities(
|
||||
[Enigma2Device(config[CONF_NAME], device, await device.get_about())]
|
||||
)
|
||||
try:
|
||||
about = await device.get_about()
|
||||
except ClientConnectorError as err:
|
||||
await device.close()
|
||||
raise PlatformNotReady from err
|
||||
|
||||
async_add_entities([Enigma2Device(config[CONF_NAME], device, about)])
|
||||
|
||||
|
||||
class Enigma2Device(MediaPlayerEntity):
|
||||
@@ -169,8 +175,8 @@ class Enigma2Device(MediaPlayerEntity):
|
||||
await self._device.send_remote_control_action(RemoteControlCodes.CHANNEL_UP)
|
||||
|
||||
async def async_media_previous_track(self) -> None:
|
||||
"""Send next track command."""
|
||||
self._device.send_remote_control_action(RemoteControlCodes.CHANNEL_DOWN)
|
||||
"""Send previous track command."""
|
||||
await self._device.send_remote_control_action(RemoteControlCodes.CHANNEL_DOWN)
|
||||
|
||||
async def async_mute_volume(self, mute: bool) -> None:
|
||||
"""Mute or unmute."""
|
||||
|
||||
@@ -479,10 +479,20 @@ class EnvoyInverterEntity(EnvoySensorBaseEntity):
|
||||
)
|
||||
|
||||
@property
|
||||
def native_value(self) -> datetime.datetime | float:
|
||||
def native_value(self) -> datetime.datetime | float | None:
|
||||
"""Return the state of the sensor."""
|
||||
inverters = self.data.inverters
|
||||
assert inverters is not None
|
||||
# Some envoy fw versions return an empty inverter array every 4 hours when
|
||||
# no production is taking place. Prevent collection failure due to this
|
||||
# as other data seems fine. Inverters will show unknown during this cycle.
|
||||
if self._serial_number not in inverters:
|
||||
_LOGGER.debug(
|
||||
"Inverter %s not in returned inverters array (size: %s)",
|
||||
self._serial_number,
|
||||
len(inverters),
|
||||
)
|
||||
return None
|
||||
return self.entity_description.value_fn(inverters[self._serial_number])
|
||||
|
||||
|
||||
|
||||
@@ -497,7 +497,6 @@ class EvoBroker:
|
||||
|
||||
session_id = get_session_id(self.client_v1)
|
||||
|
||||
self.temps = {} # these are now stale, will fall back to v2 temps
|
||||
try:
|
||||
temps = await self.client_v1.get_temperatures()
|
||||
|
||||
@@ -523,6 +522,11 @@ class EvoBroker:
|
||||
),
|
||||
err,
|
||||
)
|
||||
self.temps = {} # high-precision temps now considered stale
|
||||
|
||||
except Exception:
|
||||
self.temps = {} # high-precision temps now considered stale
|
||||
raise
|
||||
|
||||
else:
|
||||
if str(self.client_v1.location_id) != self._location.locationId:
|
||||
@@ -654,6 +658,7 @@ class EvoChild(EvoDevice):
|
||||
assert isinstance(self._evo_device, evo.HotWater | evo.Zone) # mypy check
|
||||
|
||||
if self._evo_broker.temps.get(self._evo_id) is not None:
|
||||
# use high-precision temps if available
|
||||
return self._evo_broker.temps[self._evo_id]
|
||||
return self._evo_device.temperature
|
||||
|
||||
|
||||
@@ -118,7 +118,6 @@ class FAABinarySensor(CoordinatorEntity[FAADataUpdateCoordinator], BinarySensorE
|
||||
super().__init__(coordinator)
|
||||
self.entity_description = description
|
||||
_id = coordinator.data.code
|
||||
self._attr_name = f"{_id} {description.name}"
|
||||
self._attr_unique_id = f"{_id}_{description.key}"
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, _id)},
|
||||
|
||||
@@ -26,6 +26,7 @@ from homeassistant.helpers.config_validation import ( # noqa: F401
|
||||
)
|
||||
from homeassistant.helpers.deprecation import (
|
||||
DeprecatedConstantEnum,
|
||||
all_with_deprecated_constants,
|
||||
check_if_deprecated_constant,
|
||||
dir_with_deprecated_constants,
|
||||
)
|
||||
@@ -76,10 +77,6 @@ _DEPRECATED_SUPPORT_PRESET_MODE = DeprecatedConstantEnum(
|
||||
FanEntityFeature.PRESET_MODE, "2025.1"
|
||||
)
|
||||
|
||||
# Both can be removed if no deprecated constant are in this module anymore
|
||||
__getattr__ = ft.partial(check_if_deprecated_constant, module_globals=globals())
|
||||
__dir__ = ft.partial(dir_with_deprecated_constants, module_globals=globals())
|
||||
|
||||
SERVICE_INCREASE_SPEED = "increase_speed"
|
||||
SERVICE_DECREASE_SPEED = "decrease_speed"
|
||||
SERVICE_OSCILLATE = "oscillate"
|
||||
@@ -471,3 +468,11 @@ class FanEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||
if hasattr(self, "_attr_preset_modes"):
|
||||
return self._attr_preset_modes
|
||||
return None
|
||||
|
||||
|
||||
# These can be removed if no deprecated constant are in this module anymore
|
||||
__getattr__ = ft.partial(check_if_deprecated_constant, module_globals=globals())
|
||||
__dir__ = ft.partial(
|
||||
dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
|
||||
)
|
||||
__all__ = all_with_deprecated_constants(globals())
|
||||
|
||||
@@ -69,6 +69,8 @@ class FitbitOAuth2Implementation(AuthImplementation):
|
||||
)
|
||||
if err.status == HTTPStatus.UNAUTHORIZED:
|
||||
raise FitbitAuthException(f"Unauthorized error: {err}") from err
|
||||
if err.status == HTTPStatus.BAD_REQUEST:
|
||||
raise FitbitAuthException(f"Bad Request error: {err}") from err
|
||||
raise FitbitApiException(f"Server error response: {err}") from err
|
||||
except aiohttp.ClientError as err:
|
||||
raise FitbitApiException(f"Client connection error: {err}") from err
|
||||
|
||||
@@ -19,7 +19,7 @@ from homeassistant.components.climate import (
|
||||
HVACMode,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import ATTR_TEMPERATURE, PRECISION_WHOLE, UnitOfTemperature
|
||||
from homeassistant.const import ATTR_TEMPERATURE, PRECISION_HALVES, UnitOfTemperature
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
@@ -27,6 +27,8 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import (
|
||||
DOMAIN,
|
||||
MAX_TEMP,
|
||||
MIN_TEMP,
|
||||
PRESET_TO_VENTILATION_MODE_MAP,
|
||||
VENTILATION_TO_PRESET_MODE_MAP,
|
||||
)
|
||||
@@ -65,8 +67,10 @@ class FlexitClimateEntity(ClimateEntity):
|
||||
ClimateEntityFeature.PRESET_MODE | ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
)
|
||||
|
||||
_attr_target_temperature_step = PRECISION_WHOLE
|
||||
_attr_target_temperature_step = PRECISION_HALVES
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_attr_max_temp = MAX_TEMP
|
||||
_attr_min_temp = MIN_TEMP
|
||||
|
||||
def __init__(self, device: FlexitBACnet) -> None:
|
||||
"""Initialize the unit."""
|
||||
|
||||
@@ -15,6 +15,9 @@ from homeassistant.components.climate import (
|
||||
|
||||
DOMAIN = "flexit_bacnet"
|
||||
|
||||
MAX_TEMP = 30
|
||||
MIN_TEMP = 10
|
||||
|
||||
VENTILATION_TO_PRESET_MODE_MAP = {
|
||||
VENTILATION_MODE_STOP: PRESET_NONE,
|
||||
VENTILATION_MODE_AWAY: PRESET_AWAY,
|
||||
|
||||
@@ -1063,6 +1063,7 @@ class SwitchInfo(TypedDict):
|
||||
type: str
|
||||
callback_update: Callable
|
||||
callback_switch: Callable
|
||||
init_state: bool
|
||||
|
||||
|
||||
class FritzBoxBaseEntity:
|
||||
|
||||
@@ -166,9 +166,7 @@ async def _async_wifi_entities_list(
|
||||
|
||||
_LOGGER.debug("WiFi networks list: %s", networks)
|
||||
return [
|
||||
FritzBoxWifiSwitch(
|
||||
avm_wrapper, device_friendly_name, index, data["switch_name"]
|
||||
)
|
||||
FritzBoxWifiSwitch(avm_wrapper, device_friendly_name, index, data)
|
||||
for index, data in networks.items()
|
||||
]
|
||||
|
||||
@@ -310,18 +308,16 @@ class FritzBoxBaseCoordinatorSwitch(CoordinatorEntity[AvmWrapper], SwitchEntity)
|
||||
await self._async_handle_turn_on_off(turn_on=False)
|
||||
|
||||
|
||||
class FritzBoxBaseSwitch(FritzBoxBaseEntity):
|
||||
class FritzBoxBaseSwitch(FritzBoxBaseEntity, SwitchEntity):
|
||||
"""Fritz switch base class."""
|
||||
|
||||
_attr_is_on: bool | None = False
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
avm_wrapper: AvmWrapper,
|
||||
device_friendly_name: str,
|
||||
switch_info: SwitchInfo,
|
||||
) -> None:
|
||||
"""Init Fritzbox port switch."""
|
||||
"""Init Fritzbox base switch."""
|
||||
super().__init__(avm_wrapper, device_friendly_name)
|
||||
|
||||
self._description = switch_info["description"]
|
||||
@@ -330,6 +326,7 @@ class FritzBoxBaseSwitch(FritzBoxBaseEntity):
|
||||
self._type = switch_info["type"]
|
||||
self._update = switch_info["callback_update"]
|
||||
self._switch = switch_info["callback_switch"]
|
||||
self._attr_is_on = switch_info["init_state"]
|
||||
|
||||
self._name = f"{self._friendly_name} {self._description}"
|
||||
self._unique_id = f"{self._avm_wrapper.unique_id}-{slugify(self._description)}"
|
||||
@@ -381,7 +378,7 @@ class FritzBoxBaseSwitch(FritzBoxBaseEntity):
|
||||
self._attr_is_on = turn_on
|
||||
|
||||
|
||||
class FritzBoxPortSwitch(FritzBoxBaseSwitch, SwitchEntity):
|
||||
class FritzBoxPortSwitch(FritzBoxBaseSwitch):
|
||||
"""Defines a FRITZ!Box Tools PortForward switch."""
|
||||
|
||||
def __init__(
|
||||
@@ -412,6 +409,7 @@ class FritzBoxPortSwitch(FritzBoxBaseSwitch, SwitchEntity):
|
||||
type=SWITCH_TYPE_PORTFORWARD,
|
||||
callback_update=self._async_fetch_update,
|
||||
callback_switch=self._async_switch_on_off_executor,
|
||||
init_state=port_mapping["NewEnabled"],
|
||||
)
|
||||
super().__init__(avm_wrapper, device_friendly_name, switch_info)
|
||||
|
||||
@@ -553,7 +551,7 @@ class FritzBoxProfileSwitch(FritzDeviceBase, SwitchEntity):
|
||||
return True
|
||||
|
||||
|
||||
class FritzBoxWifiSwitch(FritzBoxBaseSwitch, SwitchEntity):
|
||||
class FritzBoxWifiSwitch(FritzBoxBaseSwitch):
|
||||
"""Defines a FRITZ!Box Tools Wifi switch."""
|
||||
|
||||
def __init__(
|
||||
@@ -561,7 +559,7 @@ class FritzBoxWifiSwitch(FritzBoxBaseSwitch, SwitchEntity):
|
||||
avm_wrapper: AvmWrapper,
|
||||
device_friendly_name: str,
|
||||
network_num: int,
|
||||
network_name: str,
|
||||
network_data: dict,
|
||||
) -> None:
|
||||
"""Init Fritz Wifi switch."""
|
||||
self._avm_wrapper = avm_wrapper
|
||||
@@ -571,12 +569,13 @@ class FritzBoxWifiSwitch(FritzBoxBaseSwitch, SwitchEntity):
|
||||
self._network_num = network_num
|
||||
|
||||
switch_info = SwitchInfo(
|
||||
description=f"Wi-Fi {network_name}",
|
||||
description=f"Wi-Fi {network_data['switch_name']}",
|
||||
friendly_name=device_friendly_name,
|
||||
icon="mdi:wifi",
|
||||
type=SWITCH_TYPE_WIFINETWORK,
|
||||
callback_update=self._async_fetch_update,
|
||||
callback_switch=self._async_switch_on_off_executor,
|
||||
init_state=network_data["enabled"],
|
||||
)
|
||||
super().__init__(self._avm_wrapper, device_friendly_name, switch_info)
|
||||
|
||||
|
||||
@@ -20,5 +20,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/frontend",
|
||||
"integration_type": "system",
|
||||
"quality_scale": "internal",
|
||||
"requirements": ["home-assistant-frontend==20240101.0"]
|
||||
"requirements": ["home-assistant-frontend==20240104.0"]
|
||||
}
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
"integration_type": "service",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["aio_geojson_generic_client"],
|
||||
"requirements": ["aio-geojson-generic-client==0.3"]
|
||||
"requirements": ["aio-geojson-generic-client==0.4"]
|
||||
}
|
||||
|
||||
@@ -43,6 +43,18 @@ async def async_setup_entry(
|
||||
)
|
||||
language = lang
|
||||
break
|
||||
if (
|
||||
obj_holidays.supported_languages
|
||||
and language not in obj_holidays.supported_languages
|
||||
and (default_language := obj_holidays.default_language)
|
||||
):
|
||||
obj_holidays = country_holidays(
|
||||
country,
|
||||
subdiv=province,
|
||||
years={dt_util.now().year, dt_util.now().year + 1},
|
||||
language=default_language,
|
||||
)
|
||||
language = default_language
|
||||
|
||||
async_add_entities(
|
||||
[
|
||||
|
||||
@@ -14,6 +14,6 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/homekit_controller",
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["aiohomekit", "commentjson"],
|
||||
"requirements": ["aiohomekit==3.1.1"],
|
||||
"requirements": ["aiohomekit==3.1.2"],
|
||||
"zeroconf": ["_hap._tcp.local.", "_hap._udp.local."]
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ from homeassistant.helpers.config_validation import ( # noqa: F401
|
||||
PLATFORM_SCHEMA_BASE,
|
||||
)
|
||||
from homeassistant.helpers.deprecation import (
|
||||
all_with_deprecated_constants,
|
||||
check_if_deprecated_constant,
|
||||
dir_with_deprecated_constants,
|
||||
)
|
||||
@@ -81,12 +82,6 @@ DEVICE_CLASSES_SCHEMA = vol.All(vol.Lower, vol.Coerce(HumidifierDeviceClass))
|
||||
# use the HumidifierDeviceClass enum instead.
|
||||
DEVICE_CLASSES = [cls.value for cls in HumidifierDeviceClass]
|
||||
|
||||
# As we import deprecated constants from the const module, we need to add these two functions
|
||||
# otherwise this module will be logged for using deprecated constants and not the custom component
|
||||
# Both can be removed if no deprecated constant are in this module anymore
|
||||
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
|
||||
__dir__ = partial(dir_with_deprecated_constants, module_globals=globals())
|
||||
|
||||
# mypy: disallow-any-generics
|
||||
|
||||
|
||||
@@ -214,7 +209,7 @@ class HumidifierEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_AT
|
||||
if self.target_humidity is not None:
|
||||
data[ATTR_HUMIDITY] = self.target_humidity
|
||||
|
||||
if HumidifierEntityFeature.MODES in self.supported_features:
|
||||
if HumidifierEntityFeature.MODES in self.supported_features_compat:
|
||||
data[ATTR_MODE] = self.mode
|
||||
|
||||
return data
|
||||
@@ -293,3 +288,13 @@ class HumidifierEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_AT
|
||||
self._report_deprecated_supported_features_values(new_features)
|
||||
return new_features
|
||||
return features
|
||||
|
||||
|
||||
# As we import deprecated constants from the const module, we need to add these two functions
|
||||
# otherwise this module will be logged for using deprecated constants and not the custom component
|
||||
# These can be removed if no deprecated constant are in this module anymore
|
||||
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
|
||||
__dir__ = partial(
|
||||
dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
|
||||
)
|
||||
__all__ = all_with_deprecated_constants(globals())
|
||||
|
||||
@@ -5,6 +5,7 @@ from functools import partial
|
||||
from homeassistant.helpers.deprecation import (
|
||||
DeprecatedConstant,
|
||||
DeprecatedConstantEnum,
|
||||
all_with_deprecated_constants,
|
||||
check_if_deprecated_constant,
|
||||
dir_with_deprecated_constants,
|
||||
)
|
||||
@@ -66,6 +67,9 @@ _DEPRECATED_SUPPORT_MODES = DeprecatedConstantEnum(
|
||||
HumidifierEntityFeature.MODES, "2025.1"
|
||||
)
|
||||
|
||||
# Both can be removed if no deprecated constant are in this module anymore
|
||||
# These can be removed if no deprecated constant are in this module anymore
|
||||
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
|
||||
__dir__ = partial(dir_with_deprecated_constants, module_globals=globals())
|
||||
__dir__ = partial(
|
||||
dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
|
||||
)
|
||||
__all__ = all_with_deprecated_constants(globals())
|
||||
|
||||
@@ -118,7 +118,7 @@ async def async_setup_platform(
|
||||
|
||||
mode = get_ip_mode(host)
|
||||
mac = await hass.async_add_executor_job(partial(get_mac_address, **{mode: host}))
|
||||
if mac is None:
|
||||
if mac is None or mac == "00:00:00:00:00:00":
|
||||
raise PlatformNotReady("Cannot get the ip address of kef speaker.")
|
||||
|
||||
unique_id = f"kef-{mac}"
|
||||
|
||||
@@ -82,6 +82,9 @@ DATA_HASS_CONFIG: Final = "knx_hass_config"
|
||||
ATTR_COUNTER: Final = "counter"
|
||||
ATTR_SOURCE: Final = "source"
|
||||
|
||||
# dispatcher signal for KNX interface device triggers
|
||||
SIGNAL_KNX_TELEGRAM_DICT: Final = "knx_telegram_dict"
|
||||
|
||||
AsyncMessageCallbackType = Callable[[Telegram], Awaitable[None]]
|
||||
MessageCallbackType = Callable[[Telegram], None]
|
||||
|
||||
|
||||
@@ -9,11 +9,12 @@ from homeassistant.components.device_automation import DEVICE_TRIGGER_BASE_SCHEM
|
||||
from homeassistant.const import CONF_DEVICE_ID, CONF_DOMAIN, CONF_PLATFORM, CONF_TYPE
|
||||
from homeassistant.core import CALLBACK_TYPE, HassJob, HomeAssistant, callback
|
||||
from homeassistant.helpers import selector
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.trigger import TriggerActionType, TriggerInfo
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
from . import KNXModule
|
||||
from .const import DOMAIN
|
||||
from .const import DOMAIN, SIGNAL_KNX_TELEGRAM_DICT
|
||||
from .project import KNXProject
|
||||
from .schema import ga_list_validator
|
||||
from .telegrams import TelegramDict
|
||||
@@ -87,7 +88,6 @@ async def async_attach_trigger(
|
||||
trigger_data = trigger_info["trigger_data"]
|
||||
dst_addresses: list[str] = config.get(EXTRA_FIELD_DESTINATION, [])
|
||||
job = HassJob(action, f"KNX device trigger {trigger_info}")
|
||||
knx: KNXModule = hass.data[DOMAIN]
|
||||
|
||||
@callback
|
||||
def async_call_trigger_action(telegram: TelegramDict) -> None:
|
||||
@@ -99,6 +99,8 @@ async def async_attach_trigger(
|
||||
{"trigger": {**trigger_data, **telegram}},
|
||||
)
|
||||
|
||||
return knx.telegrams.async_listen_telegram(
|
||||
async_call_trigger_action, name="KNX device trigger call"
|
||||
return async_dispatcher_connect(
|
||||
hass,
|
||||
signal=SIGNAL_KNX_TELEGRAM_DICT,
|
||||
target=async_call_trigger_action,
|
||||
)
|
||||
|
||||
@@ -11,10 +11,11 @@ from xknx.telegram import Telegram
|
||||
from xknx.telegram.apci import GroupValueResponse, GroupValueWrite
|
||||
|
||||
from homeassistant.core import CALLBACK_TYPE, HassJob, HomeAssistant, callback
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
||||
from homeassistant.helpers.storage import Store
|
||||
import homeassistant.util.dt as dt_util
|
||||
|
||||
from .const import DOMAIN
|
||||
from .const import DOMAIN, SIGNAL_KNX_TELEGRAM_DICT
|
||||
from .project import KNXProject
|
||||
|
||||
STORAGE_VERSION: Final = 1
|
||||
@@ -87,6 +88,7 @@ class Telegrams:
|
||||
"""Handle incoming and outgoing telegrams from xknx."""
|
||||
telegram_dict = self.telegram_to_dict(telegram)
|
||||
self.recent_telegrams.append(telegram_dict)
|
||||
async_dispatcher_send(self.hass, SIGNAL_KNX_TELEGRAM_DICT, telegram_dict)
|
||||
for job in self._jobs:
|
||||
self.hass.async_run_hass_job(job, telegram_dict)
|
||||
|
||||
|
||||
@@ -2,7 +2,11 @@
|
||||
|
||||
import logging
|
||||
|
||||
from bleak_retry_connector import BleakError, close_stale_connections, get_device
|
||||
from bleak_retry_connector import (
|
||||
BleakError,
|
||||
close_stale_connections_by_address,
|
||||
get_device,
|
||||
)
|
||||
from ld2410_ble import LD2410BLE
|
||||
|
||||
from homeassistant.components import bluetooth
|
||||
@@ -24,6 +28,9 @@ _LOGGER = logging.getLogger(__name__)
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up LD2410 BLE from a config entry."""
|
||||
address: str = entry.data[CONF_ADDRESS]
|
||||
|
||||
await close_stale_connections_by_address(address)
|
||||
|
||||
ble_device = bluetooth.async_ble_device_from_address(
|
||||
hass, address.upper(), True
|
||||
) or await get_device(address)
|
||||
@@ -32,8 +39,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
f"Could not find LD2410B device with address {address}"
|
||||
)
|
||||
|
||||
await close_stale_connections(ble_device)
|
||||
|
||||
ld2410_ble = LD2410BLE(ble_device)
|
||||
|
||||
coordinator = LD2410BLECoordinator(hass, ld2410_ble)
|
||||
|
||||
@@ -33,6 +33,7 @@ from homeassistant.helpers.config_validation import ( # noqa: F401
|
||||
)
|
||||
from homeassistant.helpers.deprecation import (
|
||||
DeprecatedConstantEnum,
|
||||
all_with_deprecated_constants,
|
||||
check_if_deprecated_constant,
|
||||
dir_with_deprecated_constants,
|
||||
)
|
||||
@@ -70,10 +71,6 @@ class LockEntityFeature(IntFlag):
|
||||
# Please use the LockEntityFeature enum instead.
|
||||
_DEPRECATED_SUPPORT_OPEN = DeprecatedConstantEnum(LockEntityFeature.OPEN, "2025.1")
|
||||
|
||||
# Both can be removed if no deprecated constant are in this module anymore
|
||||
__getattr__ = ft.partial(check_if_deprecated_constant, module_globals=globals())
|
||||
__dir__ = ft.partial(dir_with_deprecated_constants, module_globals=globals())
|
||||
|
||||
PROP_TO_ATTR = {"changed_by": ATTR_CHANGED_BY, "code_format": ATTR_CODE_FORMAT}
|
||||
|
||||
# mypy: disallow-any-generics
|
||||
@@ -315,3 +312,11 @@ class LockEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||
return
|
||||
|
||||
self._lock_option_default_code = ""
|
||||
|
||||
|
||||
# These can be removed if no deprecated constant are in this module anymore
|
||||
__getattr__ = ft.partial(check_if_deprecated_constant, module_globals=globals())
|
||||
__dir__ = ft.partial(
|
||||
dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
|
||||
)
|
||||
__all__ = all_with_deprecated_constants(globals())
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"station_id": "Sensor ID",
|
||||
"sensor_id": "Sensor ID",
|
||||
"show_on_map": "Show on map"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ from homeassistant.helpers.significant_change import (
|
||||
|
||||
from . import (
|
||||
ATTR_ENTITY_PICTURE_LOCAL,
|
||||
ATTR_GROUP_MEMBERS,
|
||||
ATTR_MEDIA_POSITION,
|
||||
ATTR_MEDIA_POSITION_UPDATED_AT,
|
||||
ATTR_MEDIA_VOLUME_LEVEL,
|
||||
@@ -25,9 +24,8 @@ INSIGNIFICANT_ATTRIBUTES: set[str] = {
|
||||
|
||||
SIGNIFICANT_ATTRIBUTES: set[str] = {
|
||||
ATTR_ENTITY_PICTURE_LOCAL,
|
||||
ATTR_GROUP_MEMBERS,
|
||||
*ATTR_TO_PROPERTY,
|
||||
}
|
||||
} - INSIGNIFICANT_ATTRIBUTES
|
||||
|
||||
|
||||
@callback
|
||||
@@ -44,18 +42,10 @@ def async_check_significant_change(
|
||||
return True
|
||||
|
||||
old_attrs_s = set(
|
||||
{
|
||||
k: v
|
||||
for k, v in old_attrs.items()
|
||||
if k in SIGNIFICANT_ATTRIBUTES - INSIGNIFICANT_ATTRIBUTES
|
||||
}.items()
|
||||
{k: v for k, v in old_attrs.items() if k in SIGNIFICANT_ATTRIBUTES}.items()
|
||||
)
|
||||
new_attrs_s = set(
|
||||
{
|
||||
k: v
|
||||
for k, v in new_attrs.items()
|
||||
if k in SIGNIFICANT_ATTRIBUTES - INSIGNIFICANT_ATTRIBUTES
|
||||
}.items()
|
||||
{k: v for k, v in new_attrs.items() if k in SIGNIFICANT_ATTRIBUTES}.items()
|
||||
)
|
||||
changed_attrs: set[str] = {item[0] for item in old_attrs_s ^ new_attrs_s}
|
||||
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from collections.abc import Coroutine
|
||||
import json
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
import aiohttp
|
||||
from aiohttp.hdrs import CONTENT_TYPE
|
||||
@@ -267,11 +269,11 @@ class MicrosoftFace:
|
||||
"""Store group/person data and IDs."""
|
||||
return self._store
|
||||
|
||||
async def update_store(self):
|
||||
async def update_store(self) -> None:
|
||||
"""Load all group/person data into local store."""
|
||||
groups = await self.call_api("get", "persongroups")
|
||||
|
||||
remove_tasks = []
|
||||
remove_tasks: list[Coroutine[Any, Any, None]] = []
|
||||
new_entities = []
|
||||
for group in groups:
|
||||
g_id = group["personGroupId"]
|
||||
@@ -293,7 +295,7 @@ class MicrosoftFace:
|
||||
self._store[g_id][person["name"]] = person["personId"]
|
||||
|
||||
if remove_tasks:
|
||||
await asyncio.gather(remove_tasks)
|
||||
await asyncio.gather(*remove_tasks)
|
||||
await self._component.async_add_entities(new_entities)
|
||||
|
||||
async def call_api(self, method, function, data=None, binary=False, params=None):
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["dnspython", "mcstatus"],
|
||||
"quality_scale": "gold",
|
||||
"requirements": ["mcstatus==11.0.0"]
|
||||
"requirements": ["mcstatus==11.1.1"]
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ from .const import (
|
||||
)
|
||||
from .helpers import savable_state
|
||||
from .http_api import RegistrationsView
|
||||
from .util import async_create_cloud_hook
|
||||
from .webhook import handle_webhook
|
||||
|
||||
PLATFORMS = [Platform.SENSOR, Platform.BINARY_SENSOR, Platform.DEVICE_TRACKER]
|
||||
@@ -103,26 +104,20 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
registration_name = f"Mobile App: {registration[ATTR_DEVICE_NAME]}"
|
||||
webhook_register(hass, DOMAIN, registration_name, webhook_id, handle_webhook)
|
||||
|
||||
async def create_cloud_hook() -> None:
|
||||
"""Create a cloud hook."""
|
||||
hook = await cloud.async_create_cloudhook(hass, webhook_id)
|
||||
hass.config_entries.async_update_entry(
|
||||
entry, data={**entry.data, CONF_CLOUDHOOK_URL: hook}
|
||||
)
|
||||
|
||||
async def manage_cloudhook(state: cloud.CloudConnectionState) -> None:
|
||||
if (
|
||||
state is cloud.CloudConnectionState.CLOUD_CONNECTED
|
||||
and CONF_CLOUDHOOK_URL not in entry.data
|
||||
):
|
||||
await create_cloud_hook()
|
||||
await async_create_cloud_hook(hass, webhook_id, entry)
|
||||
|
||||
if (
|
||||
CONF_CLOUDHOOK_URL not in registration
|
||||
CONF_CLOUDHOOK_URL not in entry.data
|
||||
and cloud.async_active_subscription(hass)
|
||||
and cloud.async_is_connected(hass)
|
||||
):
|
||||
await create_cloud_hook()
|
||||
await async_create_cloud_hook(hass, webhook_id, entry)
|
||||
|
||||
entry.async_on_unload(cloud.async_listen_connection_change(hass, manage_cloudhook))
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
@@ -35,6 +35,7 @@ from .const import (
|
||||
SCHEMA_APP_DATA,
|
||||
)
|
||||
from .helpers import supports_encryption
|
||||
from .util import async_create_cloud_hook
|
||||
|
||||
|
||||
class RegistrationsView(HomeAssistantView):
|
||||
@@ -69,8 +70,8 @@ class RegistrationsView(HomeAssistantView):
|
||||
webhook_id = secrets.token_hex()
|
||||
|
||||
if cloud.async_active_subscription(hass):
|
||||
data[CONF_CLOUDHOOK_URL] = await cloud.async_create_cloudhook(
|
||||
hass, webhook_id
|
||||
data[CONF_CLOUDHOOK_URL] = await async_create_cloud_hook(
|
||||
hass, webhook_id, None
|
||||
)
|
||||
|
||||
data[CONF_WEBHOOK_ID] = webhook_id
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
"""Mobile app utility functions."""
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from homeassistant.components import cloud
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
|
||||
from .const import (
|
||||
@@ -10,6 +13,7 @@ from .const import (
|
||||
ATTR_PUSH_TOKEN,
|
||||
ATTR_PUSH_URL,
|
||||
ATTR_PUSH_WEBSOCKET_CHANNEL,
|
||||
CONF_CLOUDHOOK_URL,
|
||||
DATA_CONFIG_ENTRIES,
|
||||
DATA_DEVICES,
|
||||
DATA_NOTIFY,
|
||||
@@ -53,3 +57,19 @@ def get_notify_service(hass: HomeAssistant, webhook_id: str) -> str | None:
|
||||
return target_service
|
||||
|
||||
return None
|
||||
|
||||
|
||||
_CLOUD_HOOK_LOCK = asyncio.Lock()
|
||||
|
||||
|
||||
async def async_create_cloud_hook(
|
||||
hass: HomeAssistant, webhook_id: str, entry: ConfigEntry | None
|
||||
) -> str:
|
||||
"""Create a cloud hook."""
|
||||
async with _CLOUD_HOOK_LOCK:
|
||||
hook = await cloud.async_get_or_create_cloudhook(hass, webhook_id)
|
||||
if entry:
|
||||
hass.config_entries.async_update_entry(
|
||||
entry, data={**entry.data, CONF_CLOUDHOOK_URL: hook}
|
||||
)
|
||||
return hook
|
||||
|
||||
@@ -190,7 +190,7 @@ BASE_STRUCT_SCHEMA = BASE_COMPONENT_SCHEMA.extend(
|
||||
vol.Optional(CONF_STRUCTURE): cv.string,
|
||||
vol.Optional(CONF_SCALE, default=1): number_validator,
|
||||
vol.Optional(CONF_OFFSET, default=0): number_validator,
|
||||
vol.Optional(CONF_PRECISION, default=0): cv.positive_int,
|
||||
vol.Optional(CONF_PRECISION): cv.positive_int,
|
||||
vol.Optional(
|
||||
CONF_SWAP,
|
||||
): vol.In(
|
||||
|
||||
@@ -185,10 +185,8 @@ class BaseStructPlatform(BasePlatform, RestoreEntity):
|
||||
self._swap = config[CONF_SWAP]
|
||||
self._data_type = config[CONF_DATA_TYPE]
|
||||
self._structure: str = config[CONF_STRUCTURE]
|
||||
self._precision = config[CONF_PRECISION]
|
||||
self._scale = config[CONF_SCALE]
|
||||
if self._scale < 1 and not self._precision:
|
||||
self._precision = 2
|
||||
self._precision = config.get(CONF_PRECISION, 2 if self._scale < 1 else 0)
|
||||
self._offset = config[CONF_OFFSET]
|
||||
self._slave_count = config.get(CONF_SLAVE_COUNT, None) or config.get(
|
||||
CONF_VIRTUAL_COUNT, 0
|
||||
|
||||
@@ -70,8 +70,8 @@ MQTT_TEXT_ATTRIBUTES_BLOCKED = frozenset(
|
||||
|
||||
def valid_text_size_configuration(config: ConfigType) -> ConfigType:
|
||||
"""Validate that the text length configuration is valid, throws if it isn't."""
|
||||
if config[CONF_MIN] >= config[CONF_MAX]:
|
||||
raise vol.Invalid("text length min must be >= max")
|
||||
if config[CONF_MIN] > config[CONF_MAX]:
|
||||
raise vol.Invalid("text length min must be <= max")
|
||||
if config[CONF_MAX] > MAX_LENGTH_STATE_STATE:
|
||||
raise vol.Invalid(f"max text length must be <= {MAX_LENGTH_STATE_STATE}")
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ from homeassistant.const import (
|
||||
)
|
||||
from homeassistant.helpers.deprecation import (
|
||||
DeprecatedConstantEnum,
|
||||
all_with_deprecated_constants,
|
||||
check_if_deprecated_constant,
|
||||
dir_with_deprecated_constants,
|
||||
)
|
||||
@@ -70,10 +71,6 @@ _DEPRECATED_MODE_AUTO: Final = DeprecatedConstantEnum(NumberMode.AUTO, "2025.1")
|
||||
_DEPRECATED_MODE_BOX: Final = DeprecatedConstantEnum(NumberMode.BOX, "2025.1")
|
||||
_DEPRECATED_MODE_SLIDER: Final = DeprecatedConstantEnum(NumberMode.SLIDER, "2025.1")
|
||||
|
||||
# Both can be removed if no deprecated constant are in this module anymore
|
||||
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
|
||||
__dir__ = partial(dir_with_deprecated_constants, module_globals=globals())
|
||||
|
||||
|
||||
class NumberDeviceClass(StrEnum):
|
||||
"""Device class for numbers."""
|
||||
@@ -481,3 +478,10 @@ DEVICE_CLASS_UNITS: dict[NumberDeviceClass, set[type[StrEnum] | str | None]] = {
|
||||
UNIT_CONVERTERS: dict[str, type[BaseUnitConverter]] = {
|
||||
NumberDeviceClass.TEMPERATURE: TemperatureConverter,
|
||||
}
|
||||
|
||||
# These can be removed if no deprecated constant are in this module anymore
|
||||
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
|
||||
__dir__ = partial(
|
||||
dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
|
||||
)
|
||||
__all__ = all_with_deprecated_constants(globals())
|
||||
|
||||
@@ -3,6 +3,7 @@ from __future__ import annotations
|
||||
|
||||
from collections.abc import Mapping
|
||||
import logging
|
||||
import socket
|
||||
from typing import Any
|
||||
|
||||
from opower import (
|
||||
@@ -38,7 +39,7 @@ async def _validate_login(
|
||||
) -> dict[str, str]:
|
||||
"""Validate login data and return any errors."""
|
||||
api = Opower(
|
||||
async_create_clientsession(hass),
|
||||
async_create_clientsession(hass, family=socket.AF_INET),
|
||||
login_data[CONF_UTILITY],
|
||||
login_data[CONF_USERNAME],
|
||||
login_data[CONF_PASSWORD],
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""Coordinator to handle Opower connections."""
|
||||
from datetime import datetime, timedelta
|
||||
import logging
|
||||
import socket
|
||||
from types import MappingProxyType
|
||||
from typing import Any, cast
|
||||
|
||||
@@ -51,7 +52,7 @@ class OpowerCoordinator(DataUpdateCoordinator[dict[str, Forecast]]):
|
||||
update_interval=timedelta(hours=12),
|
||||
)
|
||||
self.api = Opower(
|
||||
aiohttp_client.async_get_clientsession(hass),
|
||||
aiohttp_client.async_get_clientsession(hass, family=socket.AF_INET),
|
||||
entry_data[CONF_UTILITY],
|
||||
entry_data[CONF_USERNAME],
|
||||
entry_data[CONF_PASSWORD],
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/orvibo",
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["orvibo"],
|
||||
"requirements": ["orvibo==1.1.1"]
|
||||
"requirements": ["orvibo==1.1.2"]
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ async def async_setup_entry(
|
||||
class PingDeviceTracker(CoordinatorEntity[PingUpdateCoordinator], ScannerEntity):
|
||||
"""Representation of a Ping device tracker."""
|
||||
|
||||
_first_offline: datetime | None = None
|
||||
_last_seen: datetime | None = None
|
||||
|
||||
def __init__(
|
||||
self, config_entry: ConfigEntry, coordinator: PingUpdateCoordinator
|
||||
@@ -171,14 +171,12 @@ class PingDeviceTracker(CoordinatorEntity[PingUpdateCoordinator], ScannerEntity)
|
||||
def is_connected(self) -> bool:
|
||||
"""Return true if ping returns is_alive or considered home."""
|
||||
if self.coordinator.data.is_alive:
|
||||
self._first_offline = None
|
||||
return True
|
||||
self._last_seen = dt_util.utcnow()
|
||||
|
||||
now = dt_util.utcnow()
|
||||
if self._first_offline is None:
|
||||
self._first_offline = now
|
||||
|
||||
return (self._first_offline + self._consider_home_interval) > now
|
||||
return (
|
||||
self._last_seen is not None
|
||||
and (dt_util.utcnow() - self._last_seen) < self._consider_home_interval
|
||||
)
|
||||
|
||||
@property
|
||||
def entity_registry_enabled_default(self) -> bool:
|
||||
|
||||
@@ -165,6 +165,10 @@ def count_torrents_in_states(
|
||||
coordinator: QBittorrentDataCoordinator, states: list[str]
|
||||
) -> int:
|
||||
"""Count the number of torrents in specified states."""
|
||||
# When torrents are not in the returned data, there are none, return 0.
|
||||
if "torrents" not in coordinator.data:
|
||||
return 0
|
||||
|
||||
if not states:
|
||||
return len(coordinator.data["torrents"])
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ from homeassistant.helpers.config_validation import ( # noqa: F401
|
||||
)
|
||||
from homeassistant.helpers.deprecation import (
|
||||
DeprecatedConstantEnum,
|
||||
all_with_deprecated_constants,
|
||||
check_if_deprecated_constant,
|
||||
dir_with_deprecated_constants,
|
||||
)
|
||||
@@ -92,10 +93,6 @@ _DEPRECATED_SUPPORT_ACTIVITY = DeprecatedConstantEnum(
|
||||
)
|
||||
|
||||
|
||||
# Both can be removed if no deprecated constant are in this module anymore
|
||||
__getattr__ = ft.partial(check_if_deprecated_constant, module_globals=globals())
|
||||
__dir__ = ft.partial(dir_with_deprecated_constants, module_globals=globals())
|
||||
|
||||
REMOTE_SERVICE_ACTIVITY_SCHEMA = make_entity_service_schema(
|
||||
{vol.Optional(ATTR_ACTIVITY): cv.string}
|
||||
)
|
||||
@@ -262,3 +259,11 @@ class RemoteEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_)
|
||||
await self.hass.async_add_executor_job(
|
||||
ft.partial(self.delete_command, **kwargs)
|
||||
)
|
||||
|
||||
|
||||
# These can be removed if no deprecated constant are in this module anymore
|
||||
__getattr__ = ft.partial(check_if_deprecated_constant, module_globals=globals())
|
||||
__dir__ = ft.partial(
|
||||
dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
|
||||
)
|
||||
__all__ = all_with_deprecated_constants(globals())
|
||||
|
||||
@@ -18,5 +18,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/reolink",
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["reolink_aio"],
|
||||
"requirements": ["reolink-aio==0.8.5"]
|
||||
"requirements": ["reolink-aio==0.8.6"]
|
||||
}
|
||||
|
||||
@@ -61,10 +61,7 @@ def async_load_screenlogic_services(hass: HomeAssistant):
|
||||
color_num,
|
||||
)
|
||||
try:
|
||||
if not await coordinator.gateway.async_set_color_lights(color_num):
|
||||
raise HomeAssistantError(
|
||||
f"Failed to call service '{SERVICE_SET_COLOR_MODE}'"
|
||||
)
|
||||
await coordinator.gateway.async_set_color_lights(color_num)
|
||||
# Debounced refresh to catch any secondary
|
||||
# changes in the device
|
||||
await coordinator.async_request_refresh()
|
||||
|
||||
@@ -59,6 +59,7 @@ from homeassistant.helpers.config_validation import (
|
||||
PLATFORM_SCHEMA_BASE,
|
||||
)
|
||||
from homeassistant.helpers.deprecation import (
|
||||
all_with_deprecated_constants,
|
||||
check_if_deprecated_constant,
|
||||
dir_with_deprecated_constants,
|
||||
)
|
||||
@@ -120,12 +121,6 @@ __all__ = [
|
||||
"SensorStateClass",
|
||||
]
|
||||
|
||||
# As we import deprecated constants from the const module, we need to add these two functions
|
||||
# otherwise this module will be logged for using deprecated constants and not the custom component
|
||||
# Both can be removed if no deprecated constant are in this module anymore
|
||||
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
|
||||
__dir__ = partial(dir_with_deprecated_constants, module_globals=globals())
|
||||
|
||||
# mypy: disallow-any-generics
|
||||
|
||||
|
||||
@@ -955,3 +950,13 @@ def async_rounded_state(hass: HomeAssistant, entity_id: str, state: State) -> st
|
||||
value = f"{numerical_value:z.{precision}f}"
|
||||
|
||||
return value
|
||||
|
||||
|
||||
# As we import deprecated constants from the const module, we need to add these two functions
|
||||
# otherwise this module will be logged for using deprecated constants and not the custom component
|
||||
# These can be removed if no deprecated constant are in this module anymore
|
||||
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
|
||||
__dir__ = partial(
|
||||
dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
|
||||
)
|
||||
__all__ = all_with_deprecated_constants(globals())
|
||||
|
||||
@@ -38,6 +38,7 @@ from homeassistant.const import (
|
||||
)
|
||||
from homeassistant.helpers.deprecation import (
|
||||
DeprecatedConstantEnum,
|
||||
all_with_deprecated_constants,
|
||||
check_if_deprecated_constant,
|
||||
dir_with_deprecated_constants,
|
||||
)
|
||||
@@ -468,10 +469,6 @@ _DEPRECATED_STATE_CLASS_TOTAL_INCREASING: Final = DeprecatedConstantEnum(
|
||||
)
|
||||
STATE_CLASSES: Final[list[str]] = [cls.value for cls in SensorStateClass]
|
||||
|
||||
# Both can be removed if no deprecated constant are in this module anymore
|
||||
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
|
||||
__dir__ = partial(dir_with_deprecated_constants, module_globals=globals())
|
||||
|
||||
UNIT_CONVERTERS: dict[SensorDeviceClass | str | None, type[BaseUnitConverter]] = {
|
||||
SensorDeviceClass.ATMOSPHERIC_PRESSURE: PressureConverter,
|
||||
SensorDeviceClass.CURRENT: ElectricCurrentConverter,
|
||||
@@ -631,3 +628,10 @@ DEVICE_CLASS_STATE_CLASSES: dict[SensorDeviceClass, set[SensorStateClass]] = {
|
||||
SensorDeviceClass.WEIGHT: {SensorStateClass.MEASUREMENT},
|
||||
SensorDeviceClass.WIND_SPEED: {SensorStateClass.MEASUREMENT},
|
||||
}
|
||||
|
||||
# These can be removed if no deprecated constant are in this module anymore
|
||||
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
|
||||
__dir__ = partial(
|
||||
dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
|
||||
)
|
||||
__all__ = all_with_deprecated_constants(globals())
|
||||
|
||||
@@ -25,6 +25,7 @@ from homeassistant.helpers.selector import SelectSelector, SelectSelectorConfig
|
||||
|
||||
from .const import (
|
||||
CONF_BLE_SCANNER_MODE,
|
||||
CONF_GEN,
|
||||
CONF_SLEEP_PERIOD,
|
||||
DOMAIN,
|
||||
LOGGER,
|
||||
@@ -35,6 +36,7 @@ from .coordinator import async_reconnect_soon
|
||||
from .utils import (
|
||||
get_block_device_sleep_period,
|
||||
get_coap_context,
|
||||
get_device_entry_gen,
|
||||
get_info_auth,
|
||||
get_info_gen,
|
||||
get_model_name,
|
||||
@@ -84,7 +86,7 @@ async def validate_input(
|
||||
"title": rpc_device.name,
|
||||
CONF_SLEEP_PERIOD: sleep_period,
|
||||
"model": rpc_device.shelly.get("model"),
|
||||
"gen": gen,
|
||||
CONF_GEN: gen,
|
||||
}
|
||||
|
||||
# Gen1
|
||||
@@ -99,7 +101,7 @@ async def validate_input(
|
||||
"title": block_device.name,
|
||||
CONF_SLEEP_PERIOD: get_block_device_sleep_period(block_device.settings),
|
||||
"model": block_device.model,
|
||||
"gen": gen,
|
||||
CONF_GEN: gen,
|
||||
}
|
||||
|
||||
|
||||
@@ -153,7 +155,7 @@ class ShellyConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
**user_input,
|
||||
CONF_SLEEP_PERIOD: device_info[CONF_SLEEP_PERIOD],
|
||||
"model": device_info["model"],
|
||||
"gen": device_info["gen"],
|
||||
CONF_GEN: device_info[CONF_GEN],
|
||||
},
|
||||
)
|
||||
errors["base"] = "firmware_not_fully_provisioned"
|
||||
@@ -190,7 +192,7 @@ class ShellyConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
CONF_HOST: self.host,
|
||||
CONF_SLEEP_PERIOD: device_info[CONF_SLEEP_PERIOD],
|
||||
"model": device_info["model"],
|
||||
"gen": device_info["gen"],
|
||||
CONF_GEN: device_info[CONF_GEN],
|
||||
},
|
||||
)
|
||||
errors["base"] = "firmware_not_fully_provisioned"
|
||||
@@ -288,7 +290,7 @@ class ShellyConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
"host": self.host,
|
||||
CONF_SLEEP_PERIOD: self.device_info[CONF_SLEEP_PERIOD],
|
||||
"model": self.device_info["model"],
|
||||
"gen": self.device_info["gen"],
|
||||
CONF_GEN: self.device_info[CONF_GEN],
|
||||
},
|
||||
)
|
||||
self._set_confirm_only()
|
||||
@@ -321,7 +323,7 @@ class ShellyConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
except (DeviceConnectionError, InvalidAuthError, FirmwareUnsupported):
|
||||
return self.async_abort(reason="reauth_unsuccessful")
|
||||
|
||||
if self.entry.data.get("gen", 1) != 1:
|
||||
if get_device_entry_gen(self.entry) != 1:
|
||||
user_input[CONF_USERNAME] = "admin"
|
||||
try:
|
||||
await validate_input(self.hass, host, info, user_input)
|
||||
@@ -334,7 +336,7 @@ class ShellyConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
await self.hass.config_entries.async_reload(self.entry.entry_id)
|
||||
return self.async_abort(reason="reauth_successful")
|
||||
|
||||
if self.entry.data.get("gen", 1) in BLOCK_GENERATIONS:
|
||||
if get_device_entry_gen(self.entry) in BLOCK_GENERATIONS:
|
||||
schema = {
|
||||
vol.Required(CONF_USERNAME): str,
|
||||
vol.Required(CONF_PASSWORD): str,
|
||||
@@ -363,7 +365,7 @@ class ShellyConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
def async_supports_options_flow(cls, config_entry: ConfigEntry) -> bool:
|
||||
"""Return options flow support for this handler."""
|
||||
return (
|
||||
config_entry.data.get("gen") in RPC_GENERATIONS
|
||||
get_device_entry_gen(config_entry) in RPC_GENERATIONS
|
||||
and not config_entry.data.get(CONF_SLEEP_PERIOD)
|
||||
and config_entry.data.get("model") != MODEL_WALL_DISPLAY
|
||||
)
|
||||
|
||||
@@ -214,3 +214,5 @@ DEVICES_WITHOUT_FIRMWARE_CHANGELOG = (
|
||||
MODEL_MOTION_2,
|
||||
MODEL_VALVE,
|
||||
)
|
||||
|
||||
CONF_GEN = "gen"
|
||||
|
||||
@@ -57,7 +57,11 @@ from .const import (
|
||||
UPDATE_PERIOD_MULTIPLIER,
|
||||
BLEScannerMode,
|
||||
)
|
||||
from .utils import get_rpc_device_wakeup_period, update_device_fw_info
|
||||
from .utils import (
|
||||
get_device_entry_gen,
|
||||
get_rpc_device_wakeup_period,
|
||||
update_device_fw_info,
|
||||
)
|
||||
|
||||
_DeviceT = TypeVar("_DeviceT", bound="BlockDevice|RpcDevice")
|
||||
|
||||
@@ -135,7 +139,7 @@ class ShellyCoordinatorBase(DataUpdateCoordinator[None], Generic[_DeviceT]):
|
||||
manufacturer="Shelly",
|
||||
model=aioshelly.const.MODEL_NAMES.get(self.model, self.model),
|
||||
sw_version=self.sw_version,
|
||||
hw_version=f"gen{self.device.gen} ({self.model})",
|
||||
hw_version=f"gen{get_device_entry_gen(self.entry)} ({self.model})",
|
||||
configuration_url=f"http://{self.entry.data[CONF_HOST]}",
|
||||
)
|
||||
self.device_id = device_entry.id
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["aioshelly"],
|
||||
"quality_scale": "platinum",
|
||||
"requirements": ["aioshelly==7.0.0"],
|
||||
"requirements": ["aioshelly==7.1.0"],
|
||||
"zeroconf": [
|
||||
{
|
||||
"type": "_http._tcp.local.",
|
||||
|
||||
@@ -36,6 +36,7 @@ from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.entity_registry import RegistryEntry
|
||||
from homeassistant.helpers.typing import StateType
|
||||
from homeassistant.util.enum import try_parse_enum
|
||||
|
||||
from .const import CONF_SLEEP_PERIOD, SHAIR_MAX_WORK_HOURS
|
||||
from .coordinator import ShellyBlockCoordinator, ShellyRpcCoordinator
|
||||
@@ -969,7 +970,7 @@ def _build_block_description(entry: RegistryEntry) -> BlockSensorDescription:
|
||||
name="",
|
||||
icon=entry.original_icon,
|
||||
native_unit_of_measurement=entry.unit_of_measurement,
|
||||
device_class=entry.original_device_class,
|
||||
device_class=try_parse_enum(SensorDeviceClass, entry.original_device_class),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ from homeassistant.util.dt import utcnow
|
||||
from .const import (
|
||||
BASIC_INPUTS_EVENTS_TYPES,
|
||||
CONF_COAP_PORT,
|
||||
CONF_GEN,
|
||||
DEFAULT_COAP_PORT,
|
||||
DEVICES_WITHOUT_FIRMWARE_CHANGELOG,
|
||||
DOMAIN,
|
||||
@@ -281,7 +282,7 @@ def get_info_auth(info: dict[str, Any]) -> bool:
|
||||
|
||||
def get_info_gen(info: dict[str, Any]) -> int:
|
||||
"""Return the device generation from shelly info."""
|
||||
return int(info.get("gen", 1))
|
||||
return int(info.get(CONF_GEN, 1))
|
||||
|
||||
|
||||
def get_model_name(info: dict[str, Any]) -> str:
|
||||
@@ -325,7 +326,7 @@ def get_rpc_entity_name(
|
||||
|
||||
def get_device_entry_gen(entry: ConfigEntry) -> int:
|
||||
"""Return the device generation from config entry."""
|
||||
return entry.data.get("gen", 1)
|
||||
return entry.data.get(CONF_GEN, 1)
|
||||
|
||||
|
||||
def get_rpc_key_instances(keys_dict: dict[str, Any], key: str) -> list[str]:
|
||||
|
||||
@@ -17,6 +17,7 @@ from homeassistant.helpers.config_validation import ( # noqa: F401
|
||||
PLATFORM_SCHEMA_BASE,
|
||||
)
|
||||
from homeassistant.helpers.deprecation import (
|
||||
all_with_deprecated_constants,
|
||||
check_if_deprecated_constant,
|
||||
dir_with_deprecated_constants,
|
||||
)
|
||||
@@ -53,12 +54,6 @@ TURN_ON_SCHEMA = {
|
||||
vol.Optional(ATTR_VOLUME_LEVEL): cv.small_float,
|
||||
}
|
||||
|
||||
# As we import deprecated constants from the const module, we need to add these two functions
|
||||
# otherwise this module will be logged for using deprecated constants and not the custom component
|
||||
# Both can be removed if no deprecated constant are in this module anymore
|
||||
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
|
||||
__dir__ = partial(dir_with_deprecated_constants, module_globals=globals())
|
||||
|
||||
|
||||
class SirenTurnOnServiceParameters(TypedDict, total=False):
|
||||
"""Represent possible parameters to siren.turn_on service data dict type."""
|
||||
@@ -218,3 +213,13 @@ class SirenEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||
self._report_deprecated_supported_features_values(new_features)
|
||||
return new_features
|
||||
return features
|
||||
|
||||
|
||||
# As we import deprecated constants from the const module, we need to add these two functions
|
||||
# otherwise this module will be logged for using deprecated constants and not the custom component
|
||||
# These can be removed if no deprecated constant are in this module anymore
|
||||
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
|
||||
__dir__ = partial(
|
||||
dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
|
||||
)
|
||||
__all__ = all_with_deprecated_constants(globals())
|
||||
|
||||
@@ -6,6 +6,7 @@ from typing import Final
|
||||
|
||||
from homeassistant.helpers.deprecation import (
|
||||
DeprecatedConstantEnum,
|
||||
all_with_deprecated_constants,
|
||||
check_if_deprecated_constant,
|
||||
dir_with_deprecated_constants,
|
||||
)
|
||||
@@ -47,6 +48,9 @@ _DEPRECATED_SUPPORT_DURATION: Final = DeprecatedConstantEnum(
|
||||
SirenEntityFeature.DURATION, "2025.1"
|
||||
)
|
||||
|
||||
# Both can be removed if no deprecated constant are in this module anymore
|
||||
# These can be removed if no deprecated constant are in this module anymore
|
||||
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
|
||||
__dir__ = partial(dir_with_deprecated_constants, module_globals=globals())
|
||||
__dir__ = partial(
|
||||
dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
|
||||
)
|
||||
__all__ = all_with_deprecated_constants(globals())
|
||||
|
||||
@@ -61,6 +61,10 @@ REPEAT_MODE_MAPPING_TO_SPOTIFY = {
|
||||
value: key for key, value in REPEAT_MODE_MAPPING_TO_HA.items()
|
||||
}
|
||||
|
||||
# This is a minimal representation of the DJ playlist that Spotify now offers
|
||||
# The DJ is not fully integrated with the playlist API, so needs to have the playlist response mocked in order to maintain functionality
|
||||
SPOTIFY_DJ_PLAYLIST = {"uri": "spotify:playlist:37i9dQZF1EYkqdzj48dyYq", "name": "DJ"}
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
@@ -423,7 +427,19 @@ class SpotifyMediaPlayer(MediaPlayerEntity):
|
||||
if context and (self._playlist is None or self._playlist["uri"] != uri):
|
||||
self._playlist = None
|
||||
if context["type"] == MediaType.PLAYLIST:
|
||||
self._playlist = self.data.client.playlist(uri)
|
||||
# The Spotify API does not currently support doing a lookup for the DJ playlist, so just use the minimal mock playlist object
|
||||
if uri == SPOTIFY_DJ_PLAYLIST["uri"]:
|
||||
self._playlist = SPOTIFY_DJ_PLAYLIST
|
||||
else:
|
||||
# Make sure any playlist lookups don't break the current playback state update
|
||||
try:
|
||||
self._playlist = self.data.client.playlist(uri)
|
||||
except SpotifyException:
|
||||
_LOGGER.debug(
|
||||
"Unable to load spotify playlist '%s'. Continuing without playlist data",
|
||||
uri,
|
||||
)
|
||||
self._playlist = None
|
||||
|
||||
device = self._currently_playing.get("device")
|
||||
if device is not None:
|
||||
|
||||
@@ -107,12 +107,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
def set_away_mode(service: ServiceCall) -> None:
|
||||
"""Set the StreamLabsWater Away Mode."""
|
||||
away_mode = service.data.get(ATTR_AWAY_MODE)
|
||||
location_id = (
|
||||
service.data.get(CONF_LOCATION_ID) or list(coordinator.data.values())[0]
|
||||
)
|
||||
location_id = service.data.get(CONF_LOCATION_ID) or list(coordinator.data)[0]
|
||||
client.update_location(location_id, away_mode)
|
||||
|
||||
hass.services.register(
|
||||
hass.services.async_register(
|
||||
DOMAIN, SERVICE_SET_AWAY_MODE, set_away_mode, schema=SET_AWAY_MODE_SCHEMA
|
||||
)
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ class StreamlabsCoordinator(DataUpdateCoordinator[dict[str, StreamlabsData]]):
|
||||
def _update_data(self) -> dict[str, StreamlabsData]:
|
||||
locations = self.client.get_locations()
|
||||
res = {}
|
||||
for location in locations:
|
||||
for location in locations["locations"]:
|
||||
location_id = location["locationId"]
|
||||
water_usage = self.client.get_water_usage_summary(location_id)
|
||||
res[location_id] = StreamlabsData(
|
||||
|
||||
@@ -27,7 +27,7 @@ async def async_setup_entry(
|
||||
|
||||
entities = []
|
||||
|
||||
for location_id in coordinator.data.values():
|
||||
for location_id in coordinator.data:
|
||||
entities.extend(
|
||||
[
|
||||
StreamLabsDailyUsage(coordinator, location_id),
|
||||
|
||||
@@ -10,6 +10,7 @@ from opendata_transport.exceptions import (
|
||||
from homeassistant import config_entries, core
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.exceptions import ConfigEntryError, ConfigEntryNotReady
|
||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
|
||||
from .const import CONF_DESTINATION, CONF_START, DOMAIN
|
||||
@@ -65,3 +66,51 @@ async def async_unload_entry(
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
|
||||
|
||||
async def async_migrate_entry(
|
||||
hass: core.HomeAssistant, config_entry: config_entries.ConfigEntry
|
||||
) -> bool:
|
||||
"""Migrate config entry."""
|
||||
_LOGGER.debug("Migrating from version %s", config_entry.version)
|
||||
|
||||
if config_entry.minor_version > 3:
|
||||
# This means the user has downgraded from a future version
|
||||
return False
|
||||
|
||||
if config_entry.minor_version == 1:
|
||||
# Remove wrongly registered devices and entries
|
||||
new_unique_id = (
|
||||
f"{config_entry.data[CONF_START]} {config_entry.data[CONF_DESTINATION]}"
|
||||
)
|
||||
entity_registry = er.async_get(hass)
|
||||
device_registry = dr.async_get(hass)
|
||||
device_entries = dr.async_entries_for_config_entry(
|
||||
device_registry, config_entry_id=config_entry.entry_id
|
||||
)
|
||||
for dev in device_entries:
|
||||
device_registry.async_remove_device(dev.id)
|
||||
|
||||
entity_id = entity_registry.async_get_entity_id(
|
||||
Platform.SENSOR, DOMAIN, "None_departure"
|
||||
)
|
||||
if entity_id:
|
||||
entity_registry.async_update_entity(
|
||||
entity_id=entity_id,
|
||||
new_unique_id=f"{new_unique_id}_departure",
|
||||
)
|
||||
_LOGGER.debug(
|
||||
"Faulty entity with unique_id 'None_departure' migrated to new unique_id '%s'",
|
||||
f"{new_unique_id}_departure",
|
||||
)
|
||||
|
||||
# Set a valid unique id for config entries
|
||||
config_entry.unique_id = new_unique_id
|
||||
config_entry.minor_version = 2
|
||||
hass.config_entries.async_update_entry(config_entry)
|
||||
|
||||
_LOGGER.debug(
|
||||
"Migration to minor version %s successful", config_entry.minor_version
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
@@ -31,6 +31,7 @@ class SwissPublicTransportConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
"""Swiss public transport config flow."""
|
||||
|
||||
VERSION = 1
|
||||
MINOR_VERSION = 2
|
||||
|
||||
async def async_step_user(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
@@ -59,6 +60,9 @@ class SwissPublicTransportConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
_LOGGER.exception("Unknown error")
|
||||
errors["base"] = "unknown"
|
||||
else:
|
||||
await self.async_set_unique_id(
|
||||
f"{user_input[CONF_START]} {user_input[CONF_DESTINATION]}"
|
||||
)
|
||||
return self.async_create_entry(
|
||||
title=f"{user_input[CONF_START]} {user_input[CONF_DESTINATION]}",
|
||||
data=user_input,
|
||||
@@ -98,6 +102,9 @@ class SwissPublicTransportConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
)
|
||||
return self.async_abort(reason="unknown")
|
||||
|
||||
await self.async_set_unique_id(
|
||||
f"{import_input[CONF_START]} {import_input[CONF_DESTINATION]}"
|
||||
)
|
||||
return self.async_create_entry(
|
||||
title=import_input[CONF_NAME],
|
||||
data=import_input,
|
||||
|
||||
@@ -122,15 +122,25 @@ class SwissPublicTransportSensor(
|
||||
entry_type=DeviceEntryType.SERVICE,
|
||||
)
|
||||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Prepare the extra attributes at start."""
|
||||
self._async_update_attrs()
|
||||
await super().async_added_to_hass()
|
||||
|
||||
@callback
|
||||
def _handle_coordinator_update(self) -> None:
|
||||
"""Handle the state update and prepare the extra state attributes."""
|
||||
self._async_update_attrs()
|
||||
return super()._handle_coordinator_update()
|
||||
|
||||
@callback
|
||||
def _async_update_attrs(self) -> None:
|
||||
"""Update the extra state attributes based on the coordinator data."""
|
||||
self._attr_extra_state_attributes = {
|
||||
key: value
|
||||
for key, value in self.coordinator.data.items()
|
||||
if key not in {"departure"}
|
||||
}
|
||||
return super()._handle_coordinator_update()
|
||||
|
||||
@property
|
||||
def native_value(self) -> str:
|
||||
|
||||
@@ -23,6 +23,7 @@ from homeassistant.helpers.config_validation import ( # noqa: F401
|
||||
)
|
||||
from homeassistant.helpers.deprecation import (
|
||||
DeprecatedConstantEnum,
|
||||
all_with_deprecated_constants,
|
||||
check_if_deprecated_constant,
|
||||
dir_with_deprecated_constants,
|
||||
)
|
||||
@@ -66,10 +67,6 @@ _DEPRECATED_DEVICE_CLASS_SWITCH = DeprecatedConstantEnum(
|
||||
SwitchDeviceClass.SWITCH, "2025.1"
|
||||
)
|
||||
|
||||
# Both can be removed if no deprecated constant are in this module anymore
|
||||
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
|
||||
__dir__ = partial(dir_with_deprecated_constants, module_globals=globals())
|
||||
|
||||
# mypy: disallow-any-generics
|
||||
|
||||
|
||||
@@ -133,3 +130,11 @@ class SwitchEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_)
|
||||
if hasattr(self, "entity_description"):
|
||||
return self.entity_description.device_class
|
||||
return None
|
||||
|
||||
|
||||
# These can be removed if no deprecated constant are in this module anymore
|
||||
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
|
||||
__dir__ = partial(
|
||||
dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
|
||||
)
|
||||
__all__ = all_with_deprecated_constants(globals())
|
||||
|
||||
@@ -98,6 +98,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
# connectable means we can make connections to the device
|
||||
connectable = switchbot_model in CONNECTABLE_SUPPORTED_MODEL_TYPES
|
||||
address: str = entry.data[CONF_ADDRESS]
|
||||
|
||||
await switchbot.close_stale_connections_by_address(address)
|
||||
|
||||
ble_device = bluetooth.async_ble_device_from_address(
|
||||
hass, address.upper(), connectable
|
||||
)
|
||||
@@ -106,7 +109,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
f"Could not find Switchbot {sensor_type} with address {address}"
|
||||
)
|
||||
|
||||
await switchbot.close_stale_connections(ble_device)
|
||||
cls = CLASS_BY_DEVICE.get(sensor_type, switchbot.SwitchbotDevice)
|
||||
if cls is switchbot.SwitchbotLock:
|
||||
try:
|
||||
|
||||
@@ -89,8 +89,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
|
||||
# New device - create device
|
||||
_LOGGER.info(
|
||||
"Discovered Switcher device - id: %s, name: %s, type: %s (%s)",
|
||||
"Discovered Switcher device - id: %s, key: %s, name: %s, type: %s (%s)",
|
||||
device.device_id,
|
||||
device.device_key,
|
||||
device.name,
|
||||
device.device_type.value,
|
||||
device.device_type.hex_rep,
|
||||
|
||||
@@ -142,7 +142,9 @@ class SwitcherThermostatButtonEntity(
|
||||
|
||||
try:
|
||||
async with SwitcherType2Api(
|
||||
self.coordinator.data.ip_address, self.coordinator.data.device_id
|
||||
self.coordinator.data.ip_address,
|
||||
self.coordinator.data.device_id,
|
||||
self.coordinator.data.device_key,
|
||||
) as swapi:
|
||||
response = await self.entity_description.press_fn(swapi, self._remote)
|
||||
except (asyncio.TimeoutError, OSError, RuntimeError) as err:
|
||||
|
||||
@@ -162,7 +162,9 @@ class SwitcherClimateEntity(
|
||||
|
||||
try:
|
||||
async with SwitcherType2Api(
|
||||
self.coordinator.data.ip_address, self.coordinator.data.device_id
|
||||
self.coordinator.data.ip_address,
|
||||
self.coordinator.data.device_id,
|
||||
self.coordinator.data.device_key,
|
||||
) as swapi:
|
||||
response = await swapi.control_breeze_device(self._remote, **kwargs)
|
||||
except (asyncio.TimeoutError, OSError, RuntimeError) as err:
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user