forked from home-assistant/core
Compare commits
89 Commits
2024.12.0b
...
llm-neverm
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf259c2278 | ||
|
|
e8ced4fa12 | ||
|
|
d9cef1e708 | ||
|
|
a760786faf | ||
|
|
8c6a24c368 | ||
|
|
24bd61be3b | ||
|
|
1abd2209b3 | ||
|
|
aa206c7608 | ||
|
|
87020e8945 | ||
|
|
dd62fb387e | ||
|
|
c19038ced6 | ||
|
|
6144cc26ba | ||
|
|
920c958ec7 | ||
|
|
0fc365a114 | ||
|
|
954ac0d288 | ||
|
|
28cfa37248 | ||
|
|
24f7bae5f2 | ||
|
|
8e12fbff88 | ||
|
|
5c8fb5ec2c | ||
|
|
a68cf21179 | ||
|
|
d596b4169d | ||
|
|
8b467268df | ||
|
|
6dd93253c6 | ||
|
|
9db6f0ffc4 | ||
|
|
889ac1552b | ||
|
|
18db16b82c | ||
|
|
1f9ecfe839 | ||
|
|
4d32fe97c3 | ||
|
|
8feb6c7e06 | ||
|
|
0b36a6d7f3 | ||
|
|
837716b69e | ||
|
|
1a9ab07742 | ||
|
|
8862c5c4d8 | ||
|
|
87320609dc | ||
|
|
62e788c7da | ||
|
|
bbce183faf | ||
|
|
0389800e2a | ||
|
|
0c5c09390c | ||
|
|
57b099c2aa | ||
|
|
ed408eb1a1 | ||
|
|
f7d2d06c9b | ||
|
|
3071aa2da1 | ||
|
|
474544abd8 | ||
|
|
dc064237ca | ||
|
|
a0584a0516 | ||
|
|
96dfa0e0cf | ||
|
|
00d82363fe | ||
|
|
c4e5b59326 | ||
|
|
d9832f8c3a | ||
|
|
f41bc98fe2 | ||
|
|
3a76bfb857 | ||
|
|
6ce5c89711 | ||
|
|
9d387acb97 | ||
|
|
1d09a5bf89 | ||
|
|
a01e7cd6cf | ||
|
|
3e0326dd66 | ||
|
|
4d27a32905 | ||
|
|
c5f68bcc58 | ||
|
|
3866176e1d | ||
|
|
a67045ee6c | ||
|
|
54ff6feadc | ||
|
|
fd14add67b | ||
|
|
b28f352902 | ||
|
|
fb152c7d22 | ||
|
|
be81fd86d3 | ||
|
|
28ec8272ee | ||
|
|
717f2ee206 | ||
|
|
5972da495a | ||
|
|
2fcd9be3f2 | ||
|
|
a0ea9a1e83 | ||
|
|
a831c37511 | ||
|
|
d26c7a0536 | ||
|
|
4257277086 | ||
|
|
fe2bca51a4 | ||
|
|
17236a5698 | ||
|
|
39c2a529d1 | ||
|
|
0f5e0dd4bf | ||
|
|
eac6673c2b | ||
|
|
bf4d6d2029 | ||
|
|
f61a5b78cc | ||
|
|
cc9a97a5cf | ||
|
|
cf7acb5ae8 | ||
|
|
6edb2c0252 | ||
|
|
fb4d86196e | ||
|
|
44fc5c7871 | ||
|
|
c82e408138 | ||
|
|
7110df04e6 | ||
|
|
1635074aae | ||
|
|
381d5453b1 |
8
.github/workflows/ci.yaml
vendored
8
.github/workflows/ci.yaml
vendored
@@ -40,7 +40,7 @@ env:
|
||||
CACHE_VERSION: 11
|
||||
UV_CACHE_VERSION: 1
|
||||
MYPY_CACHE_VERSION: 9
|
||||
HA_SHORT_VERSION: "2024.12"
|
||||
HA_SHORT_VERSION: "2025.1"
|
||||
DEFAULT_PYTHON: "3.12"
|
||||
ALL_PYTHON_VERSIONS: "['3.12', '3.13']"
|
||||
# 10.3 is the oldest supported version
|
||||
@@ -819,6 +819,12 @@ jobs:
|
||||
needs:
|
||||
- info
|
||||
- base
|
||||
- gen-requirements-all
|
||||
- hassfest
|
||||
- lint-other
|
||||
- lint-ruff
|
||||
- lint-ruff-format
|
||||
- mypy
|
||||
name: Split tests for full run
|
||||
steps:
|
||||
- name: Install additional OS dependencies
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
repos:
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.8.0
|
||||
rev: v0.8.1
|
||||
hooks:
|
||||
- id: ruff
|
||||
args:
|
||||
|
||||
@@ -41,6 +41,7 @@ homeassistant.util.unit_system
|
||||
# --- Add components below this line ---
|
||||
homeassistant.components
|
||||
homeassistant.components.abode.*
|
||||
homeassistant.components.acaia.*
|
||||
homeassistant.components.accuweather.*
|
||||
homeassistant.components.acer_projector.*
|
||||
homeassistant.components.acmeda.*
|
||||
@@ -405,6 +406,7 @@ homeassistant.components.ruuvitag_ble.*
|
||||
homeassistant.components.samsungtv.*
|
||||
homeassistant.components.scene.*
|
||||
homeassistant.components.schedule.*
|
||||
homeassistant.components.schlage.*
|
||||
homeassistant.components.scrape.*
|
||||
homeassistant.components.script.*
|
||||
homeassistant.components.search.*
|
||||
|
||||
@@ -25,5 +25,5 @@
|
||||
"integration_type": "device",
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["aioacaia"],
|
||||
"requirements": ["aioacaia==0.1.9"]
|
||||
"requirements": ["aioacaia==0.1.10"]
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from datetime import timedelta
|
||||
from functools import partial
|
||||
import logging
|
||||
from typing import TYPE_CHECKING, Any, Final, final
|
||||
|
||||
@@ -27,11 +26,6 @@ from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import ServiceValidationError
|
||||
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,
|
||||
)
|
||||
from homeassistant.helpers.entity import Entity, EntityDescription
|
||||
from homeassistant.helpers.entity_component import EntityComponent
|
||||
from homeassistant.helpers.entity_platform import EntityPlatform
|
||||
@@ -39,15 +33,7 @@ from homeassistant.helpers.frame import ReportBehavior, report_usage
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
from homeassistant.util.hass_dict import HassKey
|
||||
|
||||
from .const import ( # noqa: F401
|
||||
_DEPRECATED_FORMAT_NUMBER,
|
||||
_DEPRECATED_FORMAT_TEXT,
|
||||
_DEPRECATED_SUPPORT_ALARM_ARM_AWAY,
|
||||
_DEPRECATED_SUPPORT_ALARM_ARM_CUSTOM_BYPASS,
|
||||
_DEPRECATED_SUPPORT_ALARM_ARM_HOME,
|
||||
_DEPRECATED_SUPPORT_ALARM_ARM_NIGHT,
|
||||
_DEPRECATED_SUPPORT_ALARM_ARM_VACATION,
|
||||
_DEPRECATED_SUPPORT_ALARM_TRIGGER,
|
||||
from .const import (
|
||||
ATTR_CHANGED_BY,
|
||||
ATTR_CODE_ARM_REQUIRED,
|
||||
DOMAIN,
|
||||
@@ -412,13 +398,3 @@ class AlarmControlPanelEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_A
|
||||
self._alarm_control_panel_option_default_code = default_code
|
||||
return
|
||||
self._alarm_control_panel_option_default_code = None
|
||||
|
||||
|
||||
# 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())
|
||||
|
||||
@@ -1,16 +1,8 @@
|
||||
"""Provides the constants needed for component."""
|
||||
|
||||
from enum import IntFlag, StrEnum
|
||||
from functools import partial
|
||||
from typing import Final
|
||||
|
||||
from homeassistant.helpers.deprecation import (
|
||||
DeprecatedConstantEnum,
|
||||
all_with_deprecated_constants,
|
||||
check_if_deprecated_constant,
|
||||
dir_with_deprecated_constants,
|
||||
)
|
||||
|
||||
DOMAIN: Final = "alarm_control_panel"
|
||||
|
||||
ATTR_CHANGED_BY: Final = "changed_by"
|
||||
@@ -39,12 +31,6 @@ class CodeFormat(StrEnum):
|
||||
NUMBER = "number"
|
||||
|
||||
|
||||
# These constants are deprecated as of Home Assistant 2022.5, can be removed in 2025.1
|
||||
# Please use the CodeFormat enum instead.
|
||||
_DEPRECATED_FORMAT_TEXT: Final = DeprecatedConstantEnum(CodeFormat.TEXT, "2025.1")
|
||||
_DEPRECATED_FORMAT_NUMBER: Final = DeprecatedConstantEnum(CodeFormat.NUMBER, "2025.1")
|
||||
|
||||
|
||||
class AlarmControlPanelEntityFeature(IntFlag):
|
||||
"""Supported features of the alarm control panel entity."""
|
||||
|
||||
@@ -56,27 +42,6 @@ class AlarmControlPanelEntityFeature(IntFlag):
|
||||
ARM_VACATION = 32
|
||||
|
||||
|
||||
# These constants are deprecated as of Home Assistant 2022.5
|
||||
# Please use the AlarmControlPanelEntityFeature enum instead.
|
||||
_DEPRECATED_SUPPORT_ALARM_ARM_HOME: Final = DeprecatedConstantEnum(
|
||||
AlarmControlPanelEntityFeature.ARM_HOME, "2025.1"
|
||||
)
|
||||
_DEPRECATED_SUPPORT_ALARM_ARM_AWAY: Final = DeprecatedConstantEnum(
|
||||
AlarmControlPanelEntityFeature.ARM_AWAY, "2025.1"
|
||||
)
|
||||
_DEPRECATED_SUPPORT_ALARM_ARM_NIGHT: Final = DeprecatedConstantEnum(
|
||||
AlarmControlPanelEntityFeature.ARM_NIGHT, "2025.1"
|
||||
)
|
||||
_DEPRECATED_SUPPORT_ALARM_TRIGGER: Final = DeprecatedConstantEnum(
|
||||
AlarmControlPanelEntityFeature.TRIGGER, "2025.1"
|
||||
)
|
||||
_DEPRECATED_SUPPORT_ALARM_ARM_CUSTOM_BYPASS: Final = DeprecatedConstantEnum(
|
||||
AlarmControlPanelEntityFeature.ARM_CUSTOM_BYPASS, "2025.1"
|
||||
)
|
||||
_DEPRECATED_SUPPORT_ALARM_ARM_VACATION: Final = DeprecatedConstantEnum(
|
||||
AlarmControlPanelEntityFeature.ARM_VACATION, "2025.1"
|
||||
)
|
||||
|
||||
CONDITION_TRIGGERED: Final = "is_triggered"
|
||||
CONDITION_DISARMED: Final = "is_disarmed"
|
||||
CONDITION_ARMED_HOME: Final = "is_armed_home"
|
||||
@@ -84,10 +49,3 @@ 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())
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/apple_tv",
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["pyatv", "srptools"],
|
||||
"requirements": ["pyatv==0.15.1"],
|
||||
"requirements": ["pyatv==0.16.0"],
|
||||
"zeroconf": [
|
||||
"_mediaremotetv._tcp.local.",
|
||||
"_companion-link._tcp.local.",
|
||||
|
||||
@@ -1040,7 +1040,7 @@ class PipelineRun:
|
||||
:= await conversation.async_handle_sentence_triggers(
|
||||
self.hass, user_input
|
||||
)
|
||||
):
|
||||
) is not None:
|
||||
# Sentence trigger matched
|
||||
trigger_response = intent.IntentResponse(
|
||||
self.pipeline.conversation_language
|
||||
|
||||
@@ -6,7 +6,6 @@ from abc import ABC, abstractmethod
|
||||
import asyncio
|
||||
from collections.abc import Callable, Mapping
|
||||
from dataclasses import dataclass
|
||||
from functools import partial
|
||||
import logging
|
||||
from typing import Any, Protocol, cast
|
||||
|
||||
@@ -51,12 +50,6 @@ from homeassistant.core import (
|
||||
from homeassistant.exceptions import HomeAssistantError, ServiceNotFound, TemplateError
|
||||
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,
|
||||
)
|
||||
from homeassistant.helpers.entity import ToggleEntity
|
||||
from homeassistant.helpers.entity_component import EntityComponent
|
||||
from homeassistant.helpers.issue_registry import (
|
||||
@@ -86,12 +79,7 @@ from homeassistant.helpers.trace import (
|
||||
trace_get,
|
||||
trace_path,
|
||||
)
|
||||
from homeassistant.helpers.trigger import (
|
||||
TriggerActionType,
|
||||
TriggerData,
|
||||
TriggerInfo,
|
||||
async_initialize_triggers,
|
||||
)
|
||||
from homeassistant.helpers.trigger import async_initialize_triggers
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
from homeassistant.loader import bind_hass
|
||||
from homeassistant.util.dt import parse_datetime
|
||||
@@ -137,20 +125,6 @@ class IfAction(Protocol):
|
||||
"""AND all conditions."""
|
||||
|
||||
|
||||
# AutomationActionType, AutomationTriggerData,
|
||||
# and AutomationTriggerInfo are deprecated as of 2022.9.
|
||||
# Can be removed in 2025.1
|
||||
_DEPRECATED_AutomationActionType = DeprecatedConstant(
|
||||
TriggerActionType, "TriggerActionType", "2025.1"
|
||||
)
|
||||
_DEPRECATED_AutomationTriggerData = DeprecatedConstant(
|
||||
TriggerData, "TriggerData", "2025.1"
|
||||
)
|
||||
_DEPRECATED_AutomationTriggerInfo = DeprecatedConstant(
|
||||
TriggerInfo, "TriggerInfo", "2025.1"
|
||||
)
|
||||
|
||||
|
||||
@bind_hass
|
||||
def is_on(hass: HomeAssistant, entity_id: str) -> bool:
|
||||
"""Return true if specified automation entity_id is on.
|
||||
@@ -477,6 +451,7 @@ class UnavailableAutomationEntity(BaseAutomationEntity):
|
||||
)
|
||||
|
||||
async def async_will_remove_from_hass(self) -> None:
|
||||
"""Run when entity will be removed from hass."""
|
||||
await super().async_will_remove_from_hass()
|
||||
async_delete_issue(
|
||||
self.hass, DOMAIN, f"{self.entity_id}_validation_{self._validation_status}"
|
||||
@@ -1219,11 +1194,3 @@ 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())
|
||||
|
||||
@@ -4,7 +4,6 @@ from __future__ import annotations
|
||||
|
||||
from datetime import timedelta
|
||||
from enum import StrEnum
|
||||
from functools import partial
|
||||
import logging
|
||||
from typing import Literal, final
|
||||
|
||||
@@ -16,12 +15,6 @@ from homeassistant.const import STATE_OFF, STATE_ON, EntityCategory
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.deprecation import (
|
||||
DeprecatedConstantEnum,
|
||||
all_with_deprecated_constants,
|
||||
check_if_deprecated_constant,
|
||||
dir_with_deprecated_constants,
|
||||
)
|
||||
from homeassistant.helpers.entity import Entity, EntityDescription
|
||||
from homeassistant.helpers.entity_component import EntityComponent
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
@@ -126,94 +119,7 @@ class BinarySensorDeviceClass(StrEnum):
|
||||
|
||||
|
||||
DEVICE_CLASSES_SCHEMA = vol.All(vol.Lower, vol.Coerce(BinarySensorDeviceClass))
|
||||
|
||||
# DEVICE_CLASS* below are deprecated as of 2021.12
|
||||
# use the BinarySensorDeviceClass enum instead.
|
||||
DEVICE_CLASSES = [cls.value for cls in BinarySensorDeviceClass]
|
||||
_DEPRECATED_DEVICE_CLASS_BATTERY = DeprecatedConstantEnum(
|
||||
BinarySensorDeviceClass.BATTERY, "2025.1"
|
||||
)
|
||||
_DEPRECATED_DEVICE_CLASS_BATTERY_CHARGING = DeprecatedConstantEnum(
|
||||
BinarySensorDeviceClass.BATTERY_CHARGING, "2025.1"
|
||||
)
|
||||
_DEPRECATED_DEVICE_CLASS_CO = DeprecatedConstantEnum(
|
||||
BinarySensorDeviceClass.CO, "2025.1"
|
||||
)
|
||||
_DEPRECATED_DEVICE_CLASS_COLD = DeprecatedConstantEnum(
|
||||
BinarySensorDeviceClass.COLD, "2025.1"
|
||||
)
|
||||
_DEPRECATED_DEVICE_CLASS_CONNECTIVITY = DeprecatedConstantEnum(
|
||||
BinarySensorDeviceClass.CONNECTIVITY, "2025.1"
|
||||
)
|
||||
_DEPRECATED_DEVICE_CLASS_DOOR = DeprecatedConstantEnum(
|
||||
BinarySensorDeviceClass.DOOR, "2025.1"
|
||||
)
|
||||
_DEPRECATED_DEVICE_CLASS_GARAGE_DOOR = DeprecatedConstantEnum(
|
||||
BinarySensorDeviceClass.GARAGE_DOOR, "2025.1"
|
||||
)
|
||||
_DEPRECATED_DEVICE_CLASS_GAS = DeprecatedConstantEnum(
|
||||
BinarySensorDeviceClass.GAS, "2025.1"
|
||||
)
|
||||
_DEPRECATED_DEVICE_CLASS_HEAT = DeprecatedConstantEnum(
|
||||
BinarySensorDeviceClass.HEAT, "2025.1"
|
||||
)
|
||||
_DEPRECATED_DEVICE_CLASS_LIGHT = DeprecatedConstantEnum(
|
||||
BinarySensorDeviceClass.LIGHT, "2025.1"
|
||||
)
|
||||
_DEPRECATED_DEVICE_CLASS_LOCK = DeprecatedConstantEnum(
|
||||
BinarySensorDeviceClass.LOCK, "2025.1"
|
||||
)
|
||||
_DEPRECATED_DEVICE_CLASS_MOISTURE = DeprecatedConstantEnum(
|
||||
BinarySensorDeviceClass.MOISTURE, "2025.1"
|
||||
)
|
||||
_DEPRECATED_DEVICE_CLASS_MOTION = DeprecatedConstantEnum(
|
||||
BinarySensorDeviceClass.MOTION, "2025.1"
|
||||
)
|
||||
_DEPRECATED_DEVICE_CLASS_MOVING = DeprecatedConstantEnum(
|
||||
BinarySensorDeviceClass.MOVING, "2025.1"
|
||||
)
|
||||
_DEPRECATED_DEVICE_CLASS_OCCUPANCY = DeprecatedConstantEnum(
|
||||
BinarySensorDeviceClass.OCCUPANCY, "2025.1"
|
||||
)
|
||||
_DEPRECATED_DEVICE_CLASS_OPENING = DeprecatedConstantEnum(
|
||||
BinarySensorDeviceClass.OPENING, "2025.1"
|
||||
)
|
||||
_DEPRECATED_DEVICE_CLASS_PLUG = DeprecatedConstantEnum(
|
||||
BinarySensorDeviceClass.PLUG, "2025.1"
|
||||
)
|
||||
_DEPRECATED_DEVICE_CLASS_POWER = DeprecatedConstantEnum(
|
||||
BinarySensorDeviceClass.POWER, "2025.1"
|
||||
)
|
||||
_DEPRECATED_DEVICE_CLASS_PRESENCE = DeprecatedConstantEnum(
|
||||
BinarySensorDeviceClass.PRESENCE, "2025.1"
|
||||
)
|
||||
_DEPRECATED_DEVICE_CLASS_PROBLEM = DeprecatedConstantEnum(
|
||||
BinarySensorDeviceClass.PROBLEM, "2025.1"
|
||||
)
|
||||
_DEPRECATED_DEVICE_CLASS_RUNNING = DeprecatedConstantEnum(
|
||||
BinarySensorDeviceClass.RUNNING, "2025.1"
|
||||
)
|
||||
_DEPRECATED_DEVICE_CLASS_SAFETY = DeprecatedConstantEnum(
|
||||
BinarySensorDeviceClass.SAFETY, "2025.1"
|
||||
)
|
||||
_DEPRECATED_DEVICE_CLASS_SMOKE = DeprecatedConstantEnum(
|
||||
BinarySensorDeviceClass.SMOKE, "2025.1"
|
||||
)
|
||||
_DEPRECATED_DEVICE_CLASS_SOUND = DeprecatedConstantEnum(
|
||||
BinarySensorDeviceClass.SOUND, "2025.1"
|
||||
)
|
||||
_DEPRECATED_DEVICE_CLASS_TAMPER = DeprecatedConstantEnum(
|
||||
BinarySensorDeviceClass.TAMPER, "2025.1"
|
||||
)
|
||||
_DEPRECATED_DEVICE_CLASS_UPDATE = DeprecatedConstantEnum(
|
||||
BinarySensorDeviceClass.UPDATE, "2025.1"
|
||||
)
|
||||
_DEPRECATED_DEVICE_CLASS_VIBRATION = DeprecatedConstantEnum(
|
||||
BinarySensorDeviceClass.VIBRATION, "2025.1"
|
||||
)
|
||||
_DEPRECATED_DEVICE_CLASS_WINDOW = DeprecatedConstantEnum(
|
||||
BinarySensorDeviceClass.WINDOW, "2025.1"
|
||||
)
|
||||
|
||||
# mypy: disallow-any-generics
|
||||
|
||||
@@ -294,11 +200,3 @@ 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())
|
||||
|
||||
@@ -27,9 +27,18 @@ from homeassistant.const import CONF_PASSWORD, CONF_REGION, CONF_SOURCE, CONF_US
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.selector import SelectSelector, SelectSelectorConfig
|
||||
from homeassistant.util.ssl import get_default_context
|
||||
|
||||
from . import DOMAIN
|
||||
from .const import CONF_ALLOWED_REGIONS, CONF_GCID, CONF_READ_ONLY, CONF_REFRESH_TOKEN
|
||||
from .const import (
|
||||
CONF_ALLOWED_REGIONS,
|
||||
CONF_CAPTCHA_REGIONS,
|
||||
CONF_CAPTCHA_TOKEN,
|
||||
CONF_CAPTCHA_URL,
|
||||
CONF_GCID,
|
||||
CONF_READ_ONLY,
|
||||
CONF_REFRESH_TOKEN,
|
||||
)
|
||||
|
||||
DATA_SCHEMA = vol.Schema(
|
||||
{
|
||||
@@ -41,7 +50,14 @@ DATA_SCHEMA = vol.Schema(
|
||||
translation_key="regions",
|
||||
)
|
||||
),
|
||||
}
|
||||
},
|
||||
extra=vol.REMOVE_EXTRA,
|
||||
)
|
||||
CAPTCHA_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_CAPTCHA_TOKEN): str,
|
||||
},
|
||||
extra=vol.REMOVE_EXTRA,
|
||||
)
|
||||
|
||||
|
||||
@@ -54,6 +70,8 @@ async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> dict[str,
|
||||
data[CONF_USERNAME],
|
||||
data[CONF_PASSWORD],
|
||||
get_region_from_name(data[CONF_REGION]),
|
||||
hcaptcha_token=data.get(CONF_CAPTCHA_TOKEN),
|
||||
verify=get_default_context(),
|
||||
)
|
||||
|
||||
try:
|
||||
@@ -79,15 +97,17 @@ class BMWConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
|
||||
VERSION = 1
|
||||
|
||||
data: dict[str, Any] = {}
|
||||
|
||||
_existing_entry_data: Mapping[str, Any] | None = None
|
||||
|
||||
async def async_step_user(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
"""Handle the initial step."""
|
||||
errors: dict[str, str] = {}
|
||||
errors: dict[str, str] = self.data.pop("errors", {})
|
||||
|
||||
if user_input is not None:
|
||||
if user_input is not None and not errors:
|
||||
unique_id = f"{user_input[CONF_REGION]}-{user_input[CONF_USERNAME]}"
|
||||
await self.async_set_unique_id(unique_id)
|
||||
|
||||
@@ -96,22 +116,35 @@ class BMWConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
else:
|
||||
self._abort_if_unique_id_configured()
|
||||
|
||||
# Store user input for later use
|
||||
self.data.update(user_input)
|
||||
|
||||
# North America and Rest of World require captcha token
|
||||
if (
|
||||
self.data.get(CONF_REGION) in CONF_CAPTCHA_REGIONS
|
||||
and CONF_CAPTCHA_TOKEN not in self.data
|
||||
):
|
||||
return await self.async_step_captcha()
|
||||
|
||||
info = None
|
||||
try:
|
||||
info = await validate_input(self.hass, user_input)
|
||||
entry_data = {
|
||||
**user_input,
|
||||
CONF_REFRESH_TOKEN: info.get(CONF_REFRESH_TOKEN),
|
||||
CONF_GCID: info.get(CONF_GCID),
|
||||
}
|
||||
info = await validate_input(self.hass, self.data)
|
||||
except MissingCaptcha:
|
||||
errors["base"] = "missing_captcha"
|
||||
except CannotConnect:
|
||||
errors["base"] = "cannot_connect"
|
||||
except InvalidAuth:
|
||||
errors["base"] = "invalid_auth"
|
||||
finally:
|
||||
self.data.pop(CONF_CAPTCHA_TOKEN, None)
|
||||
|
||||
if info:
|
||||
entry_data = {
|
||||
**self.data,
|
||||
CONF_REFRESH_TOKEN: info.get(CONF_REFRESH_TOKEN),
|
||||
CONF_GCID: info.get(CONF_GCID),
|
||||
}
|
||||
|
||||
if self.source == SOURCE_REAUTH:
|
||||
return self.async_update_reload_and_abort(
|
||||
self._get_reauth_entry(), data=entry_data
|
||||
@@ -128,7 +161,7 @@ class BMWConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
|
||||
schema = self.add_suggested_values_to_schema(
|
||||
DATA_SCHEMA,
|
||||
self._existing_entry_data,
|
||||
self._existing_entry_data or self.data,
|
||||
)
|
||||
|
||||
return self.async_show_form(step_id="user", data_schema=schema, errors=errors)
|
||||
@@ -147,6 +180,22 @@ class BMWConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
self._existing_entry_data = self._get_reconfigure_entry().data
|
||||
return await self.async_step_user()
|
||||
|
||||
async def async_step_captcha(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
"""Show captcha form."""
|
||||
if user_input and user_input.get(CONF_CAPTCHA_TOKEN):
|
||||
self.data[CONF_CAPTCHA_TOKEN] = user_input[CONF_CAPTCHA_TOKEN].strip()
|
||||
return await self.async_step_user(self.data)
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="captcha",
|
||||
data_schema=CAPTCHA_SCHEMA,
|
||||
description_placeholders={
|
||||
"captcha_url": CONF_CAPTCHA_URL.format(region=self.data[CONF_REGION])
|
||||
},
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
@callback
|
||||
def async_get_options_flow(
|
||||
|
||||
@@ -8,10 +8,15 @@ ATTR_DIRECTION = "direction"
|
||||
ATTR_VIN = "vin"
|
||||
|
||||
CONF_ALLOWED_REGIONS = ["china", "north_america", "rest_of_world"]
|
||||
CONF_CAPTCHA_REGIONS = ["north_america", "rest_of_world"]
|
||||
CONF_READ_ONLY = "read_only"
|
||||
CONF_ACCOUNT = "account"
|
||||
CONF_REFRESH_TOKEN = "refresh_token"
|
||||
CONF_GCID = "gcid"
|
||||
CONF_CAPTCHA_TOKEN = "captcha_token"
|
||||
CONF_CAPTCHA_URL = (
|
||||
"https://bimmer-connected.readthedocs.io/en/stable/captcha/{region}.html"
|
||||
)
|
||||
|
||||
DATA_HASS_CONFIG = "hass_config"
|
||||
|
||||
|
||||
@@ -84,11 +84,6 @@ class BMWDataUpdateCoordinator(DataUpdateCoordinator[None]):
|
||||
|
||||
if self.account.refresh_token != old_refresh_token:
|
||||
self._update_config_entry_refresh_token(self.account.refresh_token)
|
||||
_LOGGER.debug(
|
||||
"bimmer_connected: refresh token %s > %s",
|
||||
old_refresh_token,
|
||||
self.account.refresh_token,
|
||||
)
|
||||
|
||||
def _update_config_entry_refresh_token(self, refresh_token: str | None) -> None:
|
||||
"""Update or delete the refresh_token in the Config Entry."""
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/bmw_connected_drive",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["bimmer_connected"],
|
||||
"requirements": ["bimmer-connected[china]==0.16.4"]
|
||||
"requirements": ["bimmer-connected[china]==0.17.0"]
|
||||
}
|
||||
|
||||
@@ -7,6 +7,16 @@
|
||||
"password": "[%key:common::config_flow::data::password%]",
|
||||
"region": "ConnectedDrive Region"
|
||||
}
|
||||
},
|
||||
"captcha": {
|
||||
"title": "Are you a robot?",
|
||||
"description": "A captcha is required for BMW login. Visit the external website to complete the challenge and submit the form. Copy the resulting token into the field below.\n\n{captcha_url}\n\nNo data will be exposed outside of your Home Assistant instance.",
|
||||
"data": {
|
||||
"captcha_token": "Captcha token"
|
||||
},
|
||||
"data_description": {
|
||||
"captcha_token": "One-time token retrieved from the captcha challenge."
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
|
||||
@@ -9,4 +9,3 @@ ATTR_ITEM_NAME: Final = "item"
|
||||
ATTR_NOTIFICATION_TYPE: Final = "message"
|
||||
|
||||
SERVICE_PUSH_NOTIFICATION = "send_message"
|
||||
UNIT_ITEMS = "items"
|
||||
|
||||
@@ -20,7 +20,6 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.typing import StateType
|
||||
|
||||
from . import BringConfigEntry
|
||||
from .const import UNIT_ITEMS
|
||||
from .coordinator import BringData, BringDataUpdateCoordinator
|
||||
from .entity import BringBaseEntity
|
||||
from .util import list_language, sum_attributes
|
||||
@@ -48,19 +47,16 @@ SENSOR_DESCRIPTIONS: tuple[BringSensorEntityDescription, ...] = (
|
||||
key=BringSensor.URGENT,
|
||||
translation_key=BringSensor.URGENT,
|
||||
value_fn=lambda lst, _: sum_attributes(lst, "urgent"),
|
||||
native_unit_of_measurement=UNIT_ITEMS,
|
||||
),
|
||||
BringSensorEntityDescription(
|
||||
key=BringSensor.CONVENIENT,
|
||||
translation_key=BringSensor.CONVENIENT,
|
||||
value_fn=lambda lst, _: sum_attributes(lst, "convenient"),
|
||||
native_unit_of_measurement=UNIT_ITEMS,
|
||||
),
|
||||
BringSensorEntityDescription(
|
||||
key=BringSensor.DISCOUNTED,
|
||||
translation_key=BringSensor.DISCOUNTED,
|
||||
value_fn=lambda lst, _: sum_attributes(lst, "discounted"),
|
||||
native_unit_of_measurement=UNIT_ITEMS,
|
||||
),
|
||||
BringSensorEntityDescription(
|
||||
key=BringSensor.LIST_LANGUAGE,
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
{
|
||||
"common": {
|
||||
"shopping_list_items": "items"
|
||||
},
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
@@ -29,13 +32,16 @@
|
||||
"entity": {
|
||||
"sensor": {
|
||||
"urgent": {
|
||||
"name": "Urgent"
|
||||
"name": "Urgent",
|
||||
"unit_of_measurement": "[%key:component::bring::common::shopping_list_items%]"
|
||||
},
|
||||
"convenient": {
|
||||
"name": "On occasion"
|
||||
"name": "On occasion",
|
||||
"unit_of_measurement": "[%key:component::bring::common::shopping_list_items%]"
|
||||
},
|
||||
"discounted": {
|
||||
"name": "Discount only"
|
||||
"name": "Discount only",
|
||||
"unit_of_measurement": "[%key:component::bring::common::shopping_list_items%]"
|
||||
},
|
||||
"list_language": {
|
||||
"name": "Region & language",
|
||||
|
||||
@@ -67,9 +67,7 @@ from homeassistant.helpers.template import Template
|
||||
from homeassistant.helpers.typing import ConfigType, VolDictType
|
||||
from homeassistant.loader import bind_hass
|
||||
|
||||
from .const import ( # noqa: F401
|
||||
_DEPRECATED_STREAM_TYPE_HLS,
|
||||
_DEPRECATED_STREAM_TYPE_WEB_RTC,
|
||||
from .const import (
|
||||
CAMERA_IMAGE_TIMEOUT,
|
||||
CAMERA_STREAM_SOURCE_TIMEOUT,
|
||||
CONF_DURATION,
|
||||
@@ -135,16 +133,6 @@ class CameraEntityFeature(IntFlag):
|
||||
STREAM = 2
|
||||
|
||||
|
||||
# These SUPPORT_* constants are deprecated as of Home Assistant 2022.5.
|
||||
# Pleease use the CameraEntityFeature enum instead.
|
||||
_DEPRECATED_SUPPORT_ON_OFF: Final = DeprecatedConstantEnum(
|
||||
CameraEntityFeature.ON_OFF, "2025.1"
|
||||
)
|
||||
_DEPRECATED_SUPPORT_STREAM: Final = DeprecatedConstantEnum(
|
||||
CameraEntityFeature.STREAM, "2025.1"
|
||||
)
|
||||
|
||||
|
||||
DEFAULT_CONTENT_TYPE: Final = "image/jpeg"
|
||||
ENTITY_IMAGE_URL: Final = "/api/camera_proxy/{0}?token={1}"
|
||||
|
||||
|
||||
@@ -3,15 +3,8 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from enum import StrEnum
|
||||
from functools import partial
|
||||
from typing import TYPE_CHECKING, Final
|
||||
|
||||
from homeassistant.helpers.deprecation import (
|
||||
DeprecatedConstantEnum,
|
||||
all_with_deprecated_constants,
|
||||
check_if_deprecated_constant,
|
||||
dir_with_deprecated_constants,
|
||||
)
|
||||
from homeassistant.util.hass_dict import HassKey
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -58,17 +51,3 @@ class StreamType(StrEnum):
|
||||
|
||||
HLS = "hls"
|
||||
WEB_RTC = "web_rtc"
|
||||
|
||||
|
||||
# These constants are deprecated as of Home Assistant 2022.5
|
||||
# Please use the StreamType enum instead.
|
||||
_DEPRECATED_STREAM_TYPE_HLS = DeprecatedConstantEnum(StreamType.HLS, "2025.1")
|
||||
_DEPRECATED_STREAM_TYPE_WEB_RTC = DeprecatedConstantEnum(StreamType.WEB_RTC, "2025.1")
|
||||
|
||||
|
||||
# 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())
|
||||
|
||||
@@ -26,11 +26,6 @@ from homeassistant.const import (
|
||||
from homeassistant.core import HomeAssistant, ServiceCall, callback
|
||||
from homeassistant.exceptions import ServiceValidationError
|
||||
from homeassistant.helpers import config_validation as cv, issue_registry as ir
|
||||
from homeassistant.helpers.deprecation import (
|
||||
all_with_deprecated_constants,
|
||||
check_if_deprecated_constant,
|
||||
dir_with_deprecated_constants,
|
||||
)
|
||||
from homeassistant.helpers.entity import Entity, EntityDescription
|
||||
from homeassistant.helpers.entity_component import EntityComponent
|
||||
from homeassistant.helpers.entity_platform import EntityPlatform
|
||||
@@ -41,20 +36,6 @@ from homeassistant.util.hass_dict import HassKey
|
||||
from homeassistant.util.unit_conversion import TemperatureConverter
|
||||
|
||||
from .const import ( # noqa: F401
|
||||
_DEPRECATED_HVAC_MODE_AUTO,
|
||||
_DEPRECATED_HVAC_MODE_COOL,
|
||||
_DEPRECATED_HVAC_MODE_DRY,
|
||||
_DEPRECATED_HVAC_MODE_FAN_ONLY,
|
||||
_DEPRECATED_HVAC_MODE_HEAT,
|
||||
_DEPRECATED_HVAC_MODE_HEAT_COOL,
|
||||
_DEPRECATED_HVAC_MODE_OFF,
|
||||
_DEPRECATED_SUPPORT_AUX_HEAT,
|
||||
_DEPRECATED_SUPPORT_FAN_MODE,
|
||||
_DEPRECATED_SUPPORT_PRESET_MODE,
|
||||
_DEPRECATED_SUPPORT_SWING_MODE,
|
||||
_DEPRECATED_SUPPORT_TARGET_HUMIDITY,
|
||||
_DEPRECATED_SUPPORT_TARGET_TEMPERATURE,
|
||||
_DEPRECATED_SUPPORT_TARGET_TEMPERATURE_RANGE,
|
||||
ATTR_AUX_HEAT,
|
||||
ATTR_CURRENT_HUMIDITY,
|
||||
ATTR_CURRENT_TEMPERATURE,
|
||||
@@ -1082,13 +1063,3 @@ 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())
|
||||
|
||||
@@ -1,14 +1,6 @@
|
||||
"""Provides the constants needed for component."""
|
||||
|
||||
from enum import IntFlag, StrEnum
|
||||
from functools import partial
|
||||
|
||||
from homeassistant.helpers.deprecation import (
|
||||
DeprecatedConstantEnum,
|
||||
all_with_deprecated_constants,
|
||||
check_if_deprecated_constant,
|
||||
dir_with_deprecated_constants,
|
||||
)
|
||||
|
||||
|
||||
class HVACMode(StrEnum):
|
||||
@@ -37,15 +29,6 @@ class HVACMode(StrEnum):
|
||||
FAN_ONLY = "fan_only"
|
||||
|
||||
|
||||
# These HVAC_MODE_* constants are deprecated as of Home Assistant 2022.5.
|
||||
# Please use the HVACMode enum instead.
|
||||
_DEPRECATED_HVAC_MODE_OFF = DeprecatedConstantEnum(HVACMode.OFF, "2025.1")
|
||||
_DEPRECATED_HVAC_MODE_HEAT = DeprecatedConstantEnum(HVACMode.HEAT, "2025.1")
|
||||
_DEPRECATED_HVAC_MODE_COOL = DeprecatedConstantEnum(HVACMode.COOL, "2025.1")
|
||||
_DEPRECATED_HVAC_MODE_HEAT_COOL = DeprecatedConstantEnum(HVACMode.HEAT_COOL, "2025.1")
|
||||
_DEPRECATED_HVAC_MODE_AUTO = DeprecatedConstantEnum(HVACMode.AUTO, "2025.1")
|
||||
_DEPRECATED_HVAC_MODE_DRY = DeprecatedConstantEnum(HVACMode.DRY, "2025.1")
|
||||
_DEPRECATED_HVAC_MODE_FAN_ONLY = DeprecatedConstantEnum(HVACMode.FAN_ONLY, "2025.1")
|
||||
HVAC_MODES = [cls.value for cls in HVACMode]
|
||||
|
||||
# No preset is active
|
||||
@@ -110,14 +93,6 @@ class HVACAction(StrEnum):
|
||||
PREHEATING = "preheating"
|
||||
|
||||
|
||||
# These CURRENT_HVAC_* constants are deprecated as of Home Assistant 2022.5.
|
||||
# Please use the HVACAction enum instead.
|
||||
_DEPRECATED_CURRENT_HVAC_OFF = DeprecatedConstantEnum(HVACAction.OFF, "2025.1")
|
||||
_DEPRECATED_CURRENT_HVAC_HEAT = DeprecatedConstantEnum(HVACAction.HEATING, "2025.1")
|
||||
_DEPRECATED_CURRENT_HVAC_COOL = DeprecatedConstantEnum(HVACAction.COOLING, "2025.1")
|
||||
_DEPRECATED_CURRENT_HVAC_DRY = DeprecatedConstantEnum(HVACAction.DRYING, "2025.1")
|
||||
_DEPRECATED_CURRENT_HVAC_IDLE = DeprecatedConstantEnum(HVACAction.IDLE, "2025.1")
|
||||
_DEPRECATED_CURRENT_HVAC_FAN = DeprecatedConstantEnum(HVACAction.FAN, "2025.1")
|
||||
CURRENT_HVAC_ACTIONS = [cls.value for cls in HVACAction]
|
||||
|
||||
|
||||
@@ -176,35 +151,3 @@ class ClimateEntityFeature(IntFlag):
|
||||
TURN_OFF = 128
|
||||
TURN_ON = 256
|
||||
SWING_HORIZONTAL_MODE = 512
|
||||
|
||||
|
||||
# These SUPPORT_* constants are deprecated as of Home Assistant 2022.5.
|
||||
# Please use the ClimateEntityFeature enum instead.
|
||||
_DEPRECATED_SUPPORT_TARGET_TEMPERATURE = DeprecatedConstantEnum(
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE, "2025.1"
|
||||
)
|
||||
_DEPRECATED_SUPPORT_TARGET_TEMPERATURE_RANGE = DeprecatedConstantEnum(
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE_RANGE, "2025.1"
|
||||
)
|
||||
_DEPRECATED_SUPPORT_TARGET_HUMIDITY = DeprecatedConstantEnum(
|
||||
ClimateEntityFeature.TARGET_HUMIDITY, "2025.1"
|
||||
)
|
||||
_DEPRECATED_SUPPORT_FAN_MODE = DeprecatedConstantEnum(
|
||||
ClimateEntityFeature.FAN_MODE, "2025.1"
|
||||
)
|
||||
_DEPRECATED_SUPPORT_PRESET_MODE = DeprecatedConstantEnum(
|
||||
ClimateEntityFeature.PRESET_MODE, "2025.1"
|
||||
)
|
||||
_DEPRECATED_SUPPORT_SWING_MODE = DeprecatedConstantEnum(
|
||||
ClimateEntityFeature.SWING_MODE, "2025.1"
|
||||
)
|
||||
_DEPRECATED_SUPPORT_AUX_HEAT = DeprecatedConstantEnum(
|
||||
ClimateEntityFeature.AUX_HEAT, "2025.1"
|
||||
)
|
||||
|
||||
# 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())
|
||||
|
||||
@@ -89,36 +89,8 @@ class CoverDeviceClass(StrEnum):
|
||||
|
||||
|
||||
DEVICE_CLASSES_SCHEMA = vol.All(vol.Lower, vol.Coerce(CoverDeviceClass))
|
||||
|
||||
# DEVICE_CLASS* below are deprecated as of 2021.12
|
||||
# use the CoverDeviceClass enum instead.
|
||||
DEVICE_CLASSES = [cls.value for cls in CoverDeviceClass]
|
||||
_DEPRECATED_DEVICE_CLASS_AWNING = DeprecatedConstantEnum(
|
||||
CoverDeviceClass.AWNING, "2025.1"
|
||||
)
|
||||
_DEPRECATED_DEVICE_CLASS_BLIND = DeprecatedConstantEnum(
|
||||
CoverDeviceClass.BLIND, "2025.1"
|
||||
)
|
||||
_DEPRECATED_DEVICE_CLASS_CURTAIN = DeprecatedConstantEnum(
|
||||
CoverDeviceClass.CURTAIN, "2025.1"
|
||||
)
|
||||
_DEPRECATED_DEVICE_CLASS_DAMPER = DeprecatedConstantEnum(
|
||||
CoverDeviceClass.DAMPER, "2025.1"
|
||||
)
|
||||
_DEPRECATED_DEVICE_CLASS_DOOR = DeprecatedConstantEnum(CoverDeviceClass.DOOR, "2025.1")
|
||||
_DEPRECATED_DEVICE_CLASS_GARAGE = DeprecatedConstantEnum(
|
||||
CoverDeviceClass.GARAGE, "2025.1"
|
||||
)
|
||||
_DEPRECATED_DEVICE_CLASS_GATE = DeprecatedConstantEnum(CoverDeviceClass.GATE, "2025.1")
|
||||
_DEPRECATED_DEVICE_CLASS_SHADE = DeprecatedConstantEnum(
|
||||
CoverDeviceClass.SHADE, "2025.1"
|
||||
)
|
||||
_DEPRECATED_DEVICE_CLASS_SHUTTER = DeprecatedConstantEnum(
|
||||
CoverDeviceClass.SHUTTER, "2025.1"
|
||||
)
|
||||
_DEPRECATED_DEVICE_CLASS_WINDOW = DeprecatedConstantEnum(
|
||||
CoverDeviceClass.WINDOW, "2025.1"
|
||||
)
|
||||
|
||||
|
||||
# mypy: disallow-any-generics
|
||||
|
||||
@@ -136,27 +108,6 @@ class CoverEntityFeature(IntFlag):
|
||||
SET_TILT_POSITION = 128
|
||||
|
||||
|
||||
# These SUPPORT_* constants are deprecated as of Home Assistant 2022.5.
|
||||
# Please use the CoverEntityFeature enum instead.
|
||||
_DEPRECATED_SUPPORT_OPEN = DeprecatedConstantEnum(CoverEntityFeature.OPEN, "2025.1")
|
||||
_DEPRECATED_SUPPORT_CLOSE = DeprecatedConstantEnum(CoverEntityFeature.CLOSE, "2025.1")
|
||||
_DEPRECATED_SUPPORT_SET_POSITION = DeprecatedConstantEnum(
|
||||
CoverEntityFeature.SET_POSITION, "2025.1"
|
||||
)
|
||||
_DEPRECATED_SUPPORT_STOP = DeprecatedConstantEnum(CoverEntityFeature.STOP, "2025.1")
|
||||
_DEPRECATED_SUPPORT_OPEN_TILT = DeprecatedConstantEnum(
|
||||
CoverEntityFeature.OPEN_TILT, "2025.1"
|
||||
)
|
||||
_DEPRECATED_SUPPORT_CLOSE_TILT = DeprecatedConstantEnum(
|
||||
CoverEntityFeature.CLOSE_TILT, "2025.1"
|
||||
)
|
||||
_DEPRECATED_SUPPORT_STOP_TILT = DeprecatedConstantEnum(
|
||||
CoverEntityFeature.STOP_TILT, "2025.1"
|
||||
)
|
||||
_DEPRECATED_SUPPORT_SET_TILT_POSITION = DeprecatedConstantEnum(
|
||||
CoverEntityFeature.SET_TILT_POSITION, "2025.1"
|
||||
)
|
||||
|
||||
ATTR_CURRENT_POSITION = "current_position"
|
||||
ATTR_CURRENT_TILT_POSITION = "current_tilt_position"
|
||||
ATTR_POSITION = "position"
|
||||
|
||||
@@ -2,15 +2,8 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
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,
|
||||
)
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
from homeassistant.loader import bind_hass
|
||||
|
||||
@@ -23,10 +16,6 @@ from .config_entry import ( # noqa: F401
|
||||
async_unload_entry,
|
||||
)
|
||||
from .const import ( # noqa: F401
|
||||
_DEPRECATED_SOURCE_TYPE_BLUETOOTH,
|
||||
_DEPRECATED_SOURCE_TYPE_BLUETOOTH_LE,
|
||||
_DEPRECATED_SOURCE_TYPE_GPS,
|
||||
_DEPRECATED_SOURCE_TYPE_ROUTER,
|
||||
ATTR_ATTRIBUTES,
|
||||
ATTR_BATTERY,
|
||||
ATTR_DEV_ID,
|
||||
@@ -72,13 +61,3 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
"""Set up the device tracker."""
|
||||
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())
|
||||
|
||||
@@ -4,16 +4,9 @@ from __future__ import annotations
|
||||
|
||||
from datetime import timedelta
|
||||
from enum import StrEnum
|
||||
from functools import partial
|
||||
import logging
|
||||
from typing import Final
|
||||
|
||||
from homeassistant.helpers.deprecation import (
|
||||
DeprecatedConstantEnum,
|
||||
all_with_deprecated_constants,
|
||||
check_if_deprecated_constant,
|
||||
dir_with_deprecated_constants,
|
||||
)
|
||||
from homeassistant.util.signal_type import SignalType
|
||||
|
||||
LOGGER: Final = logging.getLogger(__package__)
|
||||
@@ -34,19 +27,6 @@ class SourceType(StrEnum):
|
||||
BLUETOOTH_LE = "bluetooth_le"
|
||||
|
||||
|
||||
# SOURCE_TYPE_* below are deprecated as of 2022.9
|
||||
# use the SourceType enum instead.
|
||||
_DEPRECATED_SOURCE_TYPE_GPS: Final = DeprecatedConstantEnum(SourceType.GPS, "2025.1")
|
||||
_DEPRECATED_SOURCE_TYPE_ROUTER: Final = DeprecatedConstantEnum(
|
||||
SourceType.ROUTER, "2025.1"
|
||||
)
|
||||
_DEPRECATED_SOURCE_TYPE_BLUETOOTH: Final = DeprecatedConstantEnum(
|
||||
SourceType.BLUETOOTH, "2025.1"
|
||||
)
|
||||
_DEPRECATED_SOURCE_TYPE_BLUETOOTH_LE: Final = DeprecatedConstantEnum(
|
||||
SourceType.BLUETOOTH_LE, "2025.1"
|
||||
)
|
||||
|
||||
CONF_SCAN_INTERVAL: Final = "interval_seconds"
|
||||
SCAN_INTERVAL: Final = timedelta(seconds=12)
|
||||
|
||||
@@ -72,10 +52,3 @@ ATTR_IP: Final = "ip"
|
||||
CONNECTED_DEVICE_REGISTERED = SignalType[dict[str, str | None]](
|
||||
"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())
|
||||
|
||||
@@ -60,11 +60,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: DiscovergyConfigEntry) -
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: DiscovergyConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
|
||||
async def async_reload_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
async def async_reload_entry(hass: HomeAssistant, entry: DiscovergyConfigEntry) -> None:
|
||||
"""Handle an options update."""
|
||||
await hass.config_entries.async_reload(entry.entry_id)
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
"loggers": ["aioesphomeapi", "noiseprotocol", "bleak_esphome"],
|
||||
"mqtt": ["esphome/discover/#"],
|
||||
"requirements": [
|
||||
"aioesphomeapi==27.0.2",
|
||||
"aioesphomeapi==27.0.3",
|
||||
"esphome-dashboard-api==1.2.3",
|
||||
"bleak-esphome==1.1.0"
|
||||
],
|
||||
|
||||
@@ -10,6 +10,7 @@ from homeassistant.components.assist_pipeline.select import (
|
||||
)
|
||||
from homeassistant.components.assist_satellite import AssistSatelliteConfiguration
|
||||
from homeassistant.components.select import SelectEntity, SelectEntityDescription
|
||||
from homeassistant.const import EntityCategory
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers import restore_state
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
@@ -100,7 +101,9 @@ class EsphomeAssistSatelliteWakeWordSelect(
|
||||
"""Wake word selector for esphome devices."""
|
||||
|
||||
entity_description = SelectEntityDescription(
|
||||
key="wake_word", translation_key="wake_word"
|
||||
key="wake_word",
|
||||
translation_key="wake_word",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
)
|
||||
_attr_should_poll = False
|
||||
_attr_current_option: str | None = None
|
||||
|
||||
@@ -23,12 +23,6 @@ from homeassistant.const import (
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import ServiceValidationError
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.deprecation import (
|
||||
DeprecatedConstantEnum,
|
||||
all_with_deprecated_constants,
|
||||
check_if_deprecated_constant,
|
||||
dir_with_deprecated_constants,
|
||||
)
|
||||
from homeassistant.helpers.entity import ToggleEntity, ToggleEntityDescription
|
||||
from homeassistant.helpers.entity_component import EntityComponent
|
||||
from homeassistant.helpers.entity_platform import EntityPlatform
|
||||
@@ -61,21 +55,6 @@ class FanEntityFeature(IntFlag):
|
||||
TURN_ON = 32
|
||||
|
||||
|
||||
# These SUPPORT_* constants are deprecated as of Home Assistant 2022.5.
|
||||
# Please use the FanEntityFeature enum instead.
|
||||
_DEPRECATED_SUPPORT_SET_SPEED = DeprecatedConstantEnum(
|
||||
FanEntityFeature.SET_SPEED, "2025.1"
|
||||
)
|
||||
_DEPRECATED_SUPPORT_OSCILLATE = DeprecatedConstantEnum(
|
||||
FanEntityFeature.OSCILLATE, "2025.1"
|
||||
)
|
||||
_DEPRECATED_SUPPORT_DIRECTION = DeprecatedConstantEnum(
|
||||
FanEntityFeature.DIRECTION, "2025.1"
|
||||
)
|
||||
_DEPRECATED_SUPPORT_PRESET_MODE = DeprecatedConstantEnum(
|
||||
FanEntityFeature.PRESET_MODE, "2025.1"
|
||||
)
|
||||
|
||||
SERVICE_INCREASE_SPEED = "increase_speed"
|
||||
SERVICE_DECREASE_SPEED = "decrease_speed"
|
||||
SERVICE_OSCILLATE = "oscillate"
|
||||
@@ -543,11 +522,3 @@ class FanEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||
Requires FanEntityFeature.SET_SPEED.
|
||||
"""
|
||||
return self._attr_preset_modes
|
||||
|
||||
|
||||
# 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())
|
||||
|
||||
@@ -20,5 +20,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/frontend",
|
||||
"integration_type": "system",
|
||||
"quality_scale": "internal",
|
||||
"requirements": ["home-assistant-frontend==20241127.0"]
|
||||
"requirements": ["home-assistant-frontend==20241127.1"]
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ class HabiticaButtonEntityDescription(ButtonEntityDescription):
|
||||
"""Describes Habitica button entity."""
|
||||
|
||||
press_fn: Callable[[HabiticaDataUpdateCoordinator], Any]
|
||||
available_fn: Callable[[HabiticaData], bool] | None = None
|
||||
available_fn: Callable[[HabiticaData], bool]
|
||||
class_needed: str | None = None
|
||||
entity_picture: str | None = None
|
||||
|
||||
@@ -343,11 +343,10 @@ class HabiticaButton(HabiticaBase, ButtonEntity):
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
"""Is entity available."""
|
||||
if not super().available:
|
||||
return False
|
||||
if self.entity_description.available_fn:
|
||||
return self.entity_description.available_fn(self.coordinator.data)
|
||||
return True
|
||||
|
||||
return super().available and self.entity_description.available_fn(
|
||||
self.coordinator.data
|
||||
)
|
||||
|
||||
@property
|
||||
def entity_picture(self) -> str | None:
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from abc import abstractmethod
|
||||
from datetime import date, datetime, timedelta
|
||||
from enum import StrEnum
|
||||
|
||||
@@ -60,6 +61,43 @@ class HabiticaCalendarEntity(HabiticaBase, CalendarEntity):
|
||||
"""Initialize calendar entity."""
|
||||
super().__init__(coordinator, self.entity_description)
|
||||
|
||||
@abstractmethod
|
||||
def get_events(
|
||||
self, start_date: datetime, end_date: datetime | None = None
|
||||
) -> list[CalendarEvent]:
|
||||
"""Return events."""
|
||||
|
||||
@property
|
||||
def event(self) -> CalendarEvent | None:
|
||||
"""Return the current or next upcoming event."""
|
||||
|
||||
return next(iter(self.get_events(dt_util.now())), None)
|
||||
|
||||
async def async_get_events(
|
||||
self, hass: HomeAssistant, start_date: datetime, end_date: datetime
|
||||
) -> list[CalendarEvent]:
|
||||
"""Return calendar events within a datetime range."""
|
||||
|
||||
return self.get_events(start_date, end_date)
|
||||
|
||||
@property
|
||||
def start_of_today(self) -> datetime:
|
||||
"""Habitica daystart."""
|
||||
return dt_util.start_of_local_day(
|
||||
datetime.fromisoformat(self.coordinator.data.user["lastCron"])
|
||||
)
|
||||
|
||||
def get_recurrence_dates(
|
||||
self, recurrences: rrule, start_date: datetime, end_date: datetime | None = None
|
||||
) -> list[datetime]:
|
||||
"""Calculate recurrence dates based on start_date and end_date."""
|
||||
if end_date:
|
||||
return recurrences.between(
|
||||
start_date, end_date - timedelta(days=1), inc=True
|
||||
)
|
||||
# if no end_date is given, return only the next recurrence
|
||||
return [recurrences.after(start_date, inc=True)]
|
||||
|
||||
|
||||
class HabiticaTodosCalendarEntity(HabiticaCalendarEntity):
|
||||
"""Habitica todos calendar entity."""
|
||||
@@ -69,7 +107,7 @@ class HabiticaTodosCalendarEntity(HabiticaCalendarEntity):
|
||||
translation_key=HabiticaCalendar.TODOS,
|
||||
)
|
||||
|
||||
def dated_todos(
|
||||
def get_events(
|
||||
self, start_date: datetime, end_date: datetime | None = None
|
||||
) -> list[CalendarEvent]:
|
||||
"""Get all dated todos."""
|
||||
@@ -112,18 +150,6 @@ class HabiticaTodosCalendarEntity(HabiticaCalendarEntity):
|
||||
),
|
||||
)
|
||||
|
||||
@property
|
||||
def event(self) -> CalendarEvent | None:
|
||||
"""Return the current or next upcoming event."""
|
||||
|
||||
return next(iter(self.dated_todos(dt_util.now())), None)
|
||||
|
||||
async def async_get_events(
|
||||
self, hass: HomeAssistant, start_date: datetime, end_date: datetime
|
||||
) -> list[CalendarEvent]:
|
||||
"""Return calendar events within a datetime range."""
|
||||
return self.dated_todos(start_date, end_date)
|
||||
|
||||
|
||||
class HabiticaDailiesCalendarEntity(HabiticaCalendarEntity):
|
||||
"""Habitica dailies calendar entity."""
|
||||
@@ -133,13 +159,6 @@ class HabiticaDailiesCalendarEntity(HabiticaCalendarEntity):
|
||||
translation_key=HabiticaCalendar.DAILIES,
|
||||
)
|
||||
|
||||
@property
|
||||
def today(self) -> datetime:
|
||||
"""Habitica daystart."""
|
||||
return dt_util.start_of_local_day(
|
||||
datetime.fromisoformat(self.coordinator.data.user["lastCron"])
|
||||
)
|
||||
|
||||
def end_date(self, recurrence: datetime, end: datetime | None = None) -> date:
|
||||
"""Calculate the end date for a yesterdaily.
|
||||
|
||||
@@ -152,29 +171,20 @@ class HabiticaDailiesCalendarEntity(HabiticaCalendarEntity):
|
||||
if end:
|
||||
return recurrence.date() + timedelta(days=1)
|
||||
return (
|
||||
dt_util.start_of_local_day() if recurrence == self.today else recurrence
|
||||
dt_util.start_of_local_day()
|
||||
if recurrence == self.start_of_today
|
||||
else recurrence
|
||||
).date() + timedelta(days=1)
|
||||
|
||||
def get_recurrence_dates(
|
||||
self, recurrences: rrule, start_date: datetime, end_date: datetime | None = None
|
||||
) -> list[datetime]:
|
||||
"""Calculate recurrence dates based on start_date and end_date."""
|
||||
if end_date:
|
||||
return recurrences.between(
|
||||
start_date, end_date - timedelta(days=1), inc=True
|
||||
)
|
||||
# if no end_date is given, return only the next recurrence
|
||||
return [recurrences.after(self.today, inc=True)]
|
||||
|
||||
def due_dailies(
|
||||
def get_events(
|
||||
self, start_date: datetime, end_date: datetime | None = None
|
||||
) -> list[CalendarEvent]:
|
||||
"""Get dailies and recurrences for a given period or the next upcoming."""
|
||||
|
||||
# we only have dailies for today and future recurrences
|
||||
if end_date and end_date < self.today:
|
||||
if end_date and end_date < self.start_of_today:
|
||||
return []
|
||||
start_date = max(start_date, self.today)
|
||||
start_date = max(start_date, self.start_of_today)
|
||||
|
||||
events = []
|
||||
for task in self.coordinator.data.tasks:
|
||||
@@ -187,10 +197,12 @@ class HabiticaDailiesCalendarEntity(HabiticaCalendarEntity):
|
||||
recurrences, start_date, end_date
|
||||
)
|
||||
for recurrence in recurrence_dates:
|
||||
is_future_event = recurrence > self.today
|
||||
is_current_event = recurrence <= self.today and not task["completed"]
|
||||
is_future_event = recurrence > self.start_of_today
|
||||
is_current_event = (
|
||||
recurrence <= self.start_of_today and not task["completed"]
|
||||
)
|
||||
|
||||
if not (is_future_event or is_current_event):
|
||||
if not is_future_event and not is_current_event:
|
||||
continue
|
||||
|
||||
events.append(
|
||||
@@ -214,20 +226,15 @@ class HabiticaDailiesCalendarEntity(HabiticaCalendarEntity):
|
||||
@property
|
||||
def event(self) -> CalendarEvent | None:
|
||||
"""Return the next upcoming event."""
|
||||
return next(iter(self.due_dailies(self.today)), None)
|
||||
|
||||
async def async_get_events(
|
||||
self, hass: HomeAssistant, start_date: datetime, end_date: datetime
|
||||
) -> list[CalendarEvent]:
|
||||
"""Return calendar events within a datetime range."""
|
||||
|
||||
return self.due_dailies(start_date, end_date)
|
||||
return next(iter(self.get_events(self.start_of_today)), None)
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self) -> dict[str, bool | None] | None:
|
||||
"""Return entity specific state attributes."""
|
||||
return {
|
||||
"yesterdaily": self.event.start < self.today.date() if self.event else None
|
||||
"yesterdaily": self.event.start < self.start_of_today.date()
|
||||
if self.event
|
||||
else None
|
||||
}
|
||||
|
||||
|
||||
@@ -239,7 +246,7 @@ class HabiticaTodoRemindersCalendarEntity(HabiticaCalendarEntity):
|
||||
translation_key=HabiticaCalendar.TODO_REMINDERS,
|
||||
)
|
||||
|
||||
def reminders(
|
||||
def get_events(
|
||||
self, start_date: datetime, end_date: datetime | None = None
|
||||
) -> list[CalendarEvent]:
|
||||
"""Reminders for todos."""
|
||||
@@ -282,18 +289,6 @@ class HabiticaTodoRemindersCalendarEntity(HabiticaCalendarEntity):
|
||||
key=lambda event: event.start,
|
||||
)
|
||||
|
||||
@property
|
||||
def event(self) -> CalendarEvent | None:
|
||||
"""Return the next upcoming event."""
|
||||
return next(iter(self.reminders(dt_util.now())), None)
|
||||
|
||||
async def async_get_events(
|
||||
self, hass: HomeAssistant, start_date: datetime, end_date: datetime
|
||||
) -> list[CalendarEvent]:
|
||||
"""Return calendar events within a datetime range."""
|
||||
|
||||
return self.reminders(start_date, end_date)
|
||||
|
||||
|
||||
class HabiticaDailyRemindersCalendarEntity(HabiticaCalendarEntity):
|
||||
"""Habitica daily reminders calendar entity."""
|
||||
@@ -321,47 +316,31 @@ class HabiticaDailyRemindersCalendarEntity(HabiticaCalendarEntity):
|
||||
tzinfo=dt_util.DEFAULT_TIME_ZONE,
|
||||
)
|
||||
|
||||
@property
|
||||
def today(self) -> datetime:
|
||||
"""Habitica daystart."""
|
||||
return dt_util.start_of_local_day(
|
||||
datetime.fromisoformat(self.coordinator.data.user["lastCron"])
|
||||
)
|
||||
|
||||
def get_recurrence_dates(
|
||||
self, recurrences: rrule, start_date: datetime, end_date: datetime | None = None
|
||||
) -> list[datetime]:
|
||||
"""Calculate recurrence dates based on start_date and end_date."""
|
||||
if end_date:
|
||||
return recurrences.between(
|
||||
start_date, end_date - timedelta(days=1), inc=True
|
||||
)
|
||||
# if no end_date is given, return only the next recurrence
|
||||
return [recurrences.after(self.today, inc=True)]
|
||||
|
||||
def reminders(
|
||||
def get_events(
|
||||
self, start_date: datetime, end_date: datetime | None = None
|
||||
) -> list[CalendarEvent]:
|
||||
"""Reminders for dailies."""
|
||||
|
||||
events = []
|
||||
if end_date and end_date < self.today:
|
||||
if end_date and end_date < self.start_of_today:
|
||||
return []
|
||||
start_date = max(start_date, self.today)
|
||||
start_date = max(start_date, self.start_of_today)
|
||||
|
||||
for task in self.coordinator.data.tasks:
|
||||
if not (task["type"] == HabiticaTaskType.DAILY and task["everyX"]):
|
||||
continue
|
||||
|
||||
recurrences = build_rrule(task)
|
||||
recurrences_start = self.today
|
||||
recurrences_start = self.start_of_today
|
||||
|
||||
recurrence_dates = self.get_recurrence_dates(
|
||||
recurrences, recurrences_start, end_date
|
||||
)
|
||||
for recurrence in recurrence_dates:
|
||||
is_future_event = recurrence > self.today
|
||||
is_current_event = recurrence <= self.today and not task["completed"]
|
||||
is_future_event = recurrence > self.start_of_today
|
||||
is_current_event = (
|
||||
recurrence <= self.start_of_today and not task["completed"]
|
||||
)
|
||||
|
||||
if not is_future_event and not is_current_event:
|
||||
continue
|
||||
@@ -374,9 +353,6 @@ class HabiticaDailyRemindersCalendarEntity(HabiticaCalendarEntity):
|
||||
# Event ends before date range
|
||||
continue
|
||||
|
||||
if end_date and start > end_date:
|
||||
# Event starts after date range
|
||||
continue
|
||||
events.append(
|
||||
CalendarEvent(
|
||||
start=start,
|
||||
@@ -391,15 +367,3 @@ class HabiticaDailyRemindersCalendarEntity(HabiticaCalendarEntity):
|
||||
events,
|
||||
key=lambda event: event.start,
|
||||
)
|
||||
|
||||
@property
|
||||
def event(self) -> CalendarEvent | None:
|
||||
"""Return the next upcoming event."""
|
||||
return next(iter(self.reminders(dt_util.now())), None)
|
||||
|
||||
async def async_get_events(
|
||||
self, hass: HomeAssistant, start_date: datetime, end_date: datetime
|
||||
) -> list[CalendarEvent]:
|
||||
"""Return calendar events within a datetime range."""
|
||||
|
||||
return self.reminders(start_date, end_date)
|
||||
|
||||
@@ -25,8 +25,6 @@ ATTR_DATA = "data"
|
||||
MANUFACTURER = "HabitRPG, Inc."
|
||||
NAME = "Habitica"
|
||||
|
||||
UNIT_TASKS = "tasks"
|
||||
|
||||
ATTR_CONFIG_ENTRY = "config_entry"
|
||||
ATTR_SKILL = "skill"
|
||||
ATTR_TASK = "task"
|
||||
|
||||
@@ -24,7 +24,7 @@ from homeassistant.helpers.issue_registry import (
|
||||
)
|
||||
from homeassistant.helpers.typing import StateType
|
||||
|
||||
from .const import ASSETS_URL, DOMAIN, UNIT_TASKS
|
||||
from .const import ASSETS_URL, DOMAIN
|
||||
from .entity import HabiticaBase
|
||||
from .types import HabiticaConfigEntry
|
||||
from .util import entity_used_in, get_attribute_points, get_attributes_total
|
||||
@@ -84,40 +84,34 @@ SENSOR_DESCRIPTIONS: tuple[HabitipySensorEntityDescription, ...] = (
|
||||
HabitipySensorEntityDescription(
|
||||
key=HabitipySensorEntity.HEALTH,
|
||||
translation_key=HabitipySensorEntity.HEALTH,
|
||||
native_unit_of_measurement="HP",
|
||||
suggested_display_precision=0,
|
||||
value_fn=lambda user, _: user.get("stats", {}).get("hp"),
|
||||
),
|
||||
HabitipySensorEntityDescription(
|
||||
key=HabitipySensorEntity.HEALTH_MAX,
|
||||
translation_key=HabitipySensorEntity.HEALTH_MAX,
|
||||
native_unit_of_measurement="HP",
|
||||
entity_registry_enabled_default=False,
|
||||
value_fn=lambda user, _: user.get("stats", {}).get("maxHealth"),
|
||||
),
|
||||
HabitipySensorEntityDescription(
|
||||
key=HabitipySensorEntity.MANA,
|
||||
translation_key=HabitipySensorEntity.MANA,
|
||||
native_unit_of_measurement="MP",
|
||||
suggested_display_precision=0,
|
||||
value_fn=lambda user, _: user.get("stats", {}).get("mp"),
|
||||
),
|
||||
HabitipySensorEntityDescription(
|
||||
key=HabitipySensorEntity.MANA_MAX,
|
||||
translation_key=HabitipySensorEntity.MANA_MAX,
|
||||
native_unit_of_measurement="MP",
|
||||
value_fn=lambda user, _: user.get("stats", {}).get("maxMP"),
|
||||
),
|
||||
HabitipySensorEntityDescription(
|
||||
key=HabitipySensorEntity.EXPERIENCE,
|
||||
translation_key=HabitipySensorEntity.EXPERIENCE,
|
||||
native_unit_of_measurement="XP",
|
||||
value_fn=lambda user, _: user.get("stats", {}).get("exp"),
|
||||
),
|
||||
HabitipySensorEntityDescription(
|
||||
key=HabitipySensorEntity.EXPERIENCE_MAX,
|
||||
translation_key=HabitipySensorEntity.EXPERIENCE_MAX,
|
||||
native_unit_of_measurement="XP",
|
||||
value_fn=lambda user, _: user.get("stats", {}).get("toNextLevel"),
|
||||
),
|
||||
HabitipySensorEntityDescription(
|
||||
@@ -128,7 +122,6 @@ SENSOR_DESCRIPTIONS: tuple[HabitipySensorEntityDescription, ...] = (
|
||||
HabitipySensorEntityDescription(
|
||||
key=HabitipySensorEntity.GOLD,
|
||||
translation_key=HabitipySensorEntity.GOLD,
|
||||
native_unit_of_measurement="GP",
|
||||
suggested_display_precision=2,
|
||||
value_fn=lambda user, _: user.get("stats", {}).get("gp"),
|
||||
),
|
||||
@@ -144,7 +137,6 @@ SENSOR_DESCRIPTIONS: tuple[HabitipySensorEntityDescription, ...] = (
|
||||
translation_key=HabitipySensorEntity.GEMS,
|
||||
value_fn=lambda user, _: user.get("balance", 0) * 4,
|
||||
suggested_display_precision=0,
|
||||
native_unit_of_measurement="gems",
|
||||
entity_picture="shop_gem.png",
|
||||
),
|
||||
HabitipySensorEntityDescription(
|
||||
@@ -229,20 +221,17 @@ TASK_SENSOR_DESCRIPTION: tuple[HabitipyTaskSensorEntityDescription, ...] = (
|
||||
HabitipyTaskSensorEntityDescription(
|
||||
key=HabitipySensorEntity.HABITS,
|
||||
translation_key=HabitipySensorEntity.HABITS,
|
||||
native_unit_of_measurement=UNIT_TASKS,
|
||||
value_fn=lambda tasks: [r for r in tasks if r.get("type") == "habit"],
|
||||
),
|
||||
HabitipyTaskSensorEntityDescription(
|
||||
key=HabitipySensorEntity.DAILIES,
|
||||
translation_key=HabitipySensorEntity.DAILIES,
|
||||
native_unit_of_measurement=UNIT_TASKS,
|
||||
value_fn=lambda tasks: [r for r in tasks if r.get("type") == "daily"],
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
HabitipyTaskSensorEntityDescription(
|
||||
key=HabitipySensorEntity.TODOS,
|
||||
translation_key=HabitipySensorEntity.TODOS,
|
||||
native_unit_of_measurement=UNIT_TASKS,
|
||||
value_fn=lambda tasks: [
|
||||
r for r in tasks if r.get("type") == "todo" and not r.get("completed")
|
||||
],
|
||||
@@ -251,7 +240,6 @@ TASK_SENSOR_DESCRIPTION: tuple[HabitipyTaskSensorEntityDescription, ...] = (
|
||||
HabitipyTaskSensorEntityDescription(
|
||||
key=HabitipySensorEntity.REWARDS,
|
||||
translation_key=HabitipySensorEntity.REWARDS,
|
||||
native_unit_of_measurement=UNIT_TASKS,
|
||||
value_fn=lambda tasks: [r for r in tasks if r.get("type") == "reward"],
|
||||
),
|
||||
)
|
||||
|
||||
@@ -2,7 +2,11 @@
|
||||
"common": {
|
||||
"todos": "To-Do's",
|
||||
"dailies": "Dailies",
|
||||
"config_entry_name": "Select character"
|
||||
"config_entry_name": "Select character",
|
||||
"unit_tasks": "tasks",
|
||||
"unit_health_points": "HP",
|
||||
"unit_mana_points": "MP",
|
||||
"unit_experience_points": "XP"
|
||||
},
|
||||
"config": {
|
||||
"abort": {
|
||||
@@ -135,31 +139,39 @@
|
||||
"name": "Display name"
|
||||
},
|
||||
"health": {
|
||||
"name": "Health"
|
||||
"name": "Health",
|
||||
"unit_of_measurement": "[%key:component::habitica::common::unit_health_points%]"
|
||||
},
|
||||
"health_max": {
|
||||
"name": "Max. health"
|
||||
"name": "Max. health",
|
||||
"unit_of_measurement": "[%key:component::habitica::common::unit_health_points%]"
|
||||
},
|
||||
"mana": {
|
||||
"name": "Mana"
|
||||
"name": "Mana",
|
||||
"unit_of_measurement": "[%key:component::habitica::common::unit_mana_points%]"
|
||||
},
|
||||
"mana_max": {
|
||||
"name": "Max. mana"
|
||||
"name": "Max. mana",
|
||||
"unit_of_measurement": "[%key:component::habitica::common::unit_mana_points%]"
|
||||
},
|
||||
"experience": {
|
||||
"name": "Experience"
|
||||
"name": "Experience",
|
||||
"unit_of_measurement": "[%key:component::habitica::common::unit_experience_points%]"
|
||||
},
|
||||
"experience_max": {
|
||||
"name": "Next level"
|
||||
"name": "Next level",
|
||||
"unit_of_measurement": "[%key:component::habitica::common::unit_experience_points%]"
|
||||
},
|
||||
"level": {
|
||||
"name": "Level"
|
||||
},
|
||||
"gold": {
|
||||
"name": "Gold"
|
||||
"name": "Gold",
|
||||
"unit_of_measurement": "GP"
|
||||
},
|
||||
"gems": {
|
||||
"name": "Gems"
|
||||
"name": "Gems",
|
||||
"unit_of_measurement": "gems"
|
||||
},
|
||||
"trinkets": {
|
||||
"name": "Mystic hourglasses"
|
||||
@@ -174,16 +186,20 @@
|
||||
}
|
||||
},
|
||||
"todos": {
|
||||
"name": "[%key:component::habitica::common::todos%]"
|
||||
"name": "[%key:component::habitica::common::todos%]",
|
||||
"unit_of_measurement": "[%key:component::habitica::common::unit_tasks%]"
|
||||
},
|
||||
"dailys": {
|
||||
"name": "[%key:component::habitica::common::dailies%]"
|
||||
"name": "[%key:component::habitica::common::dailies%]",
|
||||
"unit_of_measurement": "[%key:component::habitica::common::unit_tasks%]"
|
||||
},
|
||||
"habits": {
|
||||
"name": "Habits"
|
||||
"name": "Habits",
|
||||
"unit_of_measurement": "[%key:component::habitica::common::unit_tasks%]"
|
||||
},
|
||||
"rewards": {
|
||||
"name": "Rewards"
|
||||
"name": "Rewards",
|
||||
"unit_of_measurement": "[%key:component::habitica::common::unit_tasks%]"
|
||||
},
|
||||
"strength": {
|
||||
"name": "Strength",
|
||||
|
||||
@@ -174,7 +174,7 @@ def get_attribute_points(
|
||||
)
|
||||
|
||||
return {
|
||||
"level": min(round(user["stats"]["lvl"] / 2), 50),
|
||||
"level": min(floor(user["stats"]["lvl"] / 2), 50),
|
||||
"equipment": equipment,
|
||||
"class": class_bonus,
|
||||
"allocated": user["stats"][attribute],
|
||||
|
||||
@@ -22,7 +22,7 @@ import homeassistant.util.dt as dt_util
|
||||
|
||||
from . import websocket_api
|
||||
from .const import DOMAIN
|
||||
from .helpers import entities_may_have_state_changes_after, has_recorder_run_after
|
||||
from .helpers import entities_may_have_state_changes_after, has_states_before
|
||||
|
||||
CONF_ORDER = "use_include_order"
|
||||
|
||||
@@ -107,7 +107,10 @@ class HistoryPeriodView(HomeAssistantView):
|
||||
no_attributes = "no_attributes" in request.query
|
||||
|
||||
if (
|
||||
(end_time and not has_recorder_run_after(hass, end_time))
|
||||
# has_states_before will return True if there are states older than
|
||||
# end_time. If it's false, we know there are no states in the
|
||||
# database up until end_time.
|
||||
(end_time and not has_states_before(hass, end_time))
|
||||
or not include_start_time_state
|
||||
and entity_ids
|
||||
and not entities_may_have_state_changes_after(
|
||||
|
||||
@@ -6,7 +6,6 @@ from collections.abc import Iterable
|
||||
from datetime import datetime as dt
|
||||
|
||||
from homeassistant.components.recorder import get_instance
|
||||
from homeassistant.components.recorder.models import process_timestamp
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
|
||||
@@ -26,8 +25,10 @@ def entities_may_have_state_changes_after(
|
||||
return False
|
||||
|
||||
|
||||
def has_recorder_run_after(hass: HomeAssistant, run_time: dt) -> bool:
|
||||
"""Check if the recorder has any runs after a specific time."""
|
||||
return run_time >= process_timestamp(
|
||||
get_instance(hass).recorder_runs_manager.first.start
|
||||
)
|
||||
def has_states_before(hass: HomeAssistant, run_time: dt) -> bool:
|
||||
"""Check if the recorder has states as old or older than run_time.
|
||||
|
||||
Returns True if there may be such states.
|
||||
"""
|
||||
oldest_ts = get_instance(hass).states_manager.oldest_ts
|
||||
return oldest_ts is not None and run_time.timestamp() >= oldest_ts
|
||||
|
||||
@@ -39,7 +39,7 @@ from homeassistant.util.async_ import create_eager_task
|
||||
import homeassistant.util.dt as dt_util
|
||||
|
||||
from .const import EVENT_COALESCE_TIME, MAX_PENDING_HISTORY_STATES
|
||||
from .helpers import entities_may_have_state_changes_after, has_recorder_run_after
|
||||
from .helpers import entities_may_have_state_changes_after, has_states_before
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -142,7 +142,10 @@ async def ws_get_history_during_period(
|
||||
no_attributes = msg["no_attributes"]
|
||||
|
||||
if (
|
||||
(end_time and not has_recorder_run_after(hass, end_time))
|
||||
# has_states_before will return True if there are states older than
|
||||
# end_time. If it's false, we know there are no states in the
|
||||
# database up until end_time.
|
||||
(end_time and not has_states_before(hass, end_time))
|
||||
or not include_start_time_state
|
||||
and entity_ids
|
||||
and not entities_may_have_state_changes_after(
|
||||
|
||||
@@ -16,7 +16,7 @@ from homeassistant.components.light import (
|
||||
LightEntityDescription,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ServiceValidationError
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
import homeassistant.util.color as color_util
|
||||
|
||||
@@ -150,7 +150,7 @@ class HomeConnectLight(HomeConnectEntity, LightEntity):
|
||||
self.device.appliance.set_setting, self.bsh_key, True
|
||||
)
|
||||
except HomeConnectError as err:
|
||||
raise ServiceValidationError(
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="turn_on_light",
|
||||
translation_placeholders={
|
||||
@@ -169,7 +169,7 @@ class HomeConnectLight(HomeConnectEntity, LightEntity):
|
||||
self._enable_custom_color_value_key,
|
||||
)
|
||||
except HomeConnectError as err:
|
||||
raise ServiceValidationError(
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="select_light_custom_color",
|
||||
translation_placeholders={
|
||||
@@ -187,7 +187,7 @@ class HomeConnectLight(HomeConnectEntity, LightEntity):
|
||||
f"#{hex_val}",
|
||||
)
|
||||
except HomeConnectError as err:
|
||||
raise ServiceValidationError(
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="set_light_color",
|
||||
translation_placeholders={
|
||||
@@ -219,7 +219,7 @@ class HomeConnectLight(HomeConnectEntity, LightEntity):
|
||||
f"#{hex_val}",
|
||||
)
|
||||
except HomeConnectError as err:
|
||||
raise ServiceValidationError(
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="set_light_color",
|
||||
translation_placeholders={
|
||||
@@ -244,7 +244,7 @@ class HomeConnectLight(HomeConnectEntity, LightEntity):
|
||||
self.device.appliance.set_setting, self._brightness_key, brightness
|
||||
)
|
||||
except HomeConnectError as err:
|
||||
raise ServiceValidationError(
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="set_light_brightness",
|
||||
translation_placeholders={
|
||||
@@ -263,7 +263,7 @@ class HomeConnectLight(HomeConnectEntity, LightEntity):
|
||||
self.device.appliance.set_setting, self.bsh_key, False
|
||||
)
|
||||
except HomeConnectError as err:
|
||||
raise ServiceValidationError(
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="turn_off_light",
|
||||
translation_placeholders={
|
||||
|
||||
@@ -12,7 +12,7 @@ from homeassistant.components.number import (
|
||||
NumberEntityDescription,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ServiceValidationError
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from . import HomeConnectConfigEntry, get_dict_from_home_connect_error
|
||||
@@ -117,7 +117,7 @@ class HomeConnectNumberEntity(HomeConnectEntity, NumberEntity):
|
||||
value,
|
||||
)
|
||||
except HomeConnectError as err:
|
||||
raise ServiceValidationError(
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="set_setting",
|
||||
translation_placeholders={
|
||||
|
||||
@@ -7,7 +7,7 @@ from homeconnect.api import HomeConnectError
|
||||
|
||||
from homeassistant.components.select import SelectEntity, SelectEntityDescription
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ServiceValidationError
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from . import (
|
||||
@@ -140,12 +140,12 @@ TRANSLATION_KEYS_PROGRAMS_MAP = {
|
||||
"Cooking.Oven.Program.HeatingMode.HotAir80Steam",
|
||||
"Cooking.Oven.Program.HeatingMode.HotAir100Steam",
|
||||
"Cooking.Oven.Program.HeatingMode.SabbathProgramme",
|
||||
"Cooking.Oven.Program.Microwave90Watt",
|
||||
"Cooking.Oven.Program.Microwave180Watt",
|
||||
"Cooking.Oven.Program.Microwave360Watt",
|
||||
"Cooking.Oven.Program.Microwave600Watt",
|
||||
"Cooking.Oven.Program.Microwave900Watt",
|
||||
"Cooking.Oven.Program.Microwave1000Watt",
|
||||
"Cooking.Oven.Program.Microwave.90Watt",
|
||||
"Cooking.Oven.Program.Microwave.180Watt",
|
||||
"Cooking.Oven.Program.Microwave.360Watt",
|
||||
"Cooking.Oven.Program.Microwave.600Watt",
|
||||
"Cooking.Oven.Program.Microwave.900Watt",
|
||||
"Cooking.Oven.Program.Microwave.1000Watt",
|
||||
"Cooking.Oven.Program.Microwave.Max",
|
||||
"Cooking.Oven.Program.HeatingMode.WarmingDrawer",
|
||||
"LaundryCare.Washer.Program.Cotton",
|
||||
@@ -289,7 +289,7 @@ class HomeConnectProgramSelectEntity(HomeConnectEntity, SelectEntity):
|
||||
translation_key = "start_program"
|
||||
else:
|
||||
translation_key = "select_program"
|
||||
raise ServiceValidationError(
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key=translation_key,
|
||||
translation_placeholders={
|
||||
|
||||
@@ -23,43 +23,43 @@
|
||||
},
|
||||
"exceptions": {
|
||||
"turn_on_light": {
|
||||
"message": "Error while trying to turn on {entity_id}: {description}"
|
||||
"message": "Error turning on {entity_id}: {description}"
|
||||
},
|
||||
"turn_off_light": {
|
||||
"message": "Error while trying to turn off {entity_id}: {description}"
|
||||
"message": "Error turning off {entity_id}: {description}"
|
||||
},
|
||||
"set_light_brightness": {
|
||||
"message": "Error while trying to set brightness of {entity_id}: {description}"
|
||||
"message": "Error setting brightness of {entity_id}: {description}"
|
||||
},
|
||||
"select_light_custom_color": {
|
||||
"message": "Error while trying to select custom color of {entity_id}: {description}"
|
||||
"message": "Error selecting custom color of {entity_id}: {description}"
|
||||
},
|
||||
"set_light_color": {
|
||||
"message": "Error while trying to set color of {entity_id}: {description}"
|
||||
"message": "Error setting color of {entity_id}: {description}"
|
||||
},
|
||||
"set_setting": {
|
||||
"message": "Error while trying to assign the value \"{value}\" to the setting \"{setting_key}\" for {entity_id}: {description}"
|
||||
"message": "Error assigning the value \"{value}\" to the setting \"{setting_key}\" for {entity_id}: {description}"
|
||||
},
|
||||
"turn_on": {
|
||||
"message": "Error while trying to turn on {entity_id} ({setting_key}): {description}"
|
||||
"message": "Error turning on {entity_id} ({setting_key}): {description}"
|
||||
},
|
||||
"turn_off": {
|
||||
"message": "Error while trying to turn off {entity_id} ({setting_key}): {description}"
|
||||
"message": "Error turning off {entity_id} ({setting_key}): {description}"
|
||||
},
|
||||
"select_program": {
|
||||
"message": "Error while trying to select program {program}: {description}"
|
||||
"message": "Error selecting program {program}: {description}"
|
||||
},
|
||||
"start_program": {
|
||||
"message": "Error while trying to start program {program}: {description}"
|
||||
"message": "Error starting program {program}: {description}"
|
||||
},
|
||||
"stop_program": {
|
||||
"message": "Error while trying to stop program {program}: {description}"
|
||||
"message": "Error stopping program {program}: {description}"
|
||||
},
|
||||
"power_on": {
|
||||
"message": "Error while trying to turn on {appliance_name}: {description}"
|
||||
"message": "Error turning on {appliance_name}: {description}"
|
||||
},
|
||||
"power_off": {
|
||||
"message": "Error while trying to turn off {appliance_name} with value \"{value}\": {description}"
|
||||
"message": "Error turning off {appliance_name} with value \"{value}\": {description}"
|
||||
},
|
||||
"turn_off_not_supported": {
|
||||
"message": "{appliance_name} does not support turning off or entering standby mode."
|
||||
|
||||
@@ -8,7 +8,7 @@ from homeconnect.api import HomeConnectError
|
||||
|
||||
from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ServiceValidationError
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from . import HomeConnectConfigEntry, get_dict_from_home_connect_error
|
||||
@@ -134,7 +134,7 @@ class HomeConnectSwitch(HomeConnectEntity, SwitchEntity):
|
||||
)
|
||||
except HomeConnectError as err:
|
||||
self._attr_available = False
|
||||
raise ServiceValidationError(
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="turn_on",
|
||||
translation_placeholders={
|
||||
@@ -158,7 +158,7 @@ class HomeConnectSwitch(HomeConnectEntity, SwitchEntity):
|
||||
except HomeConnectError as err:
|
||||
_LOGGER.error("Error while trying to turn off: %s", err)
|
||||
self._attr_available = False
|
||||
raise ServiceValidationError(
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="turn_off",
|
||||
translation_placeholders={
|
||||
@@ -209,7 +209,7 @@ class HomeConnectProgramSwitch(HomeConnectEntity, SwitchEntity):
|
||||
self.device.appliance.start_program, self.program_name
|
||||
)
|
||||
except HomeConnectError as err:
|
||||
raise ServiceValidationError(
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="start_program",
|
||||
translation_placeholders={
|
||||
@@ -225,7 +225,7 @@ class HomeConnectProgramSwitch(HomeConnectEntity, SwitchEntity):
|
||||
try:
|
||||
await self.hass.async_add_executor_job(self.device.appliance.stop_program)
|
||||
except HomeConnectError as err:
|
||||
raise ServiceValidationError(
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="stop_program",
|
||||
translation_placeholders={
|
||||
@@ -278,7 +278,7 @@ class HomeConnectPowerSwitch(HomeConnectEntity, SwitchEntity):
|
||||
)
|
||||
except HomeConnectError as err:
|
||||
self._attr_is_on = False
|
||||
raise ServiceValidationError(
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="power_on",
|
||||
translation_placeholders={
|
||||
@@ -291,7 +291,7 @@ class HomeConnectPowerSwitch(HomeConnectEntity, SwitchEntity):
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
"""Switch the device off."""
|
||||
if not hasattr(self, "power_off_state"):
|
||||
raise ServiceValidationError(
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="unable_to_retrieve_turn_off",
|
||||
translation_placeholders={
|
||||
@@ -300,7 +300,7 @@ class HomeConnectPowerSwitch(HomeConnectEntity, SwitchEntity):
|
||||
)
|
||||
|
||||
if self.power_off_state is None:
|
||||
raise ServiceValidationError(
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="turn_off_not_supported",
|
||||
translation_placeholders={
|
||||
@@ -316,7 +316,7 @@ class HomeConnectPowerSwitch(HomeConnectEntity, SwitchEntity):
|
||||
)
|
||||
except HomeConnectError as err:
|
||||
self._attr_is_on = True
|
||||
raise ServiceValidationError(
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="power_off",
|
||||
translation_placeholders={
|
||||
|
||||
@@ -7,7 +7,7 @@ from homeconnect.api import HomeConnectError
|
||||
|
||||
from homeassistant.components.time import TimeEntity, TimeEntityDescription
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ServiceValidationError
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from . import HomeConnectConfigEntry, get_dict_from_home_connect_error
|
||||
@@ -80,7 +80,7 @@ class HomeConnectTimeEntity(HomeConnectEntity, TimeEntity):
|
||||
time_to_seconds(value),
|
||||
)
|
||||
except HomeConnectError as err:
|
||||
raise ServiceValidationError(
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="set_setting",
|
||||
translation_placeholders={
|
||||
|
||||
@@ -14,6 +14,6 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/homekit_controller",
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["aiohomekit", "commentjson"],
|
||||
"requirements": ["aiohomekit==3.2.6"],
|
||||
"requirements": ["aiohomekit==3.2.7"],
|
||||
"zeroconf": ["_hap._tcp.local.", "_hap._udp.local."]
|
||||
}
|
||||
|
||||
@@ -326,7 +326,8 @@ class HomeAssistantApplication(web.Application):
|
||||
protocol,
|
||||
writer,
|
||||
task,
|
||||
loop=self._loop,
|
||||
# loop will never be None when called from aiohttp
|
||||
loop=self._loop, # type: ignore[arg-type]
|
||||
client_max_size=self._client_max_size,
|
||||
)
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ from __future__ import annotations
|
||||
|
||||
from datetime import timedelta
|
||||
from enum import StrEnum
|
||||
from functools import partial
|
||||
import logging
|
||||
from typing import Any, final
|
||||
|
||||
@@ -22,11 +21,6 @@ from homeassistant.const import (
|
||||
from homeassistant.core import HomeAssistant, ServiceCall
|
||||
from homeassistant.exceptions import ServiceValidationError
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.deprecation import (
|
||||
all_with_deprecated_constants,
|
||||
check_if_deprecated_constant,
|
||||
dir_with_deprecated_constants,
|
||||
)
|
||||
from homeassistant.helpers.entity import ToggleEntity, ToggleEntityDescription
|
||||
from homeassistant.helpers.entity_component import EntityComponent
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
@@ -34,9 +28,6 @@ from homeassistant.loader import bind_hass
|
||||
from homeassistant.util.hass_dict import HassKey
|
||||
|
||||
from .const import ( # noqa: F401
|
||||
_DEPRECATED_DEVICE_CLASS_DEHUMIDIFIER,
|
||||
_DEPRECATED_DEVICE_CLASS_HUMIDIFIER,
|
||||
_DEPRECATED_SUPPORT_MODES,
|
||||
ATTR_ACTION,
|
||||
ATTR_AVAILABLE_MODES,
|
||||
ATTR_CURRENT_HUMIDITY,
|
||||
@@ -314,13 +305,3 @@ async def async_service_humidity_set(
|
||||
)
|
||||
|
||||
await entity.async_set_humidity(humidity)
|
||||
|
||||
|
||||
# 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())
|
||||
|
||||
@@ -1,15 +1,6 @@
|
||||
"""Provides the constants needed for component."""
|
||||
|
||||
from enum import IntFlag, StrEnum
|
||||
from functools import partial
|
||||
|
||||
from homeassistant.helpers.deprecation import (
|
||||
DeprecatedConstant,
|
||||
DeprecatedConstantEnum,
|
||||
all_with_deprecated_constants,
|
||||
check_if_deprecated_constant,
|
||||
dir_with_deprecated_constants,
|
||||
)
|
||||
|
||||
MODE_NORMAL = "normal"
|
||||
MODE_ECO = "eco"
|
||||
@@ -43,15 +34,6 @@ DEFAULT_MAX_HUMIDITY = 100
|
||||
|
||||
DOMAIN = "humidifier"
|
||||
|
||||
# DEVICE_CLASS_* below are deprecated as of 2021.12
|
||||
# use the HumidifierDeviceClass enum instead.
|
||||
_DEPRECATED_DEVICE_CLASS_HUMIDIFIER = DeprecatedConstant(
|
||||
"humidifier", "HumidifierDeviceClass.HUMIDIFIER", "2025.1"
|
||||
)
|
||||
_DEPRECATED_DEVICE_CLASS_DEHUMIDIFIER = DeprecatedConstant(
|
||||
"dehumidifier", "HumidifierDeviceClass.DEHUMIDIFIER", "2025.1"
|
||||
)
|
||||
|
||||
SERVICE_SET_MODE = "set_mode"
|
||||
SERVICE_SET_HUMIDITY = "set_humidity"
|
||||
|
||||
@@ -60,17 +42,3 @@ class HumidifierEntityFeature(IntFlag):
|
||||
"""Supported features of the humidifier entity."""
|
||||
|
||||
MODES = 1
|
||||
|
||||
|
||||
# The SUPPORT_MODES constant is deprecated as of Home Assistant 2022.5.
|
||||
# Please use the HumidifierEntityFeature enum instead.
|
||||
_DEPRECATED_SUPPORT_MODES = DeprecatedConstantEnum(
|
||||
HumidifierEntityFeature.MODES, "2025.1"
|
||||
)
|
||||
|
||||
# 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())
|
||||
|
||||
@@ -364,7 +364,7 @@ class NevermindIntentHandler(intent.IntentHandler):
|
||||
"""Takes no action."""
|
||||
|
||||
intent_type = intent.INTENT_NEVERMIND
|
||||
description = "Cancels the current request and does nothing"
|
||||
description = "Cancel the current conversation if it was started by mistake or the user wants it to stop."
|
||||
|
||||
async def async_handle(self, intent_obj: intent.Intent) -> intent.IntentResponse:
|
||||
"""Do nothing and produces an empty response."""
|
||||
|
||||
@@ -13,5 +13,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/iron_os",
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["pynecil", "aiogithubapi"],
|
||||
"requirements": ["pynecil==0.2.1", "aiogithubapi==24.6.0"]
|
||||
"requirements": ["pynecil==1.0.1", "aiogithubapi==24.6.0"]
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@ from . import IronOSConfigEntry
|
||||
from .const import DOMAIN, MAX_TEMP, MIN_TEMP
|
||||
from .entity import IronOSBaseEntity
|
||||
|
||||
PARALLEL_UPDATES = 0
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class IronOSNumberEntityDescription(NumberEntityDescription):
|
||||
|
||||
@@ -107,6 +107,7 @@ PINECIL_SENSOR_DESCRIPTIONS: tuple[IronOSSensorEntityDescription, ...] = (
|
||||
native_unit_of_measurement=OHM,
|
||||
value_fn=lambda data: data.tip_resistance,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
IronOSSensorEntityDescription(
|
||||
key=PinecilSensor.UPTIME,
|
||||
@@ -137,7 +138,7 @@ PINECIL_SENSOR_DESCRIPTIONS: tuple[IronOSSensorEntityDescription, ...] = (
|
||||
IronOSSensorEntityDescription(
|
||||
key=PinecilSensor.TIP_VOLTAGE,
|
||||
translation_key=PinecilSensor.TIP_VOLTAGE,
|
||||
native_unit_of_measurement=UnitOfElectricPotential.MILLIVOLT,
|
||||
native_unit_of_measurement=UnitOfElectricPotential.MICROVOLT,
|
||||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
suggested_display_precision=3,
|
||||
|
||||
@@ -15,6 +15,8 @@ from . import IRON_OS_KEY, IronOSConfigEntry, IronOSLiveDataCoordinator
|
||||
from .coordinator import IronOSFirmwareUpdateCoordinator
|
||||
from .entity import IronOSBaseEntity
|
||||
|
||||
PARALLEL_UPDATES = 0
|
||||
|
||||
UPDATE_DESCRIPTION = UpdateEntityDescription(
|
||||
key="firmware",
|
||||
device_class=UpdateDeviceClass.FIRMWARE,
|
||||
|
||||
@@ -71,7 +71,6 @@ SENSOR_DESCRIPTIONS: tuple[IstaSensorEntityDescription, ...] = (
|
||||
translation_key=IstaSensorEntity.HEATING,
|
||||
suggested_display_precision=0,
|
||||
consumption_type=IstaConsumptionType.HEATING,
|
||||
native_unit_of_measurement="units",
|
||||
state_class=SensorStateClass.TOTAL,
|
||||
),
|
||||
IstaSensorEntityDescription(
|
||||
|
||||
@@ -38,7 +38,8 @@
|
||||
"entity": {
|
||||
"sensor": {
|
||||
"heating": {
|
||||
"name": "Heating"
|
||||
"name": "Heating",
|
||||
"unit_of_measurement": "units"
|
||||
},
|
||||
"heating_cost": {
|
||||
"name": "Heating cost"
|
||||
|
||||
@@ -54,6 +54,7 @@ from .const import (
|
||||
CONF_KNX_SECURE_USER_PASSWORD,
|
||||
CONF_KNX_STATE_UPDATER,
|
||||
CONF_KNX_TELEGRAM_LOG_SIZE,
|
||||
CONF_KNX_TUNNEL_ENDPOINT_IA,
|
||||
CONF_KNX_TUNNELING,
|
||||
CONF_KNX_TUNNELING_TCP,
|
||||
CONF_KNX_TUNNELING_TCP_SECURE,
|
||||
@@ -352,6 +353,7 @@ class KNXModule:
|
||||
if _conn_type == CONF_KNX_TUNNELING_TCP:
|
||||
return ConnectionConfig(
|
||||
connection_type=ConnectionType.TUNNELING_TCP,
|
||||
individual_address=self.entry.data.get(CONF_KNX_TUNNEL_ENDPOINT_IA),
|
||||
gateway_ip=self.entry.data[CONF_HOST],
|
||||
gateway_port=self.entry.data[CONF_PORT],
|
||||
auto_reconnect=True,
|
||||
@@ -364,6 +366,7 @@ class KNXModule:
|
||||
if _conn_type == CONF_KNX_TUNNELING_TCP_SECURE:
|
||||
return ConnectionConfig(
|
||||
connection_type=ConnectionType.TUNNELING_TCP_SECURE,
|
||||
individual_address=self.entry.data.get(CONF_KNX_TUNNEL_ENDPOINT_IA),
|
||||
gateway_ip=self.entry.data[CONF_HOST],
|
||||
gateway_port=self.entry.data[CONF_PORT],
|
||||
secure_config=SecureConfig(
|
||||
|
||||
@@ -104,7 +104,7 @@ class KNXConfigEntryData(TypedDict, total=False):
|
||||
route_back: bool # not required
|
||||
host: str # only required for tunnelling
|
||||
port: int # only required for tunnelling
|
||||
tunnel_endpoint_ia: str | None
|
||||
tunnel_endpoint_ia: str | None # tunnelling only - not required (use get())
|
||||
# KNX secure
|
||||
user_id: int | None # not required
|
||||
user_password: str | None # not required
|
||||
|
||||
@@ -291,6 +291,7 @@ class LmConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
CONF_ADDRESS: discovery_info.macaddress,
|
||||
}
|
||||
)
|
||||
self._async_abort_entries_match({CONF_ADDRESS: discovery_info.macaddress})
|
||||
|
||||
_LOGGER.debug(
|
||||
"Discovered La Marzocco machine %s through DHCP at address %s",
|
||||
|
||||
@@ -36,5 +36,5 @@
|
||||
"integration_type": "device",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["pylamarzocco"],
|
||||
"requirements": ["pylamarzocco==1.2.11"]
|
||||
"requirements": ["pylamarzocco==1.2.12"]
|
||||
}
|
||||
|
||||
@@ -67,8 +67,10 @@
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"title": "Update Configuration",
|
||||
"use_bluetooth": "Use Bluetooth"
|
||||
},
|
||||
"data_description": {
|
||||
"use_bluetooth": "Should the integration try to use Bluetooth to control the machine?"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,6 @@ from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import ServiceValidationError
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.deprecation import (
|
||||
DeprecatedConstantEnum,
|
||||
all_with_deprecated_constants,
|
||||
check_if_deprecated_constant,
|
||||
dir_with_deprecated_constants,
|
||||
@@ -67,10 +66,6 @@ class LockEntityFeature(IntFlag):
|
||||
OPEN = 1
|
||||
|
||||
|
||||
# The SUPPORT_OPEN constant is deprecated as of Home Assistant 2022.5.
|
||||
# Please use the LockEntityFeature enum instead.
|
||||
_DEPRECATED_SUPPORT_OPEN = DeprecatedConstantEnum(LockEntityFeature.OPEN, "2025.1")
|
||||
|
||||
PROP_TO_ATTR = {"changed_by": ATTR_CHANGED_BY, "code_format": ATTR_CODE_FORMAT}
|
||||
|
||||
# mypy: disallow-any-generics
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/met_eireann",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["meteireann"],
|
||||
"requirements": ["PyMetEireann==2021.8.0"]
|
||||
"requirements": ["PyMetEireann==2024.11.0"]
|
||||
}
|
||||
|
||||
@@ -86,7 +86,10 @@ rules:
|
||||
comment: >
|
||||
This is not possible because the integrations generates entities
|
||||
based on a user supplied config or discovery.
|
||||
reconfiguration-flow: done
|
||||
reconfiguration-flow:
|
||||
status: exempt
|
||||
comment: >
|
||||
This integration is reconfigured via options flow.
|
||||
dynamic-devices:
|
||||
status: done
|
||||
comment: |
|
||||
|
||||
@@ -7,6 +7,6 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/music_assistant",
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["music_assistant"],
|
||||
"requirements": ["music-assistant-client==1.0.5"],
|
||||
"requirements": ["music-assistant-client==1.0.8"],
|
||||
"zeroconf": ["_mass._tcp.local."]
|
||||
}
|
||||
|
||||
@@ -193,7 +193,7 @@ class MusicAssistantPlayer(MusicAssistantEntity, MediaPlayerEntity):
|
||||
super().__init__(mass, player_id)
|
||||
self._attr_icon = self.player.icon.replace("mdi-", "mdi:")
|
||||
self._attr_supported_features = SUPPORTED_FEATURES
|
||||
if PlayerFeature.SYNC in self.player.supported_features:
|
||||
if PlayerFeature.SET_MEMBERS in self.player.supported_features:
|
||||
self._attr_supported_features |= MediaPlayerEntityFeature.GROUPING
|
||||
if PlayerFeature.VOLUME_MUTE in self.player.supported_features:
|
||||
self._attr_supported_features |= MediaPlayerEntityFeature.VOLUME_MUTE
|
||||
@@ -400,19 +400,19 @@ class MusicAssistantPlayer(MusicAssistantEntity, MediaPlayerEntity):
|
||||
async def async_join_players(self, group_members: list[str]) -> None:
|
||||
"""Join `group_members` as a player group with the current player."""
|
||||
player_ids: list[str] = []
|
||||
entity_registry = er.async_get(self.hass)
|
||||
for child_entity_id in group_members:
|
||||
# resolve HA entity_id to MA player_id
|
||||
if (hass_state := self.hass.states.get(child_entity_id)) is None:
|
||||
continue
|
||||
if (mass_player_id := hass_state.attributes.get("mass_player_id")) is None:
|
||||
continue
|
||||
player_ids.append(mass_player_id)
|
||||
await self.mass.players.player_command_sync_many(self.player_id, player_ids)
|
||||
if not (entity_reg_entry := entity_registry.async_get(child_entity_id)):
|
||||
raise HomeAssistantError(f"Entity {child_entity_id} not found")
|
||||
# unique id is the MA player_id
|
||||
player_ids.append(entity_reg_entry.unique_id)
|
||||
await self.mass.players.player_command_group_many(self.player_id, player_ids)
|
||||
|
||||
@catch_musicassistant_error
|
||||
async def async_unjoin_player(self) -> None:
|
||||
"""Remove this player from any group."""
|
||||
await self.mass.players.player_command_unsync(self.player_id)
|
||||
await self.mass.players.player_command_ungroup(self.player_id)
|
||||
|
||||
@catch_musicassistant_error
|
||||
async def _async_handle_play_media(
|
||||
|
||||
@@ -59,9 +59,7 @@ from .const import (
|
||||
CONF_SUBSCRIBER_ID,
|
||||
CONF_SUBSCRIBER_ID_IMPORTED,
|
||||
CONF_SUBSCRIPTION_NAME,
|
||||
DATA_DEVICE_MANAGER,
|
||||
DATA_SDM,
|
||||
DATA_SUBSCRIBER,
|
||||
DOMAIN,
|
||||
)
|
||||
from .events import EVENT_NAME_MAP, NEST_EVENT
|
||||
@@ -72,6 +70,7 @@ from .media_source import (
|
||||
async_get_media_source_devices,
|
||||
async_get_transcoder,
|
||||
)
|
||||
from .types import NestConfigEntry, NestData
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -113,11 +112,8 @@ THUMBNAIL_SIZE_PX = 175
|
||||
|
||||
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
"""Set up Nest components with dispatch between old/new flows."""
|
||||
hass.data[DOMAIN] = {}
|
||||
|
||||
hass.http.register_view(NestEventMediaView(hass))
|
||||
hass.http.register_view(NestEventMediaThumbnailView(hass))
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@@ -128,12 +124,12 @@ class SignalUpdateCallback:
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
config_reload_cb: Callable[[], Awaitable[None]],
|
||||
config_entry_id: str,
|
||||
config_entry: NestConfigEntry,
|
||||
) -> None:
|
||||
"""Initialize EventCallback."""
|
||||
self._hass = hass
|
||||
self._config_reload_cb = config_reload_cb
|
||||
self._config_entry_id = config_entry_id
|
||||
self._config_entry = config_entry
|
||||
|
||||
async def async_handle_event(self, event_message: EventMessage) -> None:
|
||||
"""Process an incoming EventMessage."""
|
||||
@@ -181,17 +177,17 @@ class SignalUpdateCallback:
|
||||
message["zones"] = image_event.zones
|
||||
self._hass.bus.async_fire(NEST_EVENT, message)
|
||||
|
||||
def _supported_traits(self, device_id: str) -> list[TraitType]:
|
||||
if not (
|
||||
device_manager := self._hass.data[DOMAIN]
|
||||
.get(self._config_entry_id, {})
|
||||
.get(DATA_DEVICE_MANAGER)
|
||||
) or not (device := device_manager.devices.get(device_id)):
|
||||
def _supported_traits(self, device_id: str) -> list[str]:
|
||||
if (
|
||||
not self._config_entry.runtime_data
|
||||
or not (device_manager := self._config_entry.runtime_data.device_manager)
|
||||
or not (device := device_manager.devices.get(device_id))
|
||||
):
|
||||
return []
|
||||
return list(device.traits)
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: NestConfigEntry) -> bool:
|
||||
"""Set up Nest from a config entry with dispatch between old/new flows."""
|
||||
if DATA_SDM not in entry.data:
|
||||
hass.async_create_task(hass.config_entries.async_remove(entry.entry_id))
|
||||
@@ -215,7 +211,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_config_reload() -> None:
|
||||
await hass.config_entries.async_reload(entry.entry_id)
|
||||
|
||||
update_callback = SignalUpdateCallback(hass, async_config_reload, entry.entry_id)
|
||||
update_callback = SignalUpdateCallback(hass, async_config_reload, entry)
|
||||
subscriber.set_update_callback(update_callback.async_handle_event)
|
||||
try:
|
||||
await subscriber.start_async()
|
||||
@@ -245,11 +241,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
entry.async_on_unload(
|
||||
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, on_hass_stop)
|
||||
)
|
||||
|
||||
hass.data[DOMAIN][entry.entry_id] = {
|
||||
DATA_SUBSCRIBER: subscriber,
|
||||
DATA_DEVICE_MANAGER: device_manager,
|
||||
}
|
||||
entry.runtime_data = NestData(
|
||||
subscriber=subscriber,
|
||||
device_manager=device_manager,
|
||||
)
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
@@ -262,13 +257,9 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
# Legacy API
|
||||
return True
|
||||
_LOGGER.debug("Stopping nest subscriber")
|
||||
subscriber = hass.data[DOMAIN][entry.entry_id][DATA_SUBSCRIBER]
|
||||
subscriber = entry.runtime_data.subscriber
|
||||
subscriber.stop_async()
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
if unload_ok:
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
|
||||
async def async_remove_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
|
||||
@@ -17,7 +17,6 @@ from google_nest_sdm.camera_traits import (
|
||||
WebRtcStream,
|
||||
)
|
||||
from google_nest_sdm.device import Device
|
||||
from google_nest_sdm.device_manager import DeviceManager
|
||||
from google_nest_sdm.exceptions import ApiException
|
||||
from webrtc_models import RTCIceCandidateInit
|
||||
|
||||
@@ -29,15 +28,14 @@ from homeassistant.components.camera import (
|
||||
WebRTCSendMessage,
|
||||
)
|
||||
from homeassistant.components.stream import CONF_EXTRA_PART_WAIT_TIME
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.event import async_track_point_in_utc_time
|
||||
from homeassistant.util.dt import utcnow
|
||||
|
||||
from .const import DATA_DEVICE_MANAGER, DOMAIN
|
||||
from .device_info import NestDeviceInfo
|
||||
from .types import NestConfigEntry
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -53,15 +51,12 @@ BACKOFF_MULTIPLIER = 1.5
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||
hass: HomeAssistant, entry: NestConfigEntry, async_add_entities: AddEntitiesCallback
|
||||
) -> None:
|
||||
"""Set up the cameras."""
|
||||
|
||||
device_manager: DeviceManager = hass.data[DOMAIN][entry.entry_id][
|
||||
DATA_DEVICE_MANAGER
|
||||
]
|
||||
entities: list[NestCameraBaseEntity] = []
|
||||
for device in device_manager.devices.values():
|
||||
for device in entry.runtime_data.device_manager.devices.values():
|
||||
if (live_stream := device.traits.get(CameraLiveStreamTrait.NAME)) is None:
|
||||
continue
|
||||
if StreamingProtocol.WEB_RTC in live_stream.supported_protocols:
|
||||
|
||||
@@ -5,7 +5,6 @@ from __future__ import annotations
|
||||
from typing import Any, cast
|
||||
|
||||
from google_nest_sdm.device import Device
|
||||
from google_nest_sdm.device_manager import DeviceManager
|
||||
from google_nest_sdm.device_traits import FanTrait, TemperatureTrait
|
||||
from google_nest_sdm.exceptions import ApiException
|
||||
from google_nest_sdm.thermostat_traits import (
|
||||
@@ -28,14 +27,13 @@ from homeassistant.components.climate import (
|
||||
HVACAction,
|
||||
HVACMode,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import DATA_DEVICE_MANAGER, DOMAIN
|
||||
from .device_info import NestDeviceInfo
|
||||
from .types import NestConfigEntry
|
||||
|
||||
# Mapping for sdm.devices.traits.ThermostatMode mode field
|
||||
THERMOSTAT_MODE_MAP: dict[str, HVACMode] = {
|
||||
@@ -78,17 +76,13 @@ MIN_TEMP_RANGE = 1.66667
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||
hass: HomeAssistant, entry: NestConfigEntry, async_add_entities: AddEntitiesCallback
|
||||
) -> None:
|
||||
"""Set up the client entities."""
|
||||
|
||||
device_manager: DeviceManager = hass.data[DOMAIN][entry.entry_id][
|
||||
DATA_DEVICE_MANAGER
|
||||
]
|
||||
|
||||
async_add_entities(
|
||||
ThermostatEntity(device)
|
||||
for device in device_manager.devices.values()
|
||||
for device in entry.runtime_data.device_manager.devices.values()
|
||||
if ThermostatHvacTrait.NAME in device.traits
|
||||
)
|
||||
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
DOMAIN = "nest"
|
||||
DATA_SDM = "sdm"
|
||||
DATA_SUBSCRIBER = "subscriber"
|
||||
DATA_DEVICE_MANAGER = "device_manager"
|
||||
|
||||
WEB_AUTH_DOMAIN = DOMAIN
|
||||
INSTALLED_AUTH_DOMAIN = f"{DOMAIN}.installed"
|
||||
|
||||
@@ -7,11 +7,12 @@ from collections.abc import Mapping
|
||||
from google_nest_sdm.device import Device
|
||||
from google_nest_sdm.device_traits import ConnectivityTrait, InfoTrait
|
||||
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
|
||||
from .const import CONNECTIVITY_TRAIT_OFFLINE, DATA_DEVICE_MANAGER, DOMAIN
|
||||
from .const import CONNECTIVITY_TRAIT_OFFLINE, DOMAIN
|
||||
|
||||
DEVICE_TYPE_MAP: dict[str, str] = {
|
||||
"sdm.devices.types.CAMERA": "Camera",
|
||||
@@ -81,14 +82,12 @@ class NestDeviceInfo:
|
||||
@callback
|
||||
def async_nest_devices(hass: HomeAssistant) -> Mapping[str, Device]:
|
||||
"""Return a mapping of all nest devices for all config entries."""
|
||||
devices = {}
|
||||
for entry_id in hass.data[DOMAIN]:
|
||||
if not (device_manager := hass.data[DOMAIN][entry_id].get(DATA_DEVICE_MANAGER)):
|
||||
continue
|
||||
devices.update(
|
||||
{device.name: device for device in device_manager.devices.values()}
|
||||
)
|
||||
return devices
|
||||
return {
|
||||
device.name: device
|
||||
for config_entry in hass.config_entries.async_entries(DOMAIN)
|
||||
if config_entry.state == ConfigEntryState.LOADED
|
||||
for device in config_entry.runtime_data.device_manager.devices.values()
|
||||
}
|
||||
|
||||
|
||||
@callback
|
||||
|
||||
@@ -5,46 +5,26 @@ from __future__ import annotations
|
||||
from typing import Any
|
||||
|
||||
from google_nest_sdm import diagnostics
|
||||
from google_nest_sdm.device import Device
|
||||
from google_nest_sdm.device_manager import DeviceManager
|
||||
from google_nest_sdm.device_traits import InfoTrait
|
||||
|
||||
from homeassistant.components.camera import diagnostics as camera_diagnostics
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import DeviceEntry
|
||||
|
||||
from .const import DATA_DEVICE_MANAGER, DATA_SDM, DOMAIN
|
||||
from .types import NestConfigEntry
|
||||
|
||||
REDACT_DEVICE_TRAITS = {InfoTrait.NAME}
|
||||
|
||||
|
||||
@callback
|
||||
def _async_get_nest_devices(
|
||||
hass: HomeAssistant, config_entry: ConfigEntry
|
||||
) -> dict[str, Device]:
|
||||
"""Return dict of available devices."""
|
||||
if DATA_SDM not in config_entry.data:
|
||||
return {}
|
||||
|
||||
if (
|
||||
config_entry.entry_id not in hass.data[DOMAIN]
|
||||
or DATA_DEVICE_MANAGER not in hass.data[DOMAIN][config_entry.entry_id]
|
||||
):
|
||||
return {}
|
||||
|
||||
device_manager: DeviceManager = hass.data[DOMAIN][config_entry.entry_id][
|
||||
DATA_DEVICE_MANAGER
|
||||
]
|
||||
return device_manager.devices
|
||||
|
||||
|
||||
async def async_get_config_entry_diagnostics(
|
||||
hass: HomeAssistant, config_entry: ConfigEntry
|
||||
hass: HomeAssistant, config_entry: NestConfigEntry
|
||||
) -> dict[str, Any]:
|
||||
"""Return diagnostics for a config entry."""
|
||||
nest_devices = _async_get_nest_devices(hass, config_entry)
|
||||
if not nest_devices:
|
||||
if (
|
||||
not hasattr(config_entry, "runtime_data")
|
||||
or not config_entry.runtime_data
|
||||
or not (nest_devices := config_entry.runtime_data.device_manager.devices)
|
||||
):
|
||||
return {}
|
||||
data: dict[str, Any] = {
|
||||
**diagnostics.get_diagnostics(),
|
||||
@@ -62,11 +42,11 @@ async def async_get_config_entry_diagnostics(
|
||||
|
||||
async def async_get_device_diagnostics(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: NestConfigEntry,
|
||||
device: DeviceEntry,
|
||||
) -> dict[str, Any]:
|
||||
"""Return diagnostics for a device."""
|
||||
nest_devices = _async_get_nest_devices(hass, config_entry)
|
||||
nest_devices = config_entry.runtime_data.device_manager.devices
|
||||
nest_device_id = next(iter(device.identifiers))[1]
|
||||
nest_device = nest_devices.get(nest_device_id)
|
||||
return nest_device.get_diagnostics() if nest_device else {}
|
||||
|
||||
@@ -4,7 +4,6 @@ from dataclasses import dataclass
|
||||
import logging
|
||||
|
||||
from google_nest_sdm.device import Device
|
||||
from google_nest_sdm.device_manager import DeviceManager
|
||||
from google_nest_sdm.event import EventMessage, EventType
|
||||
from google_nest_sdm.traits import TraitType
|
||||
|
||||
@@ -13,11 +12,9 @@ from homeassistant.components.event import (
|
||||
EventEntity,
|
||||
EventEntityDescription,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import DATA_DEVICE_MANAGER, DOMAIN
|
||||
from .device_info import NestDeviceInfo
|
||||
from .events import (
|
||||
EVENT_CAMERA_MOTION,
|
||||
@@ -26,6 +23,7 @@ from .events import (
|
||||
EVENT_DOORBELL_CHIME,
|
||||
EVENT_NAME_MAP,
|
||||
)
|
||||
from .types import NestConfigEntry
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -68,16 +66,12 @@ ENTITY_DESCRIPTIONS = [
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||
hass: HomeAssistant, entry: NestConfigEntry, async_add_entities: AddEntitiesCallback
|
||||
) -> None:
|
||||
"""Set up the sensors."""
|
||||
|
||||
device_manager: DeviceManager = hass.data[DOMAIN][entry.entry_id][
|
||||
DATA_DEVICE_MANAGER
|
||||
]
|
||||
async_add_entities(
|
||||
NestTraitEventEntity(desc, device)
|
||||
for device in device_manager.devices.values()
|
||||
for device in entry.runtime_data.device_manager.devices.values()
|
||||
for desc in ENTITY_DESCRIPTIONS
|
||||
if any(trait in device.traits for trait in desc.trait_types)
|
||||
)
|
||||
|
||||
@@ -5,7 +5,6 @@ from __future__ import annotations
|
||||
import logging
|
||||
|
||||
from google_nest_sdm.device import Device
|
||||
from google_nest_sdm.device_manager import DeviceManager
|
||||
from google_nest_sdm.device_traits import HumidityTrait, TemperatureTrait
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
@@ -13,13 +12,12 @@ from homeassistant.components.sensor import (
|
||||
SensorEntity,
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import PERCENTAGE, UnitOfTemperature
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import DATA_DEVICE_MANAGER, DOMAIN
|
||||
from .device_info import NestDeviceInfo
|
||||
from .types import NestConfigEntry
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -33,15 +31,12 @@ DEVICE_TYPE_MAP = {
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||
hass: HomeAssistant, entry: NestConfigEntry, async_add_entities: AddEntitiesCallback
|
||||
) -> None:
|
||||
"""Set up the sensors."""
|
||||
|
||||
device_manager: DeviceManager = hass.data[DOMAIN][entry.entry_id][
|
||||
DATA_DEVICE_MANAGER
|
||||
]
|
||||
entities: list[SensorEntity] = []
|
||||
for device in device_manager.devices.values():
|
||||
for device in entry.runtime_data.device_manager.devices.values():
|
||||
if TemperatureTrait.NAME in device.traits:
|
||||
entities.append(TemperatureSensor(device))
|
||||
if HumidityTrait.NAME in device.traits:
|
||||
|
||||
19
homeassistant/components/nest/types.py
Normal file
19
homeassistant/components/nest/types.py
Normal file
@@ -0,0 +1,19 @@
|
||||
"""Type definitions for Nest."""
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
from google_nest_sdm.device_manager import DeviceManager
|
||||
from google_nest_sdm.google_nest_subscriber import GoogleNestSubscriber
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
|
||||
|
||||
@dataclass
|
||||
class NestData:
|
||||
"""Data for the Nest integration."""
|
||||
|
||||
subscriber: GoogleNestSubscriber
|
||||
device_manager: DeviceManager
|
||||
|
||||
|
||||
type NestConfigEntry = ConfigEntry[NestData]
|
||||
@@ -6,12 +6,20 @@
|
||||
"data": {
|
||||
"email": "[%key:common::config_flow::data::email%]",
|
||||
"password": "[%key:common::config_flow::data::password%]"
|
||||
},
|
||||
"data_description": {
|
||||
"email": "[%key:component::nice_go::config::step::user::data_description::email%]",
|
||||
"password": "[%key:component::nice_go::config::step::user::data_description::password%]"
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"email": "[%key:common::config_flow::data::email%]",
|
||||
"password": "[%key:common::config_flow::data::password%]"
|
||||
},
|
||||
"data_description": {
|
||||
"email": "The email address used to log in to the Nice G.O. app",
|
||||
"password": "The password used to log in to the Nice G.O. app"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from enum import StrEnum
|
||||
from functools import partial
|
||||
from typing import Final
|
||||
|
||||
import voluptuous as vol
|
||||
@@ -41,12 +40,6 @@ from homeassistant.const import (
|
||||
UnitOfVolumeFlowRate,
|
||||
UnitOfVolumetricFlux,
|
||||
)
|
||||
from homeassistant.helpers.deprecation import (
|
||||
DeprecatedConstantEnum,
|
||||
all_with_deprecated_constants,
|
||||
check_if_deprecated_constant,
|
||||
dir_with_deprecated_constants,
|
||||
)
|
||||
from homeassistant.util.unit_conversion import (
|
||||
BaseUnitConverter,
|
||||
TemperatureConverter,
|
||||
@@ -76,12 +69,6 @@ class NumberMode(StrEnum):
|
||||
SLIDER = "slider"
|
||||
|
||||
|
||||
# MODE_* are deprecated as of 2021.12, use the NumberMode enum instead.
|
||||
_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")
|
||||
|
||||
|
||||
class NumberDeviceClass(StrEnum):
|
||||
"""Device class for numbers."""
|
||||
|
||||
@@ -519,10 +506,3 @@ UNIT_CONVERTERS: dict[NumberDeviceClass, type[BaseUnitConverter]] = {
|
||||
NumberDeviceClass.TEMPERATURE: TemperatureConverter,
|
||||
NumberDeviceClass.VOLUME_FLOW_RATE: VolumeFlowRateConverter,
|
||||
}
|
||||
|
||||
# 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())
|
||||
|
||||
@@ -30,6 +30,7 @@ from homeassistant.exceptions import HomeAssistantError, TemplateError
|
||||
from homeassistant.helpers import device_registry as dr, intent, llm, template
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.util import ulid
|
||||
from homeassistant.util.json import JsonObjectType
|
||||
|
||||
from . import OpenAIConfigEntry
|
||||
from .const import (
|
||||
@@ -292,6 +293,8 @@ class OpenAIConversationEntity(
|
||||
if not tool_calls or not llm_api:
|
||||
break
|
||||
|
||||
aborting = False
|
||||
|
||||
for tool_call in tool_calls:
|
||||
tool_input = llm.ToolInput(
|
||||
tool_name=tool_call.function.name,
|
||||
@@ -301,12 +304,25 @@ class OpenAIConversationEntity(
|
||||
"Tool call: %s(%s)", tool_input.tool_name, tool_input.tool_args
|
||||
)
|
||||
|
||||
try:
|
||||
tool_response = await llm_api.async_call_tool(tool_input)
|
||||
except (HomeAssistantError, vol.Invalid) as e:
|
||||
tool_response = {"error": type(e).__name__}
|
||||
if str(e):
|
||||
tool_response["error_text"] = str(e)
|
||||
# OpenAI requires a tool response for every tool call in history
|
||||
if aborting:
|
||||
tool_response: JsonObjectType = {
|
||||
"error": "Aborted",
|
||||
"error_text": "Abort conversation requested",
|
||||
}
|
||||
if not aborting:
|
||||
try:
|
||||
tool_response = await llm_api.async_call_tool(tool_input)
|
||||
except llm.AbortConversation as e:
|
||||
aborting = True
|
||||
tool_response = {
|
||||
"error": "Aborted",
|
||||
"error_text": str(e) or "Abort conversation requested",
|
||||
}
|
||||
except (HomeAssistantError, vol.Invalid) as e:
|
||||
tool_response = {"error": type(e).__name__}
|
||||
if str(e):
|
||||
tool_response["error_text"] = str(e)
|
||||
|
||||
LOGGER.debug("Tool response: %s", tool_response)
|
||||
messages.append(
|
||||
@@ -317,6 +333,9 @@ class OpenAIConversationEntity(
|
||||
)
|
||||
)
|
||||
|
||||
if aborting:
|
||||
break
|
||||
|
||||
self.history[conversation_id] = messages
|
||||
|
||||
intent_response = intent.IntentResponse(language=user_input.language)
|
||||
|
||||
@@ -65,8 +65,6 @@ def _async_register_clientsession_shutdown(
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: RainbirdConfigEntry) -> bool:
|
||||
"""Set up the config entry for Rain Bird."""
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
|
||||
clientsession = async_create_clientsession()
|
||||
_async_register_clientsession_shutdown(hass, entry, clientsession)
|
||||
|
||||
|
||||
@@ -40,6 +40,9 @@
|
||||
"title": "[%key:component::rainbird::config::step::user::title%]",
|
||||
"data": {
|
||||
"duration": "Default irrigation time in minutes"
|
||||
},
|
||||
"data_description": {
|
||||
"duration": "The default duration the sprinkler will run when turned on."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1424,6 +1424,7 @@ class Recorder(threading.Thread):
|
||||
with session_scope(session=self.get_session()) as session:
|
||||
end_incomplete_runs(session, self.recorder_runs_manager.recording_start)
|
||||
self.recorder_runs_manager.start(session)
|
||||
self.states_manager.load_from_db(session)
|
||||
|
||||
self._open_event_session()
|
||||
|
||||
|
||||
@@ -22,9 +22,9 @@ from homeassistant.core import HomeAssistant, State, split_entity_id
|
||||
from homeassistant.helpers.recorder import get_instance
|
||||
import homeassistant.util.dt as dt_util
|
||||
|
||||
from ..db_schema import RecorderRuns, StateAttributes, States
|
||||
from ..db_schema import StateAttributes, States
|
||||
from ..filters import Filters
|
||||
from ..models import process_timestamp, process_timestamp_to_utc_isoformat
|
||||
from ..models import process_timestamp_to_utc_isoformat
|
||||
from ..models.legacy import LegacyLazyState, legacy_row_to_compressed_state
|
||||
from ..util import execute_stmt_lambda_element, session_scope
|
||||
from .const import (
|
||||
@@ -436,7 +436,7 @@ def get_last_state_changes(
|
||||
|
||||
|
||||
def _get_states_for_entities_stmt(
|
||||
run_start: datetime,
|
||||
run_start_ts: float,
|
||||
utc_point_in_time: datetime,
|
||||
entity_ids: list[str],
|
||||
no_attributes: bool,
|
||||
@@ -447,8 +447,7 @@ def _get_states_for_entities_stmt(
|
||||
)
|
||||
# We got an include-list of entities, accelerate the query by filtering already
|
||||
# in the inner query.
|
||||
run_start_ts = process_timestamp(run_start).timestamp()
|
||||
utc_point_in_time_ts = dt_util.utc_to_timestamp(utc_point_in_time)
|
||||
utc_point_in_time_ts = utc_point_in_time.timestamp()
|
||||
stmt += lambda q: q.join(
|
||||
(
|
||||
most_recent_states_for_entities_by_date := (
|
||||
@@ -483,7 +482,7 @@ def _get_rows_with_session(
|
||||
session: Session,
|
||||
utc_point_in_time: datetime,
|
||||
entity_ids: list[str],
|
||||
run: RecorderRuns | None = None,
|
||||
*,
|
||||
no_attributes: bool = False,
|
||||
) -> Iterable[Row]:
|
||||
"""Return the states at a specific point in time."""
|
||||
@@ -495,17 +494,16 @@ def _get_rows_with_session(
|
||||
),
|
||||
)
|
||||
|
||||
if run is None:
|
||||
run = get_instance(hass).recorder_runs_manager.get(utc_point_in_time)
|
||||
oldest_ts = get_instance(hass).states_manager.oldest_ts
|
||||
|
||||
if run is None or process_timestamp(run.start) > utc_point_in_time:
|
||||
# History did not run before utc_point_in_time
|
||||
if oldest_ts is None or oldest_ts > utc_point_in_time.timestamp():
|
||||
# We don't have any states for the requested time
|
||||
return []
|
||||
|
||||
# We have more than one entity to look at so we need to do a query on states
|
||||
# since the last recorder run started.
|
||||
stmt = _get_states_for_entities_stmt(
|
||||
run.start, utc_point_in_time, entity_ids, no_attributes
|
||||
oldest_ts, utc_point_in_time, entity_ids, no_attributes
|
||||
)
|
||||
return execute_stmt_lambda_element(session, stmt)
|
||||
|
||||
@@ -520,7 +518,7 @@ def _get_single_entity_states_stmt(
|
||||
stmt, join_attributes = _lambda_stmt_and_join_attributes(
|
||||
no_attributes, include_last_changed=True
|
||||
)
|
||||
utc_point_in_time_ts = dt_util.utc_to_timestamp(utc_point_in_time)
|
||||
utc_point_in_time_ts = utc_point_in_time.timestamp()
|
||||
stmt += (
|
||||
lambda q: q.filter(
|
||||
States.last_updated_ts < utc_point_in_time_ts,
|
||||
|
||||
@@ -34,7 +34,6 @@ from ..models import (
|
||||
LazyState,
|
||||
datetime_to_timestamp_or_none,
|
||||
extract_metadata_ids,
|
||||
process_timestamp,
|
||||
row_to_compressed_state,
|
||||
)
|
||||
from ..util import execute_stmt_lambda_element, session_scope
|
||||
@@ -246,12 +245,12 @@ def get_significant_states_with_session(
|
||||
if metadata_id is not None
|
||||
and split_entity_id(entity_id)[0] in SIGNIFICANT_DOMAINS
|
||||
]
|
||||
run_start_ts: float | None = None
|
||||
oldest_ts: float | None = None
|
||||
if include_start_time_state and not (
|
||||
run_start_ts := _get_run_start_ts_for_utc_point_in_time(hass, start_time)
|
||||
oldest_ts := _get_oldest_possible_ts(hass, start_time)
|
||||
):
|
||||
include_start_time_state = False
|
||||
start_time_ts = dt_util.utc_to_timestamp(start_time)
|
||||
start_time_ts = start_time.timestamp()
|
||||
end_time_ts = datetime_to_timestamp_or_none(end_time)
|
||||
single_metadata_id = metadata_ids[0] if len(metadata_ids) == 1 else None
|
||||
stmt = lambda_stmt(
|
||||
@@ -264,7 +263,7 @@ def get_significant_states_with_session(
|
||||
significant_changes_only,
|
||||
no_attributes,
|
||||
include_start_time_state,
|
||||
run_start_ts,
|
||||
oldest_ts,
|
||||
),
|
||||
track_on=[
|
||||
bool(single_metadata_id),
|
||||
@@ -411,12 +410,12 @@ def state_changes_during_period(
|
||||
entity_id_to_metadata_id: dict[str, int | None] = {
|
||||
entity_id: single_metadata_id
|
||||
}
|
||||
run_start_ts: float | None = None
|
||||
oldest_ts: float | None = None
|
||||
if include_start_time_state and not (
|
||||
run_start_ts := _get_run_start_ts_for_utc_point_in_time(hass, start_time)
|
||||
oldest_ts := _get_oldest_possible_ts(hass, start_time)
|
||||
):
|
||||
include_start_time_state = False
|
||||
start_time_ts = dt_util.utc_to_timestamp(start_time)
|
||||
start_time_ts = start_time.timestamp()
|
||||
end_time_ts = datetime_to_timestamp_or_none(end_time)
|
||||
stmt = lambda_stmt(
|
||||
lambda: _state_changed_during_period_stmt(
|
||||
@@ -426,7 +425,7 @@ def state_changes_during_period(
|
||||
no_attributes,
|
||||
limit,
|
||||
include_start_time_state,
|
||||
run_start_ts,
|
||||
oldest_ts,
|
||||
has_last_reported,
|
||||
),
|
||||
track_on=[
|
||||
@@ -600,17 +599,17 @@ def _get_start_time_state_for_entities_stmt(
|
||||
)
|
||||
|
||||
|
||||
def _get_run_start_ts_for_utc_point_in_time(
|
||||
def _get_oldest_possible_ts(
|
||||
hass: HomeAssistant, utc_point_in_time: datetime
|
||||
) -> float | None:
|
||||
"""Return the start time of a run."""
|
||||
run = get_instance(hass).recorder_runs_manager.get(utc_point_in_time)
|
||||
if (
|
||||
run is not None
|
||||
and (run_start := process_timestamp(run.start)) < utc_point_in_time
|
||||
):
|
||||
return run_start.timestamp()
|
||||
# History did not run before utc_point_in_time but we still
|
||||
"""Return the oldest possible timestamp.
|
||||
|
||||
Returns None if there are no states as old as utc_point_in_time.
|
||||
"""
|
||||
|
||||
oldest_ts = get_instance(hass).states_manager.oldest_ts
|
||||
if oldest_ts is not None and oldest_ts < utc_point_in_time.timestamp():
|
||||
return oldest_ts
|
||||
return None
|
||||
|
||||
|
||||
|
||||
@@ -2742,7 +2742,10 @@ class EventIDPostMigration(BaseRunTimeMigration):
|
||||
|
||||
|
||||
class EntityIDPostMigration(BaseMigrationWithQuery, BaseRunTimeMigration):
|
||||
"""Migration to remove old entity_id strings from states."""
|
||||
"""Migration to remove old entity_id strings from states.
|
||||
|
||||
Introduced in HA Core 2023.4 by PR #89557.
|
||||
"""
|
||||
|
||||
migration_id = "entity_id_post_migration"
|
||||
task = MigrationTask
|
||||
@@ -2764,9 +2767,9 @@ NON_LIVE_DATA_MIGRATORS = (
|
||||
)
|
||||
|
||||
LIVE_DATA_MIGRATORS = (
|
||||
EventTypeIDMigration,
|
||||
EntityIDMigration,
|
||||
EventIDPostMigration,
|
||||
EventTypeIDMigration, # Introduced in HA Core 2023.4 by PR #89465
|
||||
EntityIDMigration, # Introduced in HA Core 2023.4 by PR #89557
|
||||
EventIDPostMigration, # Introduced in HA Core 2023.4 by PR #89901
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ class LegacyLazyState(State):
|
||||
self.state = self._row.state or ""
|
||||
self._attributes: dict[str, Any] | None = None
|
||||
self._last_updated_ts: float | None = self._row.last_updated_ts or (
|
||||
dt_util.utc_to_timestamp(start_time) if start_time else None
|
||||
start_time.timestamp() if start_time else None
|
||||
)
|
||||
self._last_changed_ts: float | None = (
|
||||
self._row.last_changed_ts or self._last_updated_ts
|
||||
@@ -146,7 +146,7 @@ def legacy_row_to_compressed_state(
|
||||
COMPRESSED_STATE_ATTRIBUTES: decode_attributes_from_row_legacy(row, attr_cache),
|
||||
}
|
||||
if start_time:
|
||||
comp_state[COMPRESSED_STATE_LAST_UPDATED] = dt_util.utc_to_timestamp(start_time)
|
||||
comp_state[COMPRESSED_STATE_LAST_UPDATED] = start_time.timestamp()
|
||||
else:
|
||||
row_last_updated_ts: float = row.last_updated_ts
|
||||
comp_state[COMPRESSED_STATE_LAST_UPDATED] = row_last_updated_ts
|
||||
|
||||
@@ -123,6 +123,9 @@ def purge_old_data(
|
||||
_purge_old_entity_ids(instance, session)
|
||||
|
||||
_purge_old_recorder_runs(instance, session, purge_before)
|
||||
with session_scope(session=instance.get_session(), read_only=True) as session:
|
||||
instance.recorder_runs_manager.load_from_db(session)
|
||||
instance.states_manager.load_from_db(session)
|
||||
if repack:
|
||||
repack_database(instance)
|
||||
return True
|
||||
|
||||
@@ -637,6 +637,15 @@ def find_states_to_purge(
|
||||
)
|
||||
|
||||
|
||||
def find_oldest_state() -> StatementLambdaElement:
|
||||
"""Find the last_updated_ts of the oldest state."""
|
||||
return lambda_stmt(
|
||||
lambda: select(States.last_updated_ts).where(
|
||||
States.state_id.in_(select(func.min(States.state_id)))
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def find_short_term_statistics_to_purge(
|
||||
purge_before: datetime, max_bind_vars: int
|
||||
) -> StatementLambdaElement:
|
||||
|
||||
@@ -2,7 +2,15 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Sequence
|
||||
from typing import Any, cast
|
||||
|
||||
from sqlalchemy.engine.row import Row
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
from ..db_schema import States
|
||||
from ..queries import find_oldest_state
|
||||
from ..util import execute_stmt_lambda_element
|
||||
|
||||
|
||||
class StatesManager:
|
||||
@@ -13,6 +21,12 @@ class StatesManager:
|
||||
self._pending: dict[str, States] = {}
|
||||
self._last_committed_id: dict[str, int] = {}
|
||||
self._last_reported: dict[int, float] = {}
|
||||
self._oldest_ts: float | None = None
|
||||
|
||||
@property
|
||||
def oldest_ts(self) -> float | None:
|
||||
"""Return the oldest timestamp."""
|
||||
return self._oldest_ts
|
||||
|
||||
def pop_pending(self, entity_id: str) -> States | None:
|
||||
"""Pop a pending state.
|
||||
@@ -44,6 +58,8 @@ class StatesManager:
|
||||
recorder thread.
|
||||
"""
|
||||
self._pending[entity_id] = state
|
||||
if self._oldest_ts is None:
|
||||
self._oldest_ts = state.last_updated_ts
|
||||
|
||||
def update_pending_last_reported(
|
||||
self, state_id: int, last_reported_timestamp: float
|
||||
@@ -74,6 +90,22 @@ class StatesManager:
|
||||
"""
|
||||
self._last_committed_id.clear()
|
||||
self._pending.clear()
|
||||
self._oldest_ts = None
|
||||
|
||||
def load_from_db(self, session: Session) -> None:
|
||||
"""Update the cache.
|
||||
|
||||
Must run in the recorder thread.
|
||||
"""
|
||||
result = cast(
|
||||
Sequence[Row[Any]],
|
||||
execute_stmt_lambda_element(session, find_oldest_state()),
|
||||
)
|
||||
if not result:
|
||||
ts = None
|
||||
else:
|
||||
ts = result[0].last_updated_ts
|
||||
self._oldest_ts = ts
|
||||
|
||||
def evict_purged_state_ids(self, purged_state_ids: set[int]) -> None:
|
||||
"""Evict purged states from the committed states.
|
||||
|
||||
@@ -120,8 +120,6 @@ class PurgeTask(RecorderTask):
|
||||
if purge.purge_old_data(
|
||||
instance, self.purge_before, self.repack, self.apply_filter
|
||||
):
|
||||
with instance.get_session() as session:
|
||||
instance.recorder_runs_manager.load_from_db(session)
|
||||
# We always need to do the db cleanups after a purge
|
||||
# is finished to ensure the WAL checkpoint and other
|
||||
# tasks happen after a vacuum.
|
||||
|
||||
@@ -22,12 +22,6 @@ from homeassistant.const import (
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.deprecation import (
|
||||
DeprecatedConstantEnum,
|
||||
all_with_deprecated_constants,
|
||||
check_if_deprecated_constant,
|
||||
dir_with_deprecated_constants,
|
||||
)
|
||||
from homeassistant.helpers.entity import ToggleEntity, ToggleEntityDescription
|
||||
from homeassistant.helpers.entity_component import EntityComponent
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
@@ -74,19 +68,6 @@ class RemoteEntityFeature(IntFlag):
|
||||
ACTIVITY = 4
|
||||
|
||||
|
||||
# These SUPPORT_* constants are deprecated as of Home Assistant 2022.5.
|
||||
# Please use the RemoteEntityFeature enum instead.
|
||||
_DEPRECATED_SUPPORT_LEARN_COMMAND = DeprecatedConstantEnum(
|
||||
RemoteEntityFeature.LEARN_COMMAND, "2025.1"
|
||||
)
|
||||
_DEPRECATED_SUPPORT_DELETE_COMMAND = DeprecatedConstantEnum(
|
||||
RemoteEntityFeature.DELETE_COMMAND, "2025.1"
|
||||
)
|
||||
_DEPRECATED_SUPPORT_ACTIVITY = DeprecatedConstantEnum(
|
||||
RemoteEntityFeature.ACTIVITY, "2025.1"
|
||||
)
|
||||
|
||||
|
||||
REMOTE_SERVICE_ACTIVITY_SCHEMA = cv.make_entity_service_schema(
|
||||
{vol.Optional(ATTR_ACTIVITY): cv.string}
|
||||
)
|
||||
@@ -251,11 +232,3 @@ 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())
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
"requirements": [
|
||||
"getmac==0.9.4",
|
||||
"samsungctl[websocket]==0.7.1",
|
||||
"samsungtvws[async,encrypted]==2.7.0",
|
||||
"samsungtvws[async,encrypted]==2.7.1",
|
||||
"wakeonlan==2.1.0",
|
||||
"async-upnp-client==0.41.0"
|
||||
],
|
||||
|
||||
@@ -10,7 +10,6 @@ from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SchlageDataUpdateCoordinator
|
||||
|
||||
PLATFORMS: list[Platform] = [
|
||||
@@ -21,8 +20,10 @@ PLATFORMS: list[Platform] = [
|
||||
Platform.SWITCH,
|
||||
]
|
||||
|
||||
type SchlageConfigEntry = ConfigEntry[SchlageDataUpdateCoordinator]
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: SchlageConfigEntry) -> bool:
|
||||
"""Set up Schlage from a config entry."""
|
||||
username = entry.data[CONF_USERNAME]
|
||||
password = entry.data[CONF_PASSWORD]
|
||||
@@ -32,15 +33,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
raise ConfigEntryAuthFailed from ex
|
||||
|
||||
coordinator = SchlageDataUpdateCoordinator(hass, username, pyschlage.Schlage(auth))
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator
|
||||
entry.runtime_data = coordinator
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: SchlageConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
@@ -10,12 +10,11 @@ from homeassistant.components.binary_sensor import (
|
||||
BinarySensorEntity,
|
||||
BinarySensorEntityDescription,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import EntityCategory
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from . import SchlageConfigEntry
|
||||
from .coordinator import LockData, SchlageDataUpdateCoordinator
|
||||
from .entity import SchlageEntity
|
||||
|
||||
@@ -40,11 +39,11 @@ _DESCRIPTIONS: tuple[SchlageBinarySensorEntityDescription] = (
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: SchlageConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up binary_sensors based on a config entry."""
|
||||
coordinator: SchlageDataUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id]
|
||||
coordinator = config_entry.runtime_data
|
||||
|
||||
def _add_new_locks(locks: dict[str, LockData]) -> None:
|
||||
async_add_entities(
|
||||
|
||||
@@ -44,6 +44,7 @@ class SchlageDataUpdateCoordinator(DataUpdateCoordinator[SchlageData]):
|
||||
super().__init__(
|
||||
hass, LOGGER, name=f"{DOMAIN} ({username})", update_interval=UPDATE_INTERVAL
|
||||
)
|
||||
self.data = SchlageData(locks={})
|
||||
self.api = api
|
||||
self.new_locks_callbacks: list[Callable[[dict[str, LockData]], None]] = []
|
||||
self.async_add_listener(self._add_remove_locks)
|
||||
@@ -55,7 +56,9 @@ class SchlageDataUpdateCoordinator(DataUpdateCoordinator[SchlageData]):
|
||||
except NotAuthorizedError as ex:
|
||||
raise ConfigEntryAuthFailed from ex
|
||||
except SchlageError as ex:
|
||||
raise UpdateFailed("Failed to refresh Schlage data") from ex
|
||||
raise UpdateFailed(
|
||||
translation_domain=DOMAIN, translation_key="schlage_refresh_failed"
|
||||
) from ex
|
||||
lock_data = await asyncio.gather(
|
||||
*(
|
||||
self.hass.async_add_executor_job(self._get_lock_data, lock)
|
||||
@@ -83,9 +86,6 @@ class SchlageDataUpdateCoordinator(DataUpdateCoordinator[SchlageData]):
|
||||
@callback
|
||||
def _add_remove_locks(self) -> None:
|
||||
"""Add newly discovered locks and remove nonexistent locks."""
|
||||
if self.data is None:
|
||||
return
|
||||
|
||||
device_registry = dr.async_get(self.hass)
|
||||
devices = dr.async_entries_for_config_entry(
|
||||
device_registry, self.config_entry.entry_id
|
||||
|
||||
@@ -4,19 +4,17 @@ from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SchlageDataUpdateCoordinator
|
||||
from . import SchlageConfigEntry
|
||||
|
||||
|
||||
async def async_get_config_entry_diagnostics(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: SchlageConfigEntry,
|
||||
) -> dict[str, Any]:
|
||||
"""Return diagnostics for a config entry."""
|
||||
coordinator: SchlageDataUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id]
|
||||
coordinator = config_entry.runtime_data
|
||||
# NOTE: Schlage diagnostics are already redacted.
|
||||
return {
|
||||
"locks": [ld.lock.get_diagnostics() for ld in coordinator.data.locks.values()]
|
||||
|
||||
@@ -5,22 +5,21 @@ from __future__ import annotations
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.components.lock import LockEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from . import SchlageConfigEntry
|
||||
from .coordinator import LockData, SchlageDataUpdateCoordinator
|
||||
from .entity import SchlageEntity
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: SchlageConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Schlage WiFi locks based on a config entry."""
|
||||
coordinator: SchlageDataUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id]
|
||||
coordinator = config_entry.runtime_data
|
||||
|
||||
def _add_new_locks(locks: dict[str, LockData]) -> None:
|
||||
async_add_entities(
|
||||
|
||||
@@ -3,12 +3,11 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from homeassistant.components.select import SelectEntity, SelectEntityDescription
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import EntityCategory
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from . import SchlageConfigEntry
|
||||
from .coordinator import LockData, SchlageDataUpdateCoordinator
|
||||
from .entity import SchlageEntity
|
||||
|
||||
@@ -33,11 +32,11 @@ _DESCRIPTIONS = (
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: SchlageConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up selects based on a config entry."""
|
||||
coordinator: SchlageDataUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id]
|
||||
coordinator = config_entry.runtime_data
|
||||
|
||||
def _add_new_locks(locks: dict[str, LockData]) -> None:
|
||||
async_add_entities(
|
||||
|
||||
@@ -13,7 +13,6 @@ from homeassistant.const import PERCENTAGE, EntityCategory
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import LockData, SchlageDataUpdateCoordinator
|
||||
from .entity import SchlageEntity
|
||||
|
||||
@@ -34,7 +33,7 @@ async def async_setup_entry(
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up sensors based on a config entry."""
|
||||
coordinator: SchlageDataUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id]
|
||||
coordinator = config_entry.runtime_data
|
||||
|
||||
def _add_new_locks(locks: dict[str, LockData]) -> None:
|
||||
async_add_entities(
|
||||
|
||||
@@ -53,5 +53,10 @@
|
||||
"name": "1-Touch Locking"
|
||||
}
|
||||
}
|
||||
},
|
||||
"exceptions": {
|
||||
"schlage_refresh_failed": {
|
||||
"message": "Failed to refresh Schlage data"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ from homeassistant.const import EntityCategory
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import LockData, SchlageDataUpdateCoordinator
|
||||
from .entity import SchlageEntity
|
||||
|
||||
@@ -61,7 +60,7 @@ async def async_setup_entry(
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up switches based on a config entry."""
|
||||
coordinator: SchlageDataUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id]
|
||||
coordinator = config_entry.runtime_data
|
||||
|
||||
def _add_new_locks(locks: dict[str, LockData]) -> None:
|
||||
async_add_entities(
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user