mirror of
https://github.com/home-assistant/core.git
synced 2026-01-12 02:27:18 +01:00
Compare commits
151 Commits
test-new-t
...
2023.7.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a141bc08e7 | ||
|
|
a982958143 | ||
|
|
2822d98544 | ||
|
|
4f95039dfd | ||
|
|
658e87b6a5 | ||
|
|
e6c8e0460f | ||
|
|
c78628aa29 | ||
|
|
3dc1ceed0b | ||
|
|
89e737facb | ||
|
|
15ab483f61 | ||
|
|
0b3ff859e6 | ||
|
|
bdcc9ec984 | ||
|
|
382bfa24a8 | ||
|
|
6a54c18818 | ||
|
|
76ac7fa6a0 | ||
|
|
de1a367cff | ||
|
|
e493cd642c | ||
|
|
c2a41fc21e | ||
|
|
ebc123c355 | ||
|
|
4874e13af8 | ||
|
|
9969f67508 | ||
|
|
855962d729 | ||
|
|
98b8e27b08 | ||
|
|
b26e624b2b | ||
|
|
3c7ced21ad | ||
|
|
eec05d4fc8 | ||
|
|
32927e050f | ||
|
|
c6b7a3d564 | ||
|
|
6f5373fa6e | ||
|
|
7bd9933092 | ||
|
|
52f60f1e48 | ||
|
|
3667eb9400 | ||
|
|
80b24b23d3 | ||
|
|
816adce257 | ||
|
|
d18716e5f8 | ||
|
|
4096614ac0 | ||
|
|
ef31608ce0 | ||
|
|
7408fa4ab6 | ||
|
|
10b97a77c6 | ||
|
|
3540c78fb9 | ||
|
|
866e130967 | ||
|
|
d969b89a12 | ||
|
|
bca5aae3bb | ||
|
|
7a21e858ab | ||
|
|
224886eb29 | ||
|
|
95594a23dc | ||
|
|
4c10d186c0 | ||
|
|
6275932c29 | ||
|
|
4229778cf6 | ||
|
|
57369be322 | ||
|
|
3e19fba7d3 | ||
|
|
2196bd3a13 | ||
|
|
91ea14d9eb | ||
|
|
b90df4bdca | ||
|
|
025969a76f | ||
|
|
8ed7390a61 | ||
|
|
36f6c02c52 | ||
|
|
a709b6af4c | ||
|
|
284926159b | ||
|
|
8e2c0984b7 | ||
|
|
0bb0e3fe72 | ||
|
|
c123c22846 | ||
|
|
577ffef25c | ||
|
|
10a9b063fa | ||
|
|
e22b79dbd9 | ||
|
|
89c580f27d | ||
|
|
239cbb7ad1 | ||
|
|
d8f8557ae1 | ||
|
|
3df1e9bc99 | ||
|
|
8db987e0b3 | ||
|
|
d77a168121 | ||
|
|
3550a9e2d2 | ||
|
|
0ce3b89139 | ||
|
|
3c0d234332 | ||
|
|
601ca8562c | ||
|
|
2720e2fade | ||
|
|
d0e60f66fa | ||
|
|
806b0cb1b6 | ||
|
|
2e18641863 | ||
|
|
e4e5ecf9b4 | ||
|
|
1e66aaff0d | ||
|
|
ebb28973e0 | ||
|
|
c260e787aa | ||
|
|
fdd8489b93 | ||
|
|
a77fb14baf | ||
|
|
98242b5d54 | ||
|
|
d38aab1607 | ||
|
|
910aecb33b | ||
|
|
ca20663c31 | ||
|
|
327a54e65a | ||
|
|
e907585b7f | ||
|
|
3fa009b98c | ||
|
|
226f1d7c73 | ||
|
|
be49f90550 | ||
|
|
4993b6ee90 | ||
|
|
2de0b0f6ac | ||
|
|
313e15a915 | ||
|
|
11fd9ffa60 | ||
|
|
4192fdbdfd | ||
|
|
d1e8513d63 | ||
|
|
9a14d437dd | ||
|
|
19cbedcc05 | ||
|
|
8a6d54237f | ||
|
|
70d3dc2ef8 | ||
|
|
a8807f600b | ||
|
|
73cb17cbf5 | ||
|
|
26d171fc92 | ||
|
|
a4449d39f3 | ||
|
|
a9b8e0077d | ||
|
|
97d9876cf7 | ||
|
|
0e89d0a26b | ||
|
|
03e7170080 | ||
|
|
c67f37d1be | ||
|
|
6aba1a5af3 | ||
|
|
1cf472f4e3 | ||
|
|
b8fc6e0c66 | ||
|
|
a04c98a703 | ||
|
|
4255a2af15 | ||
|
|
c47a43c2b9 | ||
|
|
bcca1c91e6 | ||
|
|
9cd7034dbd | ||
|
|
ee72a952de | ||
|
|
22e32bc737 | ||
|
|
2a42622de9 | ||
|
|
8f88b710f0 | ||
|
|
1df12e8771 | ||
|
|
129fee09d3 | ||
|
|
61ab84bf04 | ||
|
|
4f99a71f61 | ||
|
|
f165357772 | ||
|
|
c156951925 | ||
|
|
612f33e372 | ||
|
|
2b274b4e95 | ||
|
|
2ac5bb46b1 | ||
|
|
c4a46294cc | ||
|
|
cda5ee5814 | ||
|
|
698333f894 | ||
|
|
b93ceca804 | ||
|
|
286cff314a | ||
|
|
d44ef07082 | ||
|
|
8dd2e21d0b | ||
|
|
f55ada5d61 | ||
|
|
1f72a5b1fe | ||
|
|
71b192c072 | ||
|
|
17c64ed791 | ||
|
|
4e940df224 | ||
|
|
a23c0e12f1 | ||
|
|
be22195cf4 | ||
|
|
a39ef03ff5 | ||
|
|
1687ff1f28 | ||
|
|
28a5e4735f |
@@ -182,7 +182,6 @@ omit =
|
||||
homeassistant/components/crownstone/listeners.py
|
||||
homeassistant/components/cups/sensor.py
|
||||
homeassistant/components/currencylayer/sensor.py
|
||||
homeassistant/components/daikin/__init__.py
|
||||
homeassistant/components/daikin/climate.py
|
||||
homeassistant/components/daikin/sensor.py
|
||||
homeassistant/components/daikin/switch.py
|
||||
|
||||
@@ -34,6 +34,7 @@ class AbodeAlarm(AbodeDevice, alarm.AlarmControlPanelEntity):
|
||||
"""An alarm_control_panel implementation for Abode."""
|
||||
|
||||
_attr_icon = ICON
|
||||
_attr_name = None
|
||||
_attr_code_arm_required = False
|
||||
_attr_supported_features = (
|
||||
AlarmControlPanelEntityFeature.ARM_HOME
|
||||
|
||||
@@ -39,6 +39,7 @@ class AbodeCamera(AbodeDevice, Camera):
|
||||
"""Representation of an Abode camera."""
|
||||
|
||||
_device: AbodeCam
|
||||
_attr_name = None
|
||||
|
||||
def __init__(self, data: AbodeSystem, device: AbodeDev, event: Event) -> None:
|
||||
"""Initialize the Abode device."""
|
||||
|
||||
@@ -29,6 +29,7 @@ class AbodeCover(AbodeDevice, CoverEntity):
|
||||
"""Representation of an Abode cover."""
|
||||
|
||||
_device: AbodeCV
|
||||
_attr_name = None
|
||||
|
||||
@property
|
||||
def is_closed(self) -> bool:
|
||||
|
||||
@@ -42,6 +42,7 @@ class AbodeLight(AbodeDevice, LightEntity):
|
||||
"""Representation of an Abode light."""
|
||||
|
||||
_device: AbodeLT
|
||||
_attr_name = None
|
||||
|
||||
def turn_on(self, **kwargs: Any) -> None:
|
||||
"""Turn on the light."""
|
||||
|
||||
@@ -29,6 +29,7 @@ class AbodeLock(AbodeDevice, LockEntity):
|
||||
"""Representation of an Abode lock."""
|
||||
|
||||
_device: AbodeLK
|
||||
_attr_name = None
|
||||
|
||||
def lock(self, **kwargs: Any) -> None:
|
||||
"""Lock the device."""
|
||||
|
||||
@@ -53,7 +53,6 @@ class AbodeSensor(AbodeDevice, SensorEntity):
|
||||
"""A sensor implementation for Abode devices."""
|
||||
|
||||
_device: AbodeSense
|
||||
_attr_has_entity_name = True
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
||||
@@ -44,6 +44,7 @@ class AbodeSwitch(AbodeDevice, SwitchEntity):
|
||||
"""Representation of an Abode switch."""
|
||||
|
||||
_device: AbodeSW
|
||||
_attr_name = None
|
||||
|
||||
def turn_on(self, **kwargs: Any) -> None:
|
||||
"""Turn on the device."""
|
||||
|
||||
@@ -90,6 +90,7 @@ class AdvantageAirAC(AdvantageAirAcEntity, ClimateEntity):
|
||||
_attr_target_temperature_step = PRECISION_WHOLE
|
||||
_attr_max_temp = 32
|
||||
_attr_min_temp = 16
|
||||
_attr_name = None
|
||||
|
||||
_attr_hvac_modes = [
|
||||
HVACMode.OFF,
|
||||
|
||||
@@ -9,17 +9,30 @@ from homeassistant.core import HomeAssistant
|
||||
|
||||
from .const import DOMAIN as ADVANTAGE_AIR_DOMAIN
|
||||
|
||||
TO_REDACT = ["dealerPhoneNumber", "latitude", "logoPIN", "longitude", "postCode"]
|
||||
TO_REDACT = [
|
||||
"dealerPhoneNumber",
|
||||
"latitude",
|
||||
"logoPIN",
|
||||
"longitude",
|
||||
"postCode",
|
||||
"rid",
|
||||
"deviceNames",
|
||||
"deviceIds",
|
||||
"deviceIdsV2",
|
||||
"backupId",
|
||||
]
|
||||
|
||||
|
||||
async def async_get_config_entry_diagnostics(
|
||||
hass: HomeAssistant, config_entry: ConfigEntry
|
||||
) -> dict[str, Any]:
|
||||
"""Return diagnostics for a config entry."""
|
||||
data = hass.data[ADVANTAGE_AIR_DOMAIN][config_entry.entry_id]["coordinator"].data
|
||||
data = hass.data[ADVANTAGE_AIR_DOMAIN][config_entry.entry_id].coordinator.data
|
||||
|
||||
# Return only the relevant children
|
||||
return {
|
||||
"aircons": data["aircons"],
|
||||
"aircons": data.get("aircons"),
|
||||
"myLights": data.get("myLights"),
|
||||
"myThings": data.get("myThings"),
|
||||
"system": async_redact_data(data["system"], TO_REDACT),
|
||||
}
|
||||
|
||||
@@ -84,6 +84,8 @@ class AdvantageAirZoneEntity(AdvantageAirAcEntity):
|
||||
class AdvantageAirThingEntity(AdvantageAirEntity):
|
||||
"""Parent class for Advantage Air Things Entities."""
|
||||
|
||||
_attr_name = None
|
||||
|
||||
def __init__(self, instance: AdvantageAirData, thing: dict[str, Any]) -> None:
|
||||
"""Initialize common aspects of an Advantage Air Things entity."""
|
||||
super().__init__(instance)
|
||||
|
||||
@@ -41,6 +41,7 @@ class AdvantageAirLight(AdvantageAirEntity, LightEntity):
|
||||
"""Representation of Advantage Air Light."""
|
||||
|
||||
_attr_supported_color_modes = {ColorMode.ONOFF}
|
||||
_attr_name = None
|
||||
|
||||
def __init__(self, instance: AdvantageAirData, light: dict[str, Any]) -> None:
|
||||
"""Initialize an Advantage Air Light."""
|
||||
|
||||
@@ -16,6 +16,7 @@ from .const import DOMAIN
|
||||
class AndroidTVRemoteBaseEntity(Entity):
|
||||
"""Android TV Remote Base Entity."""
|
||||
|
||||
_attr_name = None
|
||||
_attr_has_entity_name = True
|
||||
_attr_should_poll = False
|
||||
|
||||
|
||||
@@ -80,6 +80,7 @@ class AnthemAVR(MediaPlayerEntity):
|
||||
self._attr_name = f"zone {zone_number}"
|
||||
self._attr_unique_id = f"{mac_address}_{zone_number}"
|
||||
else:
|
||||
self._attr_name = None
|
||||
self._attr_unique_id = mac_address
|
||||
|
||||
self._attr_device_info = DeviceInfo(
|
||||
|
||||
@@ -16,7 +16,24 @@
|
||||
"_touch-able._tcp.local.",
|
||||
"_appletv-v2._tcp.local.",
|
||||
"_hscp._tcp.local.",
|
||||
"_airplay._tcp.local.",
|
||||
{
|
||||
"type": "_airplay._tcp.local.",
|
||||
"properties": {
|
||||
"model": "appletv*"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "_airplay._tcp.local.",
|
||||
"properties": {
|
||||
"model": "audioaccessory*"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "_airplay._tcp.local.",
|
||||
"properties": {
|
||||
"am": "airport*"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "_raop._tcp.local.",
|
||||
"properties": {
|
||||
|
||||
@@ -112,8 +112,8 @@ ENTITY_TRIGGERS = {
|
||||
{CONF_TYPE: CONF_NO_LIGHT},
|
||||
],
|
||||
BinarySensorDeviceClass.LOCK: [
|
||||
{CONF_TYPE: CONF_LOCKED},
|
||||
{CONF_TYPE: CONF_NOT_LOCKED},
|
||||
{CONF_TYPE: CONF_LOCKED},
|
||||
],
|
||||
BinarySensorDeviceClass.MOISTURE: [
|
||||
{CONF_TYPE: CONF_MOIST},
|
||||
|
||||
@@ -42,6 +42,7 @@ class BlinkSyncModule(AlarmControlPanelEntity):
|
||||
_attr_icon = ICON
|
||||
_attr_supported_features = AlarmControlPanelEntityFeature.ARM_AWAY
|
||||
_attr_name = None
|
||||
_attr_has_entity_name = True
|
||||
|
||||
def __init__(self, data, name, sync):
|
||||
"""Initialize the alarm control panel."""
|
||||
|
||||
@@ -58,13 +58,14 @@ async def async_setup_entry(
|
||||
class BlinkBinarySensor(BinarySensorEntity):
|
||||
"""Representation of a Blink binary sensor."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
|
||||
def __init__(
|
||||
self, data, camera, description: BinarySensorEntityDescription
|
||||
) -> None:
|
||||
"""Initialize the sensor."""
|
||||
self.data = data
|
||||
self.entity_description = description
|
||||
self._attr_name = f"{DOMAIN} {camera} {description.name}"
|
||||
self._camera = data.cameras[camera]
|
||||
self._attr_unique_id = f"{self._camera.serial}-{description.key}"
|
||||
self._attr_device_info = DeviceInfo(
|
||||
|
||||
@@ -38,6 +38,7 @@ async def async_setup_entry(
|
||||
class BlinkCamera(Camera):
|
||||
"""An implementation of a Blink Camera."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
_attr_name = None
|
||||
|
||||
def __init__(self, data, name, camera):
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"bleak-retry-connector==3.0.2",
|
||||
"bluetooth-adapters==0.15.3",
|
||||
"bluetooth-auto-recovery==1.2.0",
|
||||
"bluetooth-data-tools==1.2.0",
|
||||
"bluetooth-data-tools==1.3.0",
|
||||
"dbus-fast==1.86.0"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -21,3 +21,9 @@ UNIT_MAP = {
|
||||
"LITERS": UnitOfVolume.LITERS,
|
||||
"GALLONS": UnitOfVolume.GALLONS,
|
||||
}
|
||||
|
||||
SCAN_INTERVALS = {
|
||||
"china": 300,
|
||||
"north_america": 600,
|
||||
"rest_of_world": 300,
|
||||
}
|
||||
|
||||
@@ -15,10 +15,8 @@ from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||
|
||||
from .const import CONF_GCID, CONF_READ_ONLY, CONF_REFRESH_TOKEN, DOMAIN
|
||||
from .const import CONF_GCID, CONF_READ_ONLY, CONF_REFRESH_TOKEN, DOMAIN, SCAN_INTERVALS
|
||||
|
||||
DEFAULT_SCAN_INTERVAL_SECONDS = 300
|
||||
SCAN_INTERVAL = timedelta(seconds=DEFAULT_SCAN_INTERVAL_SECONDS)
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -50,7 +48,7 @@ class BMWDataUpdateCoordinator(DataUpdateCoordinator[None]):
|
||||
hass,
|
||||
_LOGGER,
|
||||
name=f"{DOMAIN}-{entry.data['username']}",
|
||||
update_interval=SCAN_INTERVAL,
|
||||
update_interval=timedelta(seconds=SCAN_INTERVALS[entry.data[CONF_REGION]]),
|
||||
)
|
||||
|
||||
async def _async_update_data(self) -> None:
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/bmw_connected_drive",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["bimmer_connected"],
|
||||
"requirements": ["bimmer-connected==0.13.7"]
|
||||
"requirements": ["bimmer-connected==0.13.8"]
|
||||
}
|
||||
|
||||
@@ -107,6 +107,7 @@ class BroadlinkRemote(BroadlinkEntity, RemoteEntity, RestoreEntity):
|
||||
"""Representation of a Broadlink remote."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
_attr_name = None
|
||||
|
||||
def __init__(self, device, codes, flags):
|
||||
"""Initialize the remote."""
|
||||
|
||||
@@ -221,6 +221,7 @@ class BroadlinkSP2Switch(BroadlinkSP1Switch):
|
||||
|
||||
_attr_assumed_state = False
|
||||
_attr_has_entity_name = True
|
||||
_attr_name = None
|
||||
|
||||
def __init__(self, device, *args, **kwargs):
|
||||
"""Initialize the switch."""
|
||||
|
||||
@@ -34,6 +34,21 @@ class BPKConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
|
||||
VERSION = 1
|
||||
|
||||
async def async_step_import(self, config: dict[str, Any]) -> FlowResult:
|
||||
"""Import a configuration from config.yaml."""
|
||||
|
||||
if config.get(CONF_LATITUDE):
|
||||
config[CONF_LOCATION] = {
|
||||
CONF_LATITUDE: config[CONF_LATITUDE],
|
||||
CONF_LONGITUDE: config[CONF_LONGITUDE],
|
||||
}
|
||||
if not config.get(CONF_AREA):
|
||||
config[CONF_AREA] = "none"
|
||||
else:
|
||||
config[CONF_AREA] = config[CONF_AREA][0]
|
||||
|
||||
return await self.async_step_user(user_input=config)
|
||||
|
||||
async def async_step_user(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> FlowResult:
|
||||
|
||||
@@ -5,19 +5,62 @@ from collections import defaultdict
|
||||
from datetime import timedelta
|
||||
|
||||
from brottsplatskartan import ATTRIBUTION, BrottsplatsKartan
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sensor import SensorEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE
|
||||
from homeassistant.components.sensor import (
|
||||
PLATFORM_SCHEMA as PARENT_PLATFORM_SCHEMA,
|
||||
SensorEntity,
|
||||
)
|
||||
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
|
||||
from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME
|
||||
from homeassistant.core import HomeAssistant
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.device_registry import DeviceEntryType
|
||||
from homeassistant.helpers.entity import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
|
||||
from .const import CONF_APP_ID, CONF_AREA, DOMAIN, LOGGER
|
||||
from .const import AREAS, CONF_APP_ID, CONF_AREA, DEFAULT_NAME, DOMAIN, LOGGER
|
||||
|
||||
SCAN_INTERVAL = timedelta(minutes=30)
|
||||
|
||||
PLATFORM_SCHEMA = PARENT_PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Inclusive(CONF_LATITUDE, "coordinates"): cv.latitude,
|
||||
vol.Inclusive(CONF_LONGITUDE, "coordinates"): cv.longitude,
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Optional(CONF_AREA, default=[]): vol.All(cv.ensure_list, [vol.In(AREAS)]),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_platform(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigType,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
discovery_info: DiscoveryInfoType | None = None,
|
||||
) -> None:
|
||||
"""Set up the Brottsplatskartan platform."""
|
||||
|
||||
async_create_issue(
|
||||
hass,
|
||||
DOMAIN,
|
||||
"deprecated_yaml",
|
||||
breaks_in_ha_version="2023.11.0",
|
||||
is_fixable=False,
|
||||
severity=IssueSeverity.WARNING,
|
||||
translation_key="deprecated_yaml",
|
||||
)
|
||||
|
||||
hass.async_create_task(
|
||||
hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_IMPORT},
|
||||
data=config,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||
@@ -40,6 +83,7 @@ class BrottsplatskartanSensor(SensorEntity):
|
||||
|
||||
_attr_attribution = ATTRIBUTION
|
||||
_attr_has_entity_name = True
|
||||
_attr_name = None
|
||||
|
||||
def __init__(self, bpk: BrottsplatsKartan, name: str, entry_id: str) -> None:
|
||||
"""Initialize the Brottsplatskartan sensor."""
|
||||
|
||||
@@ -16,6 +16,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"issues": {
|
||||
"deprecated_yaml": {
|
||||
"title": "The Brottsplatskartan YAML configuration is being removed",
|
||||
"description": "Configuring Brottsplatskartan using YAML is being removed.\n\nYour existing YAML configuration has been imported into the UI automatically.\n\nRemove the Brottsplatskartan YAML configuration from your configuration.yaml file and restart Home Assistant to fix this issue."
|
||||
}
|
||||
},
|
||||
"selector": {
|
||||
"areas": {
|
||||
"options": {
|
||||
|
||||
@@ -71,6 +71,7 @@ class BSBLANClimate(
|
||||
"""Defines a BSBLAN climate device."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
_attr_name = None
|
||||
# Determine preset modes
|
||||
_attr_supported_features = (
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE
|
||||
|
||||
@@ -20,5 +20,5 @@
|
||||
"dependencies": ["bluetooth_adapters"],
|
||||
"documentation": "https://www.home-assistant.io/integrations/bthome",
|
||||
"iot_class": "local_push",
|
||||
"requirements": ["bthome-ble==2.12.0"]
|
||||
"requirements": ["bthome-ble==2.12.1"]
|
||||
}
|
||||
|
||||
@@ -301,55 +301,55 @@
|
||||
"name": "Condition 1d",
|
||||
"state": {
|
||||
"clear": "[%key:component::buienradar::entity::sensor::condition::state::clear%]",
|
||||
"cloudy": "[%key:component::buienradar::entity::sensor::condition::state::cloudy%]",
|
||||
"fog": "[%key:component::buienradar::entity::sensor::condition::state::fog%]",
|
||||
"rainy": "[%key:component::buienradar::entity::sensor::condition::state::rainy%]",
|
||||
"snowy": "[%key:component::buienradar::entity::sensor::condition::state::snowy%]",
|
||||
"lightning": "[%key:component::buienradar::entity::sensor::condition::state::lightning%]"
|
||||
"cloudy": "[%key:component::weather::entity_component::_::state::cloudy%]",
|
||||
"fog": "[%key:component::weather::entity_component::_::state::fog%]",
|
||||
"rainy": "[%key:component::weather::entity_component::_::state::rainy%]",
|
||||
"snowy": "[%key:component::weather::entity_component::_::state::snowy%]",
|
||||
"lightning": "[%key:component::weather::entity_component::_::state::lightning%]"
|
||||
}
|
||||
},
|
||||
"condition_2d": {
|
||||
"name": "Condition 2d",
|
||||
"state": {
|
||||
"clear": "[%key:component::buienradar::entity::sensor::condition::state::clear%]",
|
||||
"cloudy": "[%key:component::buienradar::entity::sensor::condition::state::cloudy%]",
|
||||
"fog": "[%key:component::buienradar::entity::sensor::condition::state::fog%]",
|
||||
"rainy": "[%key:component::buienradar::entity::sensor::condition::state::rainy%]",
|
||||
"snowy": "[%key:component::buienradar::entity::sensor::condition::state::snowy%]",
|
||||
"lightning": "[%key:component::buienradar::entity::sensor::condition::state::lightning%]"
|
||||
"cloudy": "[%key:component::weather::entity_component::_::state::cloudy%]",
|
||||
"fog": "[%key:component::weather::entity_component::_::state::fog%]",
|
||||
"rainy": "[%key:component::weather::entity_component::_::state::rainy%]",
|
||||
"snowy": "[%key:component::weather::entity_component::_::state::snowy%]",
|
||||
"lightning": "[%key:component::weather::entity_component::_::state::lightning%]"
|
||||
}
|
||||
},
|
||||
"condition_3d": {
|
||||
"name": "Condition 3d",
|
||||
"state": {
|
||||
"clear": "[%key:component::buienradar::entity::sensor::condition::state::clear%]",
|
||||
"cloudy": "[%key:component::buienradar::entity::sensor::condition::state::cloudy%]",
|
||||
"fog": "[%key:component::buienradar::entity::sensor::condition::state::fog%]",
|
||||
"rainy": "[%key:component::buienradar::entity::sensor::condition::state::rainy%]",
|
||||
"snowy": "[%key:component::buienradar::entity::sensor::condition::state::snowy%]",
|
||||
"lightning": "[%key:component::buienradar::entity::sensor::condition::state::lightning%]"
|
||||
"cloudy": "[%key:component::weather::entity_component::_::state::cloudy%]",
|
||||
"fog": "[%key:component::weather::entity_component::_::state::fog%]",
|
||||
"rainy": "[%key:component::weather::entity_component::_::state::rainy%]",
|
||||
"snowy": "[%key:component::weather::entity_component::_::state::snowy%]",
|
||||
"lightning": "[%key:component::weather::entity_component::_::state::lightning%]"
|
||||
}
|
||||
},
|
||||
"condition_4d": {
|
||||
"name": "Condition 4d",
|
||||
"state": {
|
||||
"clear": "[%key:component::buienradar::entity::sensor::condition::state::clear%]",
|
||||
"cloudy": "[%key:component::buienradar::entity::sensor::condition::state::cloudy%]",
|
||||
"fog": "[%key:component::buienradar::entity::sensor::condition::state::fog%]",
|
||||
"rainy": "[%key:component::buienradar::entity::sensor::condition::state::rainy%]",
|
||||
"snowy": "[%key:component::buienradar::entity::sensor::condition::state::snowy%]",
|
||||
"lightning": "[%key:component::buienradar::entity::sensor::condition::state::lightning%]"
|
||||
"cloudy": "[%key:component::weather::entity_component::_::state::cloudy%]",
|
||||
"fog": "[%key:component::weather::entity_component::_::state::fog%]",
|
||||
"rainy": "[%key:component::weather::entity_component::_::state::rainy%]",
|
||||
"snowy": "[%key:component::weather::entity_component::_::state::snowy%]",
|
||||
"lightning": "[%key:component::weather::entity_component::_::state::lightning%]"
|
||||
}
|
||||
},
|
||||
"condition_5d": {
|
||||
"name": "Condition 5d",
|
||||
"state": {
|
||||
"clear": "[%key:component::buienradar::entity::sensor::condition::state::clear%]",
|
||||
"cloudy": "[%key:component::buienradar::entity::sensor::condition::state::cloudy%]",
|
||||
"fog": "[%key:component::buienradar::entity::sensor::condition::state::fog%]",
|
||||
"rainy": "[%key:component::buienradar::entity::sensor::condition::state::rainy%]",
|
||||
"snowy": "[%key:component::buienradar::entity::sensor::condition::state::snowy%]",
|
||||
"lightning": "[%key:component::buienradar::entity::sensor::condition::state::lightning%]"
|
||||
"cloudy": "[%key:component::weather::entity_component::_::state::cloudy%]",
|
||||
"fog": "[%key:component::weather::entity_component::_::state::fog%]",
|
||||
"rainy": "[%key:component::weather::entity_component::_::state::rainy%]",
|
||||
"snowy": "[%key:component::weather::entity_component::_::state::snowy%]",
|
||||
"lightning": "[%key:component::weather::entity_component::_::state::lightning%]"
|
||||
}
|
||||
},
|
||||
"conditioncode_1d": {
|
||||
@@ -371,76 +371,76 @@
|
||||
"name": "Detailed condition 1d",
|
||||
"state": {
|
||||
"clear": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::clear%]",
|
||||
"partlycloudy": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy%]",
|
||||
"partlycloudy": "[%key:component::weather::entity_component::_::state::partlycloudy%]",
|
||||
"partlycloudy-fog": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-fog%]",
|
||||
"partlycloudy-light-rain": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-light-rain%]",
|
||||
"partlycloudy-rain": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-rain%]",
|
||||
"cloudy": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::cloudy%]",
|
||||
"fog": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::fog%]",
|
||||
"rainy": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::rainy%]",
|
||||
"cloudy": "[%key:component::weather::entity_component::_::state::cloudy%]",
|
||||
"fog": "[%key:component::weather::entity_component::_::state::fog%]",
|
||||
"rainy": "[%key:component::weather::entity_component::_::state::rainy%]",
|
||||
"light-rain": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::light-rain%]",
|
||||
"light-snow": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::light-snow%]",
|
||||
"partlycloudy-light-snow": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-light-snow%]",
|
||||
"partlycloudy-snow": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-snow%]",
|
||||
"partlycloudy-lightning": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-lightning%]",
|
||||
"snowy": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::snowy%]",
|
||||
"snowy-rainy": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::snowy-rainy%]",
|
||||
"lightning": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::lightning%]"
|
||||
"snowy": "[%key:component::weather::entity_component::_::state::snowy%]",
|
||||
"snowy-rainy": "[%key:component::weather::entity_component::_::state::snowy-rainy%]",
|
||||
"lightning": "[%key:component::weather::entity_component::_::state::lightning%]"
|
||||
}
|
||||
},
|
||||
"conditiondetailed_2d": {
|
||||
"name": "Detailed condition 2d",
|
||||
"state": {
|
||||
"clear": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::clear%]",
|
||||
"partlycloudy": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy%]",
|
||||
"partlycloudy": "[%key:component::weather::entity_component::_::state::partlycloudy%]",
|
||||
"partlycloudy-fog": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-fog%]",
|
||||
"partlycloudy-light-rain": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-light-rain%]",
|
||||
"partlycloudy-rain": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-rain%]",
|
||||
"cloudy": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::cloudy%]",
|
||||
"fog": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::fog%]",
|
||||
"rainy": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::rainy%]",
|
||||
"cloudy": "[%key:component::weather::entity_component::_::state::cloudy%]",
|
||||
"fog": "[%key:component::weather::entity_component::_::state::fog%]",
|
||||
"rainy": "[%key:component::weather::entity_component::_::state::rainy%]",
|
||||
"light-rain": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::light-rain%]",
|
||||
"light-snow": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::light-snow%]",
|
||||
"partlycloudy-light-snow": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-light-snow%]",
|
||||
"partlycloudy-snow": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-snow%]",
|
||||
"partlycloudy-lightning": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-lightning%]",
|
||||
"snowy": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::snowy%]",
|
||||
"snowy-rainy": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::snowy-rainy%]",
|
||||
"lightning": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::lightning%]"
|
||||
"snowy": "[%key:component::weather::entity_component::_::state::snowy%]",
|
||||
"snowy-rainy": "[%key:component::weather::entity_component::_::state::snowy-rainy%]",
|
||||
"lightning": "[%key:component::weather::entity_component::_::state::lightning%]"
|
||||
}
|
||||
},
|
||||
"conditiondetailed_3d": {
|
||||
"name": "Detailed condition 3d",
|
||||
"state": {
|
||||
"clear": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::clear%]",
|
||||
"partlycloudy": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy%]",
|
||||
"partlycloudy": "[%key:component::weather::entity_component::_::state::partlycloudy%]",
|
||||
"partlycloudy-fog": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-fog%]",
|
||||
"partlycloudy-light-rain": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-light-rain%]",
|
||||
"partlycloudy-rain": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-rain%]",
|
||||
"cloudy": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::cloudy%]",
|
||||
"fog": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::fog%]",
|
||||
"rainy": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::rainy%]",
|
||||
"cloudy": "[%key:component::weather::entity_component::_::state::cloudy%]",
|
||||
"fog": "[%key:component::weather::entity_component::_::state::fog%]",
|
||||
"rainy": "[%key:component::weather::entity_component::_::state::rainy%]",
|
||||
"light-rain": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::light-rain%]",
|
||||
"light-snow": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::light-snow%]",
|
||||
"partlycloudy-light-snow": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-light-snow%]",
|
||||
"partlycloudy-snow": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-snow%]",
|
||||
"partlycloudy-lightning": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-lightning%]",
|
||||
"snowy": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::snowy%]",
|
||||
"snowy-rainy": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::snowy-rainy%]",
|
||||
"lightning": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::lightning%]"
|
||||
"snowy": "[%key:component::weather::entity_component::_::state::snowy%]",
|
||||
"snowy-rainy": "[%key:component::weather::entity_component::_::state::snowy-rainy%]",
|
||||
"lightning": "[%key:component::weather::entity_component::_::state::lightning%]"
|
||||
}
|
||||
},
|
||||
"conditiondetailed_4d": {
|
||||
"name": "Detailed condition 4d",
|
||||
"state": {
|
||||
"clear": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::clear%]",
|
||||
"partlycloudy": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy%]",
|
||||
"partlycloudy": "[%key:component::weather::entity_component::_::state::partlycloudy%]",
|
||||
"partlycloudy-fog": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-fog%]",
|
||||
"partlycloudy-light-rain": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-light-rain%]",
|
||||
"partlycloudy-rain": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-rain%]",
|
||||
"cloudy": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::cloudy%]",
|
||||
"fog": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::fog%]",
|
||||
"rainy": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::rainy%]",
|
||||
"cloudy": "[%key:component::weather::entity_component::_::state::cloudy%]",
|
||||
"fog": "[%key:component::weather::entity_component::_::state::fog%]",
|
||||
"rainy": "[%key:component::weather::entity_component::_::state::rainy%]",
|
||||
"light-rain": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::light-rain%]",
|
||||
"light-snow": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::light-snow%]",
|
||||
"partlycloudy-light-snow": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-light-snow%]",
|
||||
@@ -455,21 +455,21 @@
|
||||
"name": "Detailed condition 5d",
|
||||
"state": {
|
||||
"clear": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::clear%]",
|
||||
"partlycloudy": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy%]",
|
||||
"partlycloudy": "[%key:component::weather::entity_component::_::state::partlycloudy%]",
|
||||
"partlycloudy-fog": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-fog%]",
|
||||
"partlycloudy-light-rain": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-light-rain%]",
|
||||
"partlycloudy-rain": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-rain%]",
|
||||
"cloudy": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::cloudy%]",
|
||||
"fog": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::fog%]",
|
||||
"rainy": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::rainy%]",
|
||||
"cloudy": "[%key:component::weather::entity_component::_::state::cloudy%]",
|
||||
"fog": "[%key:component::weather::entity_component::_::state::fog%]",
|
||||
"rainy": "[%key:component::weather::entity_component::_::state::rainy%]",
|
||||
"light-rain": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::light-rain%]",
|
||||
"light-snow": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::light-snow%]",
|
||||
"partlycloudy-light-snow": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-light-snow%]",
|
||||
"partlycloudy-snow": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-snow%]",
|
||||
"partlycloudy-lightning": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-lightning%]",
|
||||
"snowy": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::snowy%]",
|
||||
"snowy-rainy": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::snowy-rainy%]",
|
||||
"lightning": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::lightning%]"
|
||||
"snowy": "[%key:component::weather::entity_component::_::state::snowy%]",
|
||||
"snowy-rainy": "[%key:component::weather::entity_component::_::state::snowy-rainy%]",
|
||||
"lightning": "[%key:component::weather::entity_component::_::state::lightning%]"
|
||||
}
|
||||
},
|
||||
"conditionexact_1d": {
|
||||
|
||||
@@ -60,6 +60,7 @@ from .const import (
|
||||
EVENT_TIME_FIELDS,
|
||||
EVENT_TYPES,
|
||||
EVENT_UID,
|
||||
LIST_EVENT_FIELDS,
|
||||
CalendarEntityFeature,
|
||||
)
|
||||
|
||||
@@ -263,8 +264,8 @@ SERVICE_LIST_EVENTS_SCHEMA: Final = vol.All(
|
||||
cv.has_at_most_one_key(EVENT_END_DATETIME, EVENT_DURATION),
|
||||
cv.make_entity_service_schema(
|
||||
{
|
||||
vol.Optional(EVENT_START_DATETIME): datetime.datetime,
|
||||
vol.Optional(EVENT_END_DATETIME): datetime.datetime,
|
||||
vol.Optional(EVENT_START_DATETIME): cv.datetime,
|
||||
vol.Optional(EVENT_END_DATETIME): cv.datetime,
|
||||
vol.Optional(EVENT_DURATION): vol.All(
|
||||
cv.time_period, cv.positive_timedelta
|
||||
),
|
||||
@@ -415,6 +416,17 @@ def _api_event_dict_factory(obj: Iterable[tuple[str, Any]]) -> dict[str, Any]:
|
||||
return result
|
||||
|
||||
|
||||
def _list_events_dict_factory(
|
||||
obj: Iterable[tuple[str, Any]]
|
||||
) -> dict[str, JsonValueType]:
|
||||
"""Convert CalendarEvent dataclass items to dictionary of attributes."""
|
||||
return {
|
||||
name: value
|
||||
for name, value in _event_dict_factory(obj).items()
|
||||
if name in LIST_EVENT_FIELDS and value is not None
|
||||
}
|
||||
|
||||
|
||||
def _get_datetime_local(
|
||||
dt_or_d: datetime.datetime | datetime.date,
|
||||
) -> datetime.datetime:
|
||||
@@ -781,10 +793,12 @@ async def async_list_events_service(
|
||||
end = start + service_call.data[EVENT_DURATION]
|
||||
else:
|
||||
end = service_call.data[EVENT_END_DATETIME]
|
||||
calendar_event_list = await calendar.async_get_events(calendar.hass, start, end)
|
||||
events: list[JsonValueType] = [
|
||||
dataclasses.asdict(event) for event in calendar_event_list
|
||||
]
|
||||
calendar_event_list = await calendar.async_get_events(
|
||||
calendar.hass, dt_util.as_local(start), dt_util.as_local(end)
|
||||
)
|
||||
return {
|
||||
"events": events,
|
||||
"events": [
|
||||
dataclasses.asdict(event, dict_factory=_list_events_dict_factory)
|
||||
for event in calendar_event_list
|
||||
]
|
||||
}
|
||||
|
||||
@@ -41,3 +41,12 @@ EVENT_TIME_FIELDS = {
|
||||
}
|
||||
EVENT_TYPES = "event_types"
|
||||
EVENT_DURATION = "duration"
|
||||
|
||||
# Fields for the list events service
|
||||
LIST_EVENT_FIELDS = {
|
||||
"start",
|
||||
"end",
|
||||
EVENT_SUMMARY,
|
||||
EVENT_DESCRIPTION,
|
||||
EVENT_LOCATION,
|
||||
}
|
||||
|
||||
@@ -8,10 +8,10 @@ from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
CONF_HOST,
|
||||
CONF_PORT,
|
||||
EVENT_HOMEASSISTANT_STARTED,
|
||||
Platform,
|
||||
)
|
||||
from homeassistant.core import CoreState, HomeAssistant
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.start import async_at_started
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||
|
||||
from .const import DEFAULT_PORT, DOMAIN
|
||||
@@ -38,19 +38,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
if entry.unique_id is None:
|
||||
hass.config_entries.async_update_entry(entry, unique_id=f"{host}:{port}")
|
||||
|
||||
async def async_finish_startup(_):
|
||||
async def _async_finish_startup(_):
|
||||
await coordinator.async_refresh()
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
if hass.state == CoreState.running:
|
||||
await async_finish_startup(None)
|
||||
else:
|
||||
entry.async_on_unload(
|
||||
hass.bus.async_listen_once(
|
||||
EVENT_HOMEASSISTANT_STARTED, async_finish_startup
|
||||
)
|
||||
)
|
||||
|
||||
async_at_started(hass, _async_finish_startup)
|
||||
return True
|
||||
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ async def async_setup_platform(
|
||||
hass,
|
||||
DOMAIN,
|
||||
"deprecated_yaml_binary_sensor",
|
||||
breaks_in_ha_version="2023.8.0",
|
||||
breaks_in_ha_version="2023.12.0",
|
||||
is_fixable=False,
|
||||
severity=IssueSeverity.WARNING,
|
||||
translation_key="deprecated_platform_yaml",
|
||||
|
||||
@@ -73,7 +73,7 @@ async def async_setup_platform(
|
||||
hass,
|
||||
DOMAIN,
|
||||
"deprecated_yaml_cover",
|
||||
breaks_in_ha_version="2023.8.0",
|
||||
breaks_in_ha_version="2023.12.0",
|
||||
is_fixable=False,
|
||||
severity=IssueSeverity.WARNING,
|
||||
translation_key="deprecated_platform_yaml",
|
||||
|
||||
@@ -43,7 +43,7 @@ def get_service(
|
||||
hass,
|
||||
DOMAIN,
|
||||
"deprecated_yaml_notify",
|
||||
breaks_in_ha_version="2023.8.0",
|
||||
breaks_in_ha_version="2023.12.0",
|
||||
is_fixable=False,
|
||||
severity=IssueSeverity.WARNING,
|
||||
translation_key="deprecated_platform_yaml",
|
||||
|
||||
@@ -74,7 +74,7 @@ async def async_setup_platform(
|
||||
hass,
|
||||
DOMAIN,
|
||||
"deprecated_yaml_sensor",
|
||||
breaks_in_ha_version="2023.8.0",
|
||||
breaks_in_ha_version="2023.12.0",
|
||||
is_fixable=False,
|
||||
severity=IssueSeverity.WARNING,
|
||||
translation_key="deprecated_platform_yaml",
|
||||
|
||||
@@ -74,7 +74,7 @@ async def async_setup_platform(
|
||||
hass,
|
||||
DOMAIN,
|
||||
"deprecated_yaml_switch",
|
||||
breaks_in_ha_version="2023.8.0",
|
||||
breaks_in_ha_version="2023.12.0",
|
||||
is_fixable=False,
|
||||
severity=IssueSeverity.WARNING,
|
||||
translation_key="deprecated_platform_yaml",
|
||||
|
||||
@@ -8,6 +8,7 @@ import logging
|
||||
import re
|
||||
from typing import Any, Literal
|
||||
|
||||
from hassil.recognize import RecognizeResult
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import core
|
||||
@@ -353,6 +354,10 @@ async def websocket_hass_agent_debug(
|
||||
}
|
||||
for entity_key, entity in result.entities.items()
|
||||
},
|
||||
"targets": {
|
||||
state.entity_id: {"matched": is_matched}
|
||||
for state, is_matched in _get_debug_targets(hass, result)
|
||||
},
|
||||
}
|
||||
if result is not None
|
||||
else None
|
||||
@@ -362,6 +367,49 @@ async def websocket_hass_agent_debug(
|
||||
)
|
||||
|
||||
|
||||
def _get_debug_targets(
|
||||
hass: HomeAssistant,
|
||||
result: RecognizeResult,
|
||||
) -> Iterable[tuple[core.State, bool]]:
|
||||
"""Yield state/is_matched pairs for a hassil recognition."""
|
||||
entities = result.entities
|
||||
|
||||
name: str | None = None
|
||||
area_name: str | None = None
|
||||
domains: set[str] | None = None
|
||||
device_classes: set[str] | None = None
|
||||
state_names: set[str] | None = None
|
||||
|
||||
if "name" in entities:
|
||||
name = str(entities["name"].value)
|
||||
|
||||
if "area" in entities:
|
||||
area_name = str(entities["area"].value)
|
||||
|
||||
if "domain" in entities:
|
||||
domains = set(cv.ensure_list(entities["domain"].value))
|
||||
|
||||
if "device_class" in entities:
|
||||
device_classes = set(cv.ensure_list(entities["device_class"].value))
|
||||
|
||||
if "state" in entities:
|
||||
# HassGetState only
|
||||
state_names = set(cv.ensure_list(entities["state"].value))
|
||||
|
||||
states = intent.async_match_states(
|
||||
hass,
|
||||
name=name,
|
||||
area_name=area_name,
|
||||
domains=domains,
|
||||
device_classes=device_classes,
|
||||
)
|
||||
|
||||
for state in states:
|
||||
# For queries, a target is "matched" based on its state
|
||||
is_matched = (state_names is None) or (state.state in state_names)
|
||||
yield state, is_matched
|
||||
|
||||
|
||||
class ConversationProcessView(http.HomeAssistantView):
|
||||
"""View to process text."""
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ process:
|
||||
name: Text
|
||||
description: Transcribed text
|
||||
example: Turn all lights on
|
||||
required: true
|
||||
selector:
|
||||
text:
|
||||
language:
|
||||
@@ -20,4 +21,4 @@ process:
|
||||
description: Assist engine to process your request
|
||||
example: homeassistant
|
||||
selector:
|
||||
text:
|
||||
conversation_agent:
|
||||
|
||||
@@ -3,6 +3,7 @@ from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from hassil.recognize import PUNCTUATION
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.const import CONF_COMMAND, CONF_PLATFORM
|
||||
@@ -15,10 +16,22 @@ from . import HOME_ASSISTANT_AGENT, _get_agent_manager
|
||||
from .const import DOMAIN
|
||||
from .default_agent import DefaultAgent
|
||||
|
||||
|
||||
def has_no_punctuation(value: list[str]) -> list[str]:
|
||||
"""Validate result does not contain punctuation."""
|
||||
for sentence in value:
|
||||
if PUNCTUATION.search(sentence):
|
||||
raise vol.Invalid("sentence should not contain punctuation")
|
||||
|
||||
return value
|
||||
|
||||
|
||||
TRIGGER_SCHEMA = cv.TRIGGER_BASE_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_PLATFORM): DOMAIN,
|
||||
vol.Required(CONF_COMMAND): vol.All(cv.ensure_list, [cv.string]),
|
||||
vol.Required(CONF_COMMAND): vol.All(
|
||||
cv.ensure_list, [cv.string], has_no_punctuation
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -292,7 +292,7 @@ class Counter(collection.CollectionEntity, RestoreEntity):
|
||||
self.hass,
|
||||
DOMAIN,
|
||||
"deprecated_configure_service",
|
||||
breaks_in_ha_version="2023.8.0",
|
||||
breaks_in_ha_version="2023.12.0",
|
||||
is_fixable=True,
|
||||
is_persistent=True,
|
||||
severity=IssueSeverity.WARNING,
|
||||
|
||||
@@ -15,8 +15,9 @@ from homeassistant.const import (
|
||||
CONF_UUID,
|
||||
Platform,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
|
||||
@@ -52,6 +53,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
if not daikin_api:
|
||||
return False
|
||||
|
||||
await async_migrate_unique_id(hass, entry, daikin_api)
|
||||
|
||||
hass.data.setdefault(DOMAIN, {}).update({entry.entry_id: daikin_api})
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
return True
|
||||
@@ -67,7 +70,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
return unload_ok
|
||||
|
||||
|
||||
async def daikin_api_setup(hass, host, key, uuid, password):
|
||||
async def daikin_api_setup(hass: HomeAssistant, host, key, uuid, password):
|
||||
"""Create a Daikin instance only once."""
|
||||
|
||||
session = async_get_clientsession(hass)
|
||||
@@ -127,3 +130,82 @@ class DaikinApi:
|
||||
name=info.get("name"),
|
||||
sw_version=info.get("ver", "").replace("_", "."),
|
||||
)
|
||||
|
||||
|
||||
async def async_migrate_unique_id(
|
||||
hass: HomeAssistant, config_entry: ConfigEntry, api: DaikinApi
|
||||
) -> None:
|
||||
"""Migrate old entry."""
|
||||
dev_reg = dr.async_get(hass)
|
||||
old_unique_id = config_entry.unique_id
|
||||
new_unique_id = api.device.mac
|
||||
new_name = api.device.values["name"]
|
||||
|
||||
@callback
|
||||
def _update_unique_id(entity_entry: er.RegistryEntry) -> dict[str, str] | None:
|
||||
"""Update unique ID of entity entry."""
|
||||
return update_unique_id(entity_entry, new_unique_id)
|
||||
|
||||
if new_unique_id == old_unique_id:
|
||||
return
|
||||
|
||||
# Migrate devices
|
||||
for device_entry in dr.async_entries_for_config_entry(
|
||||
dev_reg, config_entry.entry_id
|
||||
):
|
||||
for connection in device_entry.connections:
|
||||
if connection[1] == old_unique_id:
|
||||
new_connections = {
|
||||
(CONNECTION_NETWORK_MAC, dr.format_mac(new_unique_id))
|
||||
}
|
||||
|
||||
_LOGGER.debug(
|
||||
"Migrating device %s connections to %s",
|
||||
device_entry.name,
|
||||
new_connections,
|
||||
)
|
||||
dev_reg.async_update_device(
|
||||
device_entry.id,
|
||||
merge_connections=new_connections,
|
||||
)
|
||||
|
||||
if device_entry.name is None:
|
||||
_LOGGER.debug(
|
||||
"Migrating device name to %s",
|
||||
new_name,
|
||||
)
|
||||
dev_reg.async_update_device(
|
||||
device_entry.id,
|
||||
name=new_name,
|
||||
)
|
||||
|
||||
# Migrate entities
|
||||
await er.async_migrate_entries(hass, config_entry.entry_id, _update_unique_id)
|
||||
|
||||
new_data = {**config_entry.data, KEY_MAC: dr.format_mac(new_unique_id)}
|
||||
|
||||
hass.config_entries.async_update_entry(
|
||||
config_entry, unique_id=new_unique_id, data=new_data
|
||||
)
|
||||
|
||||
|
||||
@callback
|
||||
def update_unique_id(
|
||||
entity_entry: er.RegistryEntry, unique_id: str
|
||||
) -> dict[str, str] | None:
|
||||
"""Update unique ID of entity entry."""
|
||||
if entity_entry.unique_id.startswith(unique_id):
|
||||
# Already correct, nothing to do
|
||||
return None
|
||||
|
||||
unique_id_parts = entity_entry.unique_id.split("-")
|
||||
unique_id_parts[0] = unique_id
|
||||
entity_new_unique_id = "-".join(unique_id_parts)
|
||||
|
||||
_LOGGER.debug(
|
||||
"Migrating entity %s from %s to new id %s",
|
||||
entity_entry.entity_id,
|
||||
entity_entry.unique_id,
|
||||
entity_new_unique_id,
|
||||
)
|
||||
return {"new_unique_id": entity_new_unique_id}
|
||||
|
||||
@@ -7,6 +7,6 @@
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["pydaikin"],
|
||||
"quality_scale": "platinum",
|
||||
"requirements": ["pydaikin==2.9.0"],
|
||||
"requirements": ["pydaikin==2.10.5"],
|
||||
"zeroconf": ["_dkapi._tcp.local."]
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ async def async_setup_entry(
|
||||
[
|
||||
DaikinZoneSwitch(daikin_api, zone_id)
|
||||
for zone_id, zone in enumerate(zones)
|
||||
if zone != ("-", "0")
|
||||
if zone[0] != "-"
|
||||
]
|
||||
)
|
||||
if daikin_api.device.support_advanced_modes:
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/delijn",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["pydelijn"],
|
||||
"requirements": ["pydelijn==1.0.0"]
|
||||
"requirements": ["pydelijn==1.1.0"]
|
||||
}
|
||||
|
||||
@@ -108,6 +108,7 @@ async def async_setup_entry(
|
||||
):
|
||||
device_info = DeviceInfo(
|
||||
identifiers=device.identifiers,
|
||||
connections=device.connections,
|
||||
)
|
||||
else:
|
||||
device_info = None
|
||||
|
||||
@@ -726,6 +726,10 @@ class DeviceTracker:
|
||||
class Device(RestoreEntity):
|
||||
"""Base class for a tracked device."""
|
||||
|
||||
# This entity is legacy and does not have a platform.
|
||||
# We can't fix this easily without breaking changes.
|
||||
_no_platform_reported = True
|
||||
|
||||
host_name: str | None = None
|
||||
location_name: str | None = None
|
||||
gps: GPSType | None = None
|
||||
|
||||
@@ -8,6 +8,8 @@ from .devolo_device import DevoloDeviceEntity
|
||||
class DevoloMultiLevelSwitchDeviceEntity(DevoloDeviceEntity):
|
||||
"""Representation of a multi level switch device within devolo Home Control. Something like a dimmer or a thermostat."""
|
||||
|
||||
_attr_name = None
|
||||
|
||||
def __init__(
|
||||
self, homecontrol: HomeControl, device_instance: Zwave, element_uid: str
|
||||
) -> None:
|
||||
|
||||
@@ -41,6 +41,8 @@ async def async_setup_entry(
|
||||
class DevoloSwitch(DevoloDeviceEntity, SwitchEntity):
|
||||
"""Representation of a switch."""
|
||||
|
||||
_attr_name = None
|
||||
|
||||
def __init__(
|
||||
self, homecontrol: HomeControl, device_instance: Zwave, element_uid: str
|
||||
) -> None:
|
||||
|
||||
@@ -60,7 +60,6 @@ class ServiceDetails(NamedTuple):
|
||||
SERVICE_HANDLERS = {
|
||||
SERVICE_ENIGMA2: ServiceDetails("media_player", "enigma2"),
|
||||
"yamaha": ServiceDetails("media_player", "yamaha"),
|
||||
"openhome": ServiceDetails("media_player", "openhome"),
|
||||
"bluesound": ServiceDetails("media_player", "bluesound"),
|
||||
}
|
||||
|
||||
@@ -87,6 +86,7 @@ MIGRATED_SERVICE_HANDLERS = [
|
||||
SERVICE_MOBILE_APP,
|
||||
SERVICE_NETGEAR,
|
||||
SERVICE_OCTOPRINT,
|
||||
"openhome",
|
||||
"philips_hue",
|
||||
SERVICE_SAMSUNG_PRINTER,
|
||||
"sonos",
|
||||
|
||||
@@ -91,7 +91,7 @@ async def async_setup_platform(
|
||||
hass,
|
||||
DOMAIN,
|
||||
"deprecated_yaml",
|
||||
breaks_in_ha_version="2023.8.0",
|
||||
breaks_in_ha_version="2023.12.0",
|
||||
is_fixable=False,
|
||||
severity=IssueSeverity.WARNING,
|
||||
translation_key="deprecated_yaml",
|
||||
|
||||
@@ -47,6 +47,7 @@ async def async_setup_entry(
|
||||
class ElgatoLight(ElgatoEntity, LightEntity):
|
||||
"""Defines an Elgato Light."""
|
||||
|
||||
_attr_name = None
|
||||
_attr_min_mireds = 143
|
||||
_attr_max_mireds = 344
|
||||
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/environment_canada",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["env_canada"],
|
||||
"requirements": ["env-canada==0.5.34"]
|
||||
"requirements": ["env-canada==0.5.35"]
|
||||
}
|
||||
|
||||
@@ -76,6 +76,7 @@ class ControllerEntity(ClimateEntity):
|
||||
|
||||
_attr_fan_modes = list(_HA_FAN_TO_ESCEA)
|
||||
_attr_has_entity_name = True
|
||||
_attr_name = None
|
||||
_attr_hvac_modes = [HVACMode.HEAT, HVACMode.OFF]
|
||||
_attr_icon = ICON
|
||||
_attr_precision = PRECISION_WHOLE
|
||||
|
||||
@@ -388,6 +388,7 @@ async def async_setup_entry( # noqa: C901
|
||||
assert cli.api_version is not None
|
||||
entry_data.api_version = cli.api_version
|
||||
entry_data.available = True
|
||||
entry_data.expected_disconnect = True
|
||||
if entry_data.device_info.name:
|
||||
reconnect_logic.name = entry_data.device_info.name
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .entity import (
|
||||
EsphomeEntity,
|
||||
esphome_state_property,
|
||||
platform_async_setup_entry,
|
||||
)
|
||||
from .enum_mapper import EsphomeEnumMapper
|
||||
@@ -111,6 +112,7 @@ class EsphomeAlarmControlPanel(
|
||||
self._attr_code_arm_required = bool(static_info.requires_code_to_arm)
|
||||
|
||||
@property
|
||||
@esphome_state_property
|
||||
def state(self) -> str | None:
|
||||
"""Return the state of the device."""
|
||||
return _ESPHOME_ACP_STATE_TO_HASS_STATE.from_esphome(self._state.state)
|
||||
|
||||
@@ -3,6 +3,7 @@ from __future__ import annotations
|
||||
|
||||
from collections import OrderedDict
|
||||
from collections.abc import Mapping
|
||||
import json
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
@@ -33,13 +34,15 @@ from .const import (
|
||||
DEFAULT_NEW_CONFIG_ALLOW_ALLOW_SERVICE_CALLS,
|
||||
DOMAIN,
|
||||
)
|
||||
from .dashboard import async_get_dashboard, async_set_dashboard_info
|
||||
from .dashboard import async_get_or_create_dashboard_manager, async_set_dashboard_info
|
||||
|
||||
ERROR_REQUIRES_ENCRYPTION_KEY = "requires_encryption_key"
|
||||
ERROR_INVALID_ENCRYPTION_KEY = "invalid_psk"
|
||||
ESPHOME_URL = "https://esphome.io/"
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
ZERO_NOISE_PSK = "MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA="
|
||||
|
||||
|
||||
class EsphomeFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
"""Handle a esphome config flow."""
|
||||
@@ -149,11 +152,22 @@ class EsphomeFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
async def _async_try_fetch_device_info(self) -> FlowResult:
|
||||
error = await self.fetch_device_info()
|
||||
|
||||
if (
|
||||
error == ERROR_REQUIRES_ENCRYPTION_KEY
|
||||
and await self._retrieve_encryption_key_from_dashboard()
|
||||
):
|
||||
error = await self.fetch_device_info()
|
||||
if error == ERROR_REQUIRES_ENCRYPTION_KEY:
|
||||
if not self._device_name and not self._noise_psk:
|
||||
# If device name is not set we can send a zero noise psk
|
||||
# to get the device name which will allow us to populate
|
||||
# the device name and hopefully get the encryption key
|
||||
# from the dashboard.
|
||||
self._noise_psk = ZERO_NOISE_PSK
|
||||
error = await self.fetch_device_info()
|
||||
self._noise_psk = None
|
||||
|
||||
if (
|
||||
self._device_name
|
||||
and await self._retrieve_encryption_key_from_dashboard()
|
||||
):
|
||||
error = await self.fetch_device_info()
|
||||
|
||||
# If the fetched key is invalid, unset it again.
|
||||
if error == ERROR_INVALID_ENCRYPTION_KEY:
|
||||
self._noise_psk = None
|
||||
@@ -323,7 +337,10 @@ class EsphomeFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
self._device_info = await cli.device_info()
|
||||
except RequiresEncryptionAPIError:
|
||||
return ERROR_REQUIRES_ENCRYPTION_KEY
|
||||
except InvalidEncryptionKeyAPIError:
|
||||
except InvalidEncryptionKeyAPIError as ex:
|
||||
if ex.received_name:
|
||||
self._device_name = ex.received_name
|
||||
self._name = ex.received_name
|
||||
return ERROR_INVALID_ENCRYPTION_KEY
|
||||
except ResolveAPIError:
|
||||
return "resolve_error"
|
||||
@@ -334,9 +351,8 @@ class EsphomeFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
|
||||
self._name = self._device_info.friendly_name or self._device_info.name
|
||||
self._device_name = self._device_info.name
|
||||
await self.async_set_unique_id(
|
||||
self._device_info.mac_address, raise_on_progress=False
|
||||
)
|
||||
mac_address = format_mac(self._device_info.mac_address)
|
||||
await self.async_set_unique_id(mac_address, raise_on_progress=False)
|
||||
if not self._reauth_entry:
|
||||
self._abort_if_unique_id_configured(
|
||||
updates={CONF_HOST: self._host, CONF_PORT: self._port}
|
||||
@@ -373,14 +389,15 @@ class EsphomeFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
|
||||
Return boolean if a key was retrieved.
|
||||
"""
|
||||
if self._device_name is None:
|
||||
return False
|
||||
|
||||
if (dashboard := async_get_dashboard(self.hass)) is None:
|
||||
if (
|
||||
self._device_name is None
|
||||
or (manager := await async_get_or_create_dashboard_manager(self.hass))
|
||||
is None
|
||||
or (dashboard := manager.async_get()) is None
|
||||
):
|
||||
return False
|
||||
|
||||
await dashboard.async_request_refresh()
|
||||
|
||||
if not dashboard.last_update_success:
|
||||
return False
|
||||
|
||||
@@ -394,6 +411,11 @@ class EsphomeFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
except aiohttp.ClientError as err:
|
||||
_LOGGER.error("Error talking to the dashboard: %s", err)
|
||||
return False
|
||||
except json.JSONDecodeError as err:
|
||||
_LOGGER.error(
|
||||
"Error parsing response from dashboard: %s", err, exc_info=True
|
||||
)
|
||||
return False
|
||||
|
||||
self._noise_psk = noise_psk
|
||||
return True
|
||||
|
||||
@@ -93,13 +93,6 @@ class ESPHomeDashboardManager:
|
||||
hass, addon_slug, url, async_get_clientsession(hass)
|
||||
)
|
||||
await dashboard.async_request_refresh()
|
||||
if not cur_dashboard and not dashboard.last_update_success:
|
||||
# If there was no previous dashboard and the new one is not available,
|
||||
# we skip setup and wait for discovery.
|
||||
_LOGGER.error(
|
||||
"Dashboard unavailable; skipping setup: %s", dashboard.last_exception
|
||||
)
|
||||
return
|
||||
|
||||
self._current_dashboard = dashboard
|
||||
|
||||
@@ -143,7 +136,14 @@ class ESPHomeDashboardManager:
|
||||
|
||||
@callback
|
||||
def async_get_dashboard(hass: HomeAssistant) -> ESPHomeDashboard | None:
|
||||
"""Get an instance of the dashboard if set."""
|
||||
"""Get an instance of the dashboard if set.
|
||||
|
||||
This is only safe to call after `async_setup` has been completed.
|
||||
|
||||
It should not be called from the config flow because there is a race
|
||||
where manager can be an asyncio.Event instead of the actual manager
|
||||
because the singleton decorator is not yet done.
|
||||
"""
|
||||
manager: ESPHomeDashboardManager | None = hass.data.get(KEY_DASHBOARD_MANAGER)
|
||||
return manager.async_get() if manager else None
|
||||
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["aioesphomeapi", "noiseprotocol"],
|
||||
"requirements": [
|
||||
"aioesphomeapi==15.0.0",
|
||||
"bluetooth-data-tools==1.2.0",
|
||||
"aioesphomeapi==15.1.1",
|
||||
"bluetooth-data-tools==1.3.0",
|
||||
"esphome-dashboard-api==1.2.3"
|
||||
],
|
||||
"zeroconf": ["_esphomelib._tcp.local."]
|
||||
|
||||
@@ -312,7 +312,7 @@ class EzvizCamera(EzvizEntity, Camera):
|
||||
self.hass,
|
||||
DOMAIN,
|
||||
"service_depreciation_detection_sensibility",
|
||||
breaks_in_ha_version="2023.8.0",
|
||||
breaks_in_ha_version="2023.12.0",
|
||||
is_fixable=False,
|
||||
severity=ir.IssueSeverity.WARNING,
|
||||
translation_key="service_depreciation_detection_sensibility",
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
"issues": {
|
||||
"service_depreciation_detection_sensibility": {
|
||||
"title": "Ezviz Detection sensitivity service is being removed",
|
||||
"description": "Ezviz Detection sensitivity service is deprecated and will be removed in Home Assistant 2023.8; Please adjust the automation or script that uses the service and select submit below to mark this issue as resolved."
|
||||
"description": "Ezviz Detection sensitivity service is deprecated and will be removed in Home Assistant 2023.12; Please adjust the automation or script that uses the service and select submit below to mark this issue as resolved."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,4 @@
|
||||
# Describes the format for available fan services
|
||||
set_speed:
|
||||
name: Set speed
|
||||
description: Set fan speed.
|
||||
target:
|
||||
entity:
|
||||
domain: fan
|
||||
fields:
|
||||
speed:
|
||||
name: Speed
|
||||
description: Speed setting.
|
||||
required: true
|
||||
example: "low"
|
||||
selector:
|
||||
text:
|
||||
|
||||
set_preset_mode:
|
||||
name: Set preset mode
|
||||
description: Set preset mode for a fan device.
|
||||
@@ -53,12 +38,6 @@ turn_on:
|
||||
entity:
|
||||
domain: fan
|
||||
fields:
|
||||
speed:
|
||||
name: Speed
|
||||
description: Speed setting.
|
||||
example: "high"
|
||||
selector:
|
||||
text:
|
||||
percentage:
|
||||
name: Percentage
|
||||
description: Percentage speed setting.
|
||||
|
||||
@@ -34,6 +34,7 @@ class FlickPricingSensor(SensorEntity):
|
||||
|
||||
_attr_attribution = "Data provided by Flick Electric"
|
||||
_attr_native_unit_of_measurement = f"{CURRENCY_CENT}/{UnitOfEnergy.KILO_WATT_HOUR}"
|
||||
_attr_has_entity_name = True
|
||||
_attr_translation_key = "power_price"
|
||||
_attributes: dict[str, Any] = {}
|
||||
|
||||
|
||||
@@ -30,9 +30,6 @@ async def async_setup_entry(
|
||||
avm_wrapper.fritz_guest_wifi.get_info
|
||||
)
|
||||
|
||||
if not guest_wifi_info.get("NewEnable"):
|
||||
return
|
||||
|
||||
async_add_entities(
|
||||
[
|
||||
FritzGuestWifiQRImage(
|
||||
|
||||
@@ -20,5 +20,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/frontend",
|
||||
"integration_type": "system",
|
||||
"quality_scale": "internal",
|
||||
"requirements": ["home-assistant-frontend==20230628.0"]
|
||||
"requirements": ["home-assistant-frontend==20230705.1"]
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ async def async_setup_platform(
|
||||
hass,
|
||||
DOMAIN,
|
||||
"deprecated_yaml",
|
||||
breaks_in_ha_version="2023.8.0",
|
||||
breaks_in_ha_version="2023.12.0",
|
||||
is_fixable=False,
|
||||
severity=IssueSeverity.WARNING,
|
||||
translation_key="deprecated_yaml",
|
||||
|
||||
@@ -16,5 +16,5 @@
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["goalzero"],
|
||||
"quality_scale": "silver",
|
||||
"requirements": ["goalzero==0.2.1"]
|
||||
"requirements": ["goalzero==0.2.2"]
|
||||
}
|
||||
|
||||
@@ -18,18 +18,11 @@ from homeassistant.helpers.config_entry_oauth2_flow import (
|
||||
)
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
from .const import (
|
||||
CONF_ENABLE_CONVERSATION_AGENT,
|
||||
CONF_LANGUAGE_CODE,
|
||||
DATA_MEM_STORAGE,
|
||||
DATA_SESSION,
|
||||
DOMAIN,
|
||||
)
|
||||
from .const import DATA_MEM_STORAGE, DATA_SESSION, DOMAIN, SUPPORTED_LANGUAGE_CODES
|
||||
from .helpers import (
|
||||
GoogleAssistantSDKAudioView,
|
||||
InMemoryStorage,
|
||||
async_send_text_commands,
|
||||
default_language_code,
|
||||
)
|
||||
|
||||
SERVICE_SEND_TEXT_COMMAND = "send_text_command"
|
||||
@@ -82,8 +75,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
|
||||
await async_setup_service(hass)
|
||||
|
||||
entry.async_on_unload(entry.add_update_listener(update_listener))
|
||||
await update_listener(hass, entry)
|
||||
agent = GoogleAssistantConversationAgent(hass, entry)
|
||||
conversation.async_set_agent(hass, entry, agent)
|
||||
|
||||
return True
|
||||
|
||||
@@ -100,8 +93,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
for service_name in hass.services.async_services()[DOMAIN]:
|
||||
hass.services.async_remove(DOMAIN, service_name)
|
||||
|
||||
if entry.options.get(CONF_ENABLE_CONVERSATION_AGENT, False):
|
||||
conversation.async_unset_agent(hass, entry)
|
||||
conversation.async_unset_agent(hass, entry)
|
||||
|
||||
return True
|
||||
|
||||
@@ -125,15 +117,6 @@ async def async_setup_service(hass: HomeAssistant) -> None:
|
||||
)
|
||||
|
||||
|
||||
async def update_listener(hass, entry):
|
||||
"""Handle options update."""
|
||||
if entry.options.get(CONF_ENABLE_CONVERSATION_AGENT, False):
|
||||
agent = GoogleAssistantConversationAgent(hass, entry)
|
||||
conversation.async_set_agent(hass, entry, agent)
|
||||
else:
|
||||
conversation.async_unset_agent(hass, entry)
|
||||
|
||||
|
||||
class GoogleAssistantConversationAgent(conversation.AbstractConversationAgent):
|
||||
"""Google Assistant SDK conversation agent."""
|
||||
|
||||
@@ -143,6 +126,7 @@ class GoogleAssistantConversationAgent(conversation.AbstractConversationAgent):
|
||||
self.entry = entry
|
||||
self.assistant: TextAssistant | None = None
|
||||
self.session: OAuth2Session | None = None
|
||||
self.language: str | None = None
|
||||
|
||||
@property
|
||||
def attribution(self):
|
||||
@@ -155,10 +139,7 @@ class GoogleAssistantConversationAgent(conversation.AbstractConversationAgent):
|
||||
@property
|
||||
def supported_languages(self) -> list[str]:
|
||||
"""Return a list of supported languages."""
|
||||
language_code = self.entry.options.get(
|
||||
CONF_LANGUAGE_CODE, default_language_code(self.hass)
|
||||
)
|
||||
return [language_code]
|
||||
return SUPPORTED_LANGUAGE_CODES
|
||||
|
||||
async def async_process(
|
||||
self, user_input: conversation.ConversationInput
|
||||
@@ -172,12 +153,10 @@ class GoogleAssistantConversationAgent(conversation.AbstractConversationAgent):
|
||||
if not session.valid_token:
|
||||
await session.async_ensure_token_valid()
|
||||
self.assistant = None
|
||||
if not self.assistant:
|
||||
if not self.assistant or user_input.language != self.language:
|
||||
credentials = Credentials(session.token[CONF_ACCESS_TOKEN])
|
||||
language_code = self.entry.options.get(
|
||||
CONF_LANGUAGE_CODE, default_language_code(self.hass)
|
||||
)
|
||||
self.assistant = TextAssistant(credentials, language_code)
|
||||
self.language = user_input.language
|
||||
self.assistant = TextAssistant(credentials, self.language)
|
||||
|
||||
resp = self.assistant.assist(user_input.text)
|
||||
text_response = resp[0] or "<empty response>"
|
||||
|
||||
@@ -13,13 +13,7 @@ from homeassistant.core import callback
|
||||
from homeassistant.data_entry_flow import FlowResult
|
||||
from homeassistant.helpers import config_entry_oauth2_flow
|
||||
|
||||
from .const import (
|
||||
CONF_ENABLE_CONVERSATION_AGENT,
|
||||
CONF_LANGUAGE_CODE,
|
||||
DEFAULT_NAME,
|
||||
DOMAIN,
|
||||
SUPPORTED_LANGUAGE_CODES,
|
||||
)
|
||||
from .const import CONF_LANGUAGE_CODE, DEFAULT_NAME, DOMAIN, SUPPORTED_LANGUAGE_CODES
|
||||
from .helpers import default_language_code
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -114,12 +108,6 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
||||
CONF_LANGUAGE_CODE,
|
||||
default=self.config_entry.options.get(CONF_LANGUAGE_CODE),
|
||||
): vol.In(SUPPORTED_LANGUAGE_CODES),
|
||||
vol.Required(
|
||||
CONF_ENABLE_CONVERSATION_AGENT,
|
||||
default=self.config_entry.options.get(
|
||||
CONF_ENABLE_CONVERSATION_AGENT
|
||||
),
|
||||
): bool,
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
@@ -5,7 +5,6 @@ DOMAIN: Final = "google_assistant_sdk"
|
||||
|
||||
DEFAULT_NAME: Final = "Google Assistant SDK"
|
||||
|
||||
CONF_ENABLE_CONVERSATION_AGENT: Final = "enable_conversation_agent"
|
||||
CONF_LANGUAGE_CODE: Final = "language_code"
|
||||
|
||||
DATA_MEM_STORAGE: Final = "mem_storage"
|
||||
|
||||
@@ -31,10 +31,8 @@
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"enable_conversation_agent": "Enable the conversation agent",
|
||||
"language_code": "Language code"
|
||||
},
|
||||
"description": "Set language for interactions with Google Assistant and whether you want to enable the conversation agent."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/google_generative_ai_conversation",
|
||||
"integration_type": "service",
|
||||
"iot_class": "cloud_polling",
|
||||
"requirements": ["google-generativeai==0.1.0rc2"]
|
||||
"requirements": ["google-generativeai==0.1.0"]
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ INVERTER_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = (
|
||||
),
|
||||
GrowattSensorEntityDescription(
|
||||
key="inverter_temperature",
|
||||
translation_key="inverter_energy_today",
|
||||
translation_key="inverter_temperature",
|
||||
api_key="temperature",
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
|
||||
@@ -83,7 +83,7 @@
|
||||
"name": "Intelligent Power Management temperature"
|
||||
},
|
||||
"inverter_temperature": {
|
||||
"name": "Energytoday"
|
||||
"name": "Inverter temperature"
|
||||
},
|
||||
"mix_statement_of_charge": {
|
||||
"name": "Statement of charge"
|
||||
|
||||
@@ -168,7 +168,7 @@ BRIDGE_SCHEMA = vol.All(
|
||||
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
|
||||
vol.Optional(CONF_IP_ADDRESS): vol.All(ipaddress.ip_address, cv.string),
|
||||
vol.Optional(CONF_ADVERTISE_IP): vol.All(
|
||||
cv.ensure_list, ipaddress.ip_address, cv.string
|
||||
cv.ensure_list, [ipaddress.ip_address], [cv.string]
|
||||
),
|
||||
vol.Optional(CONF_FILTER, default={}): BASE_FILTER_SCHEMA,
|
||||
vol.Optional(CONF_ENTITY_CONFIG, default={}): validate_entity_config,
|
||||
|
||||
@@ -14,6 +14,6 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/homekit_controller",
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["aiohomekit", "commentjson"],
|
||||
"requirements": ["aiohomekit==2.6.5"],
|
||||
"requirements": ["aiohomekit==2.6.7"],
|
||||
"zeroconf": ["_hap._tcp.local.", "_hap._udp.local."]
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ class HomeWizardSwitchEntityDescription(
|
||||
SWITCHES = [
|
||||
HomeWizardSwitchEntityDescription(
|
||||
key="power_on",
|
||||
name=None,
|
||||
device_class=SwitchDeviceClass.OUTLET,
|
||||
create_fn=lambda coordinator: coordinator.supports_state(),
|
||||
available_fn=lambda data: data.state is not None and not data.state.switch_lock,
|
||||
|
||||
@@ -101,6 +101,7 @@ class HoneywellUSThermostat(ClimateEntity):
|
||||
"""Representation of a Honeywell US Thermostat."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
_attr_name = None
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
||||
@@ -48,6 +48,8 @@ async def async_setup_entry(
|
||||
class IBeaconTrackerEntity(IBeaconEntity, BaseTrackerEntity):
|
||||
"""An iBeacon Tracker entity."""
|
||||
|
||||
_attr_name = None
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: IBeaconCoordinator,
|
||||
|
||||
@@ -162,6 +162,7 @@ async def async_setup_entry(
|
||||
):
|
||||
device_info = DeviceInfo(
|
||||
identifiers=device.identifiers,
|
||||
connections=device.connections,
|
||||
)
|
||||
else:
|
||||
device_info = None
|
||||
|
||||
@@ -90,6 +90,7 @@ class JellyfinMediaPlayer(JellyfinEntity, MediaPlayerEntity):
|
||||
sw_version=self.app_version,
|
||||
via_device=(DOMAIN, coordinator.server_id),
|
||||
)
|
||||
self._attr_name = None
|
||||
else:
|
||||
self._attr_device_info = None
|
||||
self._attr_has_entity_name = False
|
||||
|
||||
@@ -42,6 +42,7 @@ def _count_now_playing(data: JellyfinDataT) -> int:
|
||||
SENSOR_TYPES: dict[str, JellyfinSensorEntityDescription] = {
|
||||
"sessions": JellyfinSensorEntityDescription(
|
||||
key="watching",
|
||||
name=None,
|
||||
icon="mdi:television-play",
|
||||
native_unit_of_measurement="Watching",
|
||||
value_fn=_count_now_playing,
|
||||
|
||||
@@ -52,6 +52,8 @@ async def async_setup_entry(
|
||||
class JvcProjectorRemote(JvcProjectorEntity, RemoteEntity):
|
||||
"""Representation of a JVC Projector device."""
|
||||
|
||||
_attr_name = None
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool:
|
||||
"""Return True if entity is on."""
|
||||
|
||||
@@ -30,6 +30,7 @@ from homeassistant.const import (
|
||||
CONF_PORT,
|
||||
CONF_TYPE,
|
||||
EVENT_HOMEASSISTANT_STOP,
|
||||
SERVICE_RELOAD,
|
||||
Platform,
|
||||
)
|
||||
from homeassistant.core import Event, HomeAssistant, ServiceCall
|
||||
@@ -312,6 +313,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
schema=SERVICE_KNX_EXPOSURE_REGISTER_SCHEMA,
|
||||
)
|
||||
|
||||
async def _reload_integration(call: ServiceCall) -> None:
|
||||
"""Reload the integration."""
|
||||
await hass.config_entries.async_reload(entry.entry_id)
|
||||
hass.bus.async_fire(f"event_{DOMAIN}_reloaded", context=call.context)
|
||||
|
||||
async_register_admin_service(hass, DOMAIN, SERVICE_RELOAD, _reload_integration)
|
||||
|
||||
await register_panel(hass)
|
||||
|
||||
return True
|
||||
|
||||
@@ -106,3 +106,6 @@ exposure_register:
|
||||
default: false
|
||||
selector:
|
||||
boolean:
|
||||
reload:
|
||||
name: Reload
|
||||
description: Reload the KNX integration.
|
||||
|
||||
@@ -53,7 +53,7 @@ async def async_setup_platform(
|
||||
hass,
|
||||
DOMAIN,
|
||||
"deprecated_yaml",
|
||||
breaks_in_ha_version="2023.8.0",
|
||||
breaks_in_ha_version="2023.12.0",
|
||||
is_fixable=False,
|
||||
severity=IssueSeverity.WARNING,
|
||||
translation_key="deprecated_yaml",
|
||||
|
||||
@@ -20,5 +20,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/ld2410_ble/",
|
||||
"integration_type": "device",
|
||||
"iot_class": "local_push",
|
||||
"requirements": ["bluetooth-data-tools==1.2.0", "ld2410-ble==0.1.1"]
|
||||
"requirements": ["bluetooth-data-tools==1.3.0", "ld2410-ble==0.1.1"]
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ class LEDBLEEntity(CoordinatorEntity, LightEntity):
|
||||
|
||||
_attr_supported_color_modes = {ColorMode.RGB, ColorMode.WHITE}
|
||||
_attr_has_entity_name = True
|
||||
_attr_name = None
|
||||
_attr_supported_features = LightEntityFeature.EFFECT
|
||||
|
||||
def __init__(
|
||||
|
||||
@@ -32,5 +32,5 @@
|
||||
"dependencies": ["bluetooth_adapters"],
|
||||
"documentation": "https://www.home-assistant.io/integrations/led_ble/",
|
||||
"iot_class": "local_polling",
|
||||
"requirements": ["bluetooth-data-tools==1.2.0", "led-ble==1.0.0"]
|
||||
"requirements": ["bluetooth-data-tools==1.3.0", "led-ble==1.0.0"]
|
||||
}
|
||||
|
||||
@@ -44,9 +44,9 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
)
|
||||
cloud_client = cloud_loqed.LoqedCloudAPI(cloud_api_client)
|
||||
lock_data = await cloud_client.async_get_locks()
|
||||
except aiohttp.ClientError:
|
||||
except aiohttp.ClientError as err:
|
||||
_LOGGER.error("HTTP Connection error to loqed API")
|
||||
raise CannotConnect from aiohttp.ClientError
|
||||
raise CannotConnect from err
|
||||
|
||||
try:
|
||||
selected_lock = next(
|
||||
@@ -137,7 +137,10 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
errors["base"] = "invalid_auth"
|
||||
else:
|
||||
await self.async_set_unique_id(
|
||||
re.sub(r"LOQED-([a-f0-9]+)\.local", r"\1", info["bridge_mdns_hostname"])
|
||||
re.sub(
|
||||
r"LOQED-([a-f0-9]+)\.local", r"\1", info["bridge_mdns_hostname"]
|
||||
),
|
||||
raise_on_progress=False,
|
||||
)
|
||||
self._abort_if_unique_id_configured()
|
||||
|
||||
|
||||
@@ -2,5 +2,3 @@
|
||||
|
||||
|
||||
DOMAIN = "loqed"
|
||||
OAUTH2_AUTHORIZE = "https://app.loqed.com/API/integration_oauth3/login.php"
|
||||
OAUTH2_TOKEN = "https://app.loqed.com/API/integration_oauth3/token.php"
|
||||
|
||||
@@ -8,8 +8,8 @@ from loqedAPI import loqed
|
||||
|
||||
from homeassistant.components import webhook
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_WEBHOOK_ID
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.const import CONF_NAME, CONF_WEBHOOK_ID
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||
|
||||
from .const import DOMAIN
|
||||
@@ -79,17 +79,16 @@ class LoqedDataCoordinator(DataUpdateCoordinator[StatusMessage]):
|
||||
) -> None:
|
||||
"""Initialize the Loqed Data Update coordinator."""
|
||||
super().__init__(hass, _LOGGER, name="Loqed sensors")
|
||||
self._hass = hass
|
||||
self._api = api
|
||||
self._entry = entry
|
||||
self.lock = lock
|
||||
self.device_name = self._entry.data[CONF_NAME]
|
||||
|
||||
async def _async_update_data(self) -> StatusMessage:
|
||||
"""Fetch data from API endpoint."""
|
||||
async with async_timeout.timeout(10):
|
||||
return await self._api.async_get_lock_details()
|
||||
|
||||
@callback
|
||||
async def _handle_webhook(
|
||||
self, hass: HomeAssistant, webhook_id: str, request: Request
|
||||
) -> None:
|
||||
@@ -116,7 +115,7 @@ class LoqedDataCoordinator(DataUpdateCoordinator[StatusMessage]):
|
||||
self.hass, DOMAIN, "Loqed", webhook_id, self._handle_webhook
|
||||
)
|
||||
webhook_url = webhook.async_generate_url(self.hass, webhook_id)
|
||||
_LOGGER.info("Webhook URL: %s", webhook_url)
|
||||
_LOGGER.debug("Webhook URL: %s", webhook_url)
|
||||
|
||||
webhooks = await self.lock.getWebhooks()
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ class LoqedEntity(CoordinatorEntity[LoqedDataCoordinator]):
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, lock_id)},
|
||||
manufacturer="LOQED",
|
||||
name="LOQED Lock",
|
||||
name=coordinator.device_name,
|
||||
model="Touch Smart Lock",
|
||||
connections={(CONNECTION_NETWORK_MAC, lock_id)},
|
||||
)
|
||||
|
||||
@@ -24,7 +24,7 @@ async def async_setup_entry(
|
||||
"""Set up the Loqed lock platform."""
|
||||
coordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
async_add_entities([LoqedLock(coordinator, entry.data["name"])])
|
||||
async_add_entities([LoqedLock(coordinator)])
|
||||
|
||||
|
||||
class LoqedLock(LoqedEntity, LockEntity):
|
||||
@@ -32,17 +32,17 @@ class LoqedLock(LoqedEntity, LockEntity):
|
||||
|
||||
_attr_supported_features = LockEntityFeature.OPEN
|
||||
|
||||
def __init__(self, coordinator: LoqedDataCoordinator, name: str) -> None:
|
||||
def __init__(self, coordinator: LoqedDataCoordinator) -> None:
|
||||
"""Initialize the lock."""
|
||||
super().__init__(coordinator)
|
||||
self._lock = coordinator.lock
|
||||
self._attr_unique_id = self._lock.id
|
||||
self._attr_name = name
|
||||
self._attr_name = None
|
||||
|
||||
@property
|
||||
def changed_by(self) -> str:
|
||||
"""Return internal ID of last used key."""
|
||||
return "KeyID " + str(self._lock.last_key_id)
|
||||
return f"KeyID {self._lock.last_key_id}"
|
||||
|
||||
@property
|
||||
def is_locking(self) -> bool | None:
|
||||
|
||||
@@ -12,8 +12,7 @@
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
|
||||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]"
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Device is already configured"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Failed to connect",
|
||||
"invalid_auth": "Invalid authentication",
|
||||
"unknown": "Unexpected error"
|
||||
},
|
||||
"flow_title": "LOQED Touch Smartlock setup",
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"api_key": "API Key",
|
||||
"name": "Name of your lock in the LOQED app."
|
||||
},
|
||||
"description": "Login at {config_url} and: \n* Create an API-key by clicking 'Create' \n* Copy the created access token."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -96,20 +96,24 @@ class MatterAdapter:
|
||||
)
|
||||
|
||||
self.config_entry.async_on_unload(
|
||||
self.matter_client.subscribe(
|
||||
self.matter_client.subscribe_events(
|
||||
endpoint_added_callback, EventType.ENDPOINT_ADDED
|
||||
)
|
||||
)
|
||||
self.config_entry.async_on_unload(
|
||||
self.matter_client.subscribe(
|
||||
self.matter_client.subscribe_events(
|
||||
endpoint_removed_callback, EventType.ENDPOINT_REMOVED
|
||||
)
|
||||
)
|
||||
self.config_entry.async_on_unload(
|
||||
self.matter_client.subscribe(node_removed_callback, EventType.NODE_REMOVED)
|
||||
self.matter_client.subscribe_events(
|
||||
node_removed_callback, EventType.NODE_REMOVED
|
||||
)
|
||||
)
|
||||
self.config_entry.async_on_unload(
|
||||
self.matter_client.subscribe(node_added_callback, EventType.NODE_ADDED)
|
||||
self.matter_client.subscribe_events(
|
||||
node_added_callback, EventType.NODE_ADDED
|
||||
)
|
||||
)
|
||||
|
||||
def _setup_node(self, node: MatterNode) -> None:
|
||||
|
||||
@@ -65,7 +65,6 @@ DISCOVERY_SCHEMAS = [
|
||||
entity_description=MatterBinarySensorEntityDescription(
|
||||
key="HueMotionSensor",
|
||||
device_class=BinarySensorDeviceClass.MOTION,
|
||||
name="Motion",
|
||||
measurement_to_ha=lambda x: (x & 1 == 1) if x is not None else None,
|
||||
),
|
||||
entity_class=MatterBinarySensor,
|
||||
@@ -78,7 +77,6 @@ DISCOVERY_SCHEMAS = [
|
||||
entity_description=MatterBinarySensorEntityDescription(
|
||||
key="ContactSensor",
|
||||
device_class=BinarySensorDeviceClass.DOOR,
|
||||
name="Contact",
|
||||
# value is inverted on matter to what we expect
|
||||
measurement_to_ha=lambda x: not x,
|
||||
),
|
||||
@@ -90,7 +88,6 @@ DISCOVERY_SCHEMAS = [
|
||||
entity_description=MatterBinarySensorEntityDescription(
|
||||
key="OccupancySensor",
|
||||
device_class=BinarySensorDeviceClass.OCCUPANCY,
|
||||
name="Occupancy",
|
||||
# The first bit = if occupied
|
||||
measurement_to_ha=lambda x: (x & 1 == 1) if x is not None else None,
|
||||
),
|
||||
@@ -102,7 +99,6 @@ DISCOVERY_SCHEMAS = [
|
||||
entity_description=MatterBinarySensorEntityDescription(
|
||||
key="BatteryChargeLevel",
|
||||
device_class=BinarySensorDeviceClass.BATTERY,
|
||||
name="Battery Status",
|
||||
measurement_to_ha=lambda x: x
|
||||
!= clusters.PowerSource.Enums.BatChargeLevelEnum.kOk,
|
||||
),
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user