mirror of
https://github.com/home-assistant/core.git
synced 2025-07-18 18:57:06 +00:00
Merge pull request #54853 from home-assistant/rc
This commit is contained in:
commit
14b74fbf71
@ -2,7 +2,7 @@
|
||||
"domain": "bmw_connected_drive",
|
||||
"name": "BMW Connected Drive",
|
||||
"documentation": "https://www.home-assistant.io/integrations/bmw_connected_drive",
|
||||
"requirements": ["bimmer_connected==0.7.18"],
|
||||
"requirements": ["bimmer_connected==0.7.19"],
|
||||
"codeowners": ["@gerard33", "@rikroe"],
|
||||
"config_flow": true,
|
||||
"iot_class": "cloud_polling"
|
||||
|
@ -134,10 +134,15 @@ def get_accessory(hass, driver, state, aid, config): # noqa: C901
|
||||
and features & cover.SUPPORT_SET_POSITION
|
||||
):
|
||||
a_type = "Window"
|
||||
elif features & (cover.SUPPORT_SET_POSITION | cover.SUPPORT_SET_TILT_POSITION):
|
||||
elif features & cover.SUPPORT_SET_POSITION:
|
||||
a_type = "WindowCovering"
|
||||
elif features & (cover.SUPPORT_OPEN | cover.SUPPORT_CLOSE):
|
||||
a_type = "WindowCoveringBasic"
|
||||
elif features & cover.SUPPORT_SET_TILT_POSITION:
|
||||
# WindowCovering and WindowCoveringBasic both support tilt
|
||||
# only WindowCovering can handle the covers that are missing
|
||||
# SUPPORT_SET_POSITION, SUPPORT_OPEN, and SUPPORT_CLOSE
|
||||
a_type = "WindowCovering"
|
||||
|
||||
elif state.domain == "fan":
|
||||
a_type = "Fan"
|
||||
|
@ -3,7 +3,7 @@
|
||||
"name": "Met Éireann",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/met_eireann",
|
||||
"requirements": ["pyMetEireann==0.2"],
|
||||
"requirements": ["pyMetEireann==2021.8.0"],
|
||||
"codeowners": ["@DylanGore"],
|
||||
"iot_class": "cloud_polling"
|
||||
}
|
||||
|
@ -28,9 +28,14 @@ import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.event import async_track_time_interval
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||
from homeassistant.util.dt import utc_from_timestamp
|
||||
from homeassistant.util.dt import as_local, utc_from_timestamp
|
||||
|
||||
from .common import SmartDevices, async_discover_devices, get_static_devices
|
||||
from .common import (
|
||||
SmartDevices,
|
||||
async_discover_devices,
|
||||
get_static_devices,
|
||||
get_time_offset,
|
||||
)
|
||||
from .const import (
|
||||
ATTR_CONFIG,
|
||||
ATTR_CURRENT_A,
|
||||
@ -156,7 +161,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
|
||||
for device in unavailable_devices:
|
||||
try:
|
||||
device.get_sysinfo()
|
||||
await hass.async_add_executor_job(device.get_sysinfo)
|
||||
except SmartDeviceException:
|
||||
continue
|
||||
_LOGGER.debug(
|
||||
@ -170,7 +175,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
for switch in switches:
|
||||
|
||||
try:
|
||||
await hass.async_add_executor_job(switch.get_sysinfo)
|
||||
info = await hass.async_add_executor_job(switch.get_sysinfo)
|
||||
except SmartDeviceException:
|
||||
_LOGGER.warning(
|
||||
"Device at '%s' not reachable during setup, will retry later",
|
||||
@ -181,7 +186,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
|
||||
hass_data[COORDINATORS][
|
||||
switch.context or switch.mac
|
||||
] = coordinator = SmartPlugDataUpdateCoordinator(hass, switch)
|
||||
] = coordinator = SmartPlugDataUpdateCoordinator(hass, switch, info["alias"])
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
|
||||
if unavailable_devices:
|
||||
@ -217,16 +222,20 @@ class SmartPlugDataUpdateCoordinator(DataUpdateCoordinator):
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
smartplug: SmartPlug,
|
||||
alias: str,
|
||||
) -> None:
|
||||
"""Initialize DataUpdateCoordinator to gather data for specific SmartPlug."""
|
||||
self.smartplug = smartplug
|
||||
|
||||
update_interval = timedelta(seconds=30)
|
||||
super().__init__(
|
||||
hass, _LOGGER, name=smartplug.alias, update_interval=update_interval
|
||||
hass,
|
||||
_LOGGER,
|
||||
name=alias,
|
||||
update_interval=update_interval,
|
||||
)
|
||||
|
||||
async def _async_update_data(self) -> dict:
|
||||
def _update_data(self) -> dict:
|
||||
"""Fetch all device and sensor data from api."""
|
||||
try:
|
||||
info = self.smartplug.sys_info
|
||||
@ -239,9 +248,7 @@ class SmartPlugDataUpdateCoordinator(DataUpdateCoordinator):
|
||||
if self.smartplug.context is None:
|
||||
data[CONF_ALIAS] = info["alias"]
|
||||
data[CONF_DEVICE_ID] = info["mac"]
|
||||
data[CONF_STATE] = (
|
||||
self.smartplug.state == self.smartplug.SWITCH_STATE_ON
|
||||
)
|
||||
data[CONF_STATE] = bool(info["relay_state"])
|
||||
else:
|
||||
plug_from_context = next(
|
||||
c
|
||||
@ -251,7 +258,9 @@ class SmartPlugDataUpdateCoordinator(DataUpdateCoordinator):
|
||||
data[CONF_ALIAS] = plug_from_context["alias"]
|
||||
data[CONF_DEVICE_ID] = self.smartplug.context
|
||||
data[CONF_STATE] = plug_from_context["state"] == 1
|
||||
if self.smartplug.has_emeter:
|
||||
|
||||
# Check if the device has emeter
|
||||
if "ENE" in info["feature"]:
|
||||
emeter_readings = self.smartplug.get_emeter_realtime()
|
||||
data[CONF_EMETER_PARAMS] = {
|
||||
ATTR_CURRENT_POWER_W: round(float(emeter_readings["power"]), 2),
|
||||
@ -261,9 +270,16 @@ class SmartPlugDataUpdateCoordinator(DataUpdateCoordinator):
|
||||
ATTR_LAST_RESET: {ATTR_TOTAL_ENERGY_KWH: utc_from_timestamp(0)},
|
||||
}
|
||||
emeter_statics = self.smartplug.get_emeter_daily()
|
||||
last_reset = datetime.now() - get_time_offset(self.smartplug)
|
||||
last_reset_local = as_local(last_reset.replace(second=0, microsecond=0))
|
||||
_LOGGER.debug(
|
||||
"%s last reset time as local to server is %s",
|
||||
self.smartplug.alias,
|
||||
last_reset_local.strftime("%Y/%m/%d %H:%M:%S"),
|
||||
)
|
||||
data[CONF_EMETER_PARAMS][ATTR_LAST_RESET][
|
||||
ATTR_TODAY_ENERGY_KWH
|
||||
] = datetime.utcnow().replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
] = last_reset_local
|
||||
if emeter_statics.get(int(time.strftime("%e"))):
|
||||
data[CONF_EMETER_PARAMS][ATTR_TODAY_ENERGY_KWH] = round(
|
||||
float(emeter_statics[int(time.strftime("%e"))]), 3
|
||||
@ -276,3 +292,7 @@ class SmartPlugDataUpdateCoordinator(DataUpdateCoordinator):
|
||||
|
||||
self.name = data[CONF_ALIAS]
|
||||
return data
|
||||
|
||||
async def _async_update_data(self) -> dict:
|
||||
"""Fetch all device and sensor data from api."""
|
||||
return await self.hass.async_add_executor_job(self._update_data)
|
||||
|
@ -1,6 +1,7 @@
|
||||
"""Common code for tplink."""
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
from typing import Callable
|
||||
|
||||
@ -184,3 +185,16 @@ def add_available_devices(
|
||||
|
||||
hass.data[TPLINK_DOMAIN][f"{device_type}_remaining"] = devices_unavailable
|
||||
return entities_ready
|
||||
|
||||
|
||||
def get_time_offset(device: SmartDevice) -> timedelta:
|
||||
"""Get the time offset since last device reset (local midnight)."""
|
||||
device_time = device.time.replace(microsecond=0)
|
||||
offset = device_time - device_time.replace(hour=0, minute=0, second=0)
|
||||
_LOGGER.debug(
|
||||
"%s local time is %s, offset from midnight is %s",
|
||||
device.alias,
|
||||
device_time.strftime("%Y/%m/%d %H:%M:%S"),
|
||||
str(offset),
|
||||
)
|
||||
return offset
|
||||
|
@ -1,6 +1,7 @@
|
||||
"""Support for TPLink HS100/HS110/HS200 smart switch energy sensors."""
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Any, Final
|
||||
|
||||
from pyHS100 import SmartPlug
|
||||
@ -156,3 +157,10 @@ class SmartPlugSensor(CoordinatorEntity, SensorEntity):
|
||||
"connections": {(dr.CONNECTION_NETWORK_MAC, self.data[CONF_MAC])},
|
||||
"sw_version": self.data[CONF_SW_VERSION],
|
||||
}
|
||||
|
||||
@property
|
||||
def last_reset(self) -> datetime | None:
|
||||
"""Return the last reset time for emeter."""
|
||||
return self.data[CONF_EMETER_PARAMS][ATTR_LAST_RESET].get(
|
||||
self.entity_description.key
|
||||
)
|
||||
|
@ -46,7 +46,7 @@ def _async_setup_entities(devices, async_add_entities):
|
||||
for dev in devices:
|
||||
if DEV_TYPE_TO_HA.get(dev.device_type) in ("walldimmer", "bulb-dimmable"):
|
||||
entities.append(VeSyncDimmableLightHA(dev))
|
||||
elif DEV_TYPE_TO_HA.get(dev.device_type) in ("bulb-tunable-white"):
|
||||
elif DEV_TYPE_TO_HA.get(dev.device_type) in ("bulb-tunable-white",):
|
||||
entities.append(VeSyncTunableWhiteLightHA(dev))
|
||||
else:
|
||||
_LOGGER.debug(
|
||||
@ -82,7 +82,7 @@ class VeSyncBaseLight(VeSyncDevice, LightEntity):
|
||||
"""Turn the device on."""
|
||||
attribute_adjustment_only = False
|
||||
# set white temperature
|
||||
if self.color_mode in (COLOR_MODE_COLOR_TEMP) and ATTR_COLOR_TEMP in kwargs:
|
||||
if self.color_mode in (COLOR_MODE_COLOR_TEMP,) and ATTR_COLOR_TEMP in kwargs:
|
||||
# get white temperature from HA data
|
||||
color_temp = int(kwargs[ATTR_COLOR_TEMP])
|
||||
# ensure value between min-max supported Mireds
|
||||
|
@ -5,7 +5,7 @@ from typing import Final
|
||||
|
||||
MAJOR_VERSION: Final = 2021
|
||||
MINOR_VERSION: Final = 8
|
||||
PATCH_VERSION: Final = "7"
|
||||
PATCH_VERSION: Final = "8"
|
||||
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
|
||||
__version__: Final = f"{__short_version__}.{PATCH_VERSION}"
|
||||
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 8, 0)
|
||||
|
@ -365,7 +365,7 @@ beautifulsoup4==4.9.3
|
||||
bellows==0.26.0
|
||||
|
||||
# homeassistant.components.bmw_connected_drive
|
||||
bimmer_connected==0.7.18
|
||||
bimmer_connected==0.7.19
|
||||
|
||||
# homeassistant.components.bizkaibus
|
||||
bizkaibus==0.1.1
|
||||
@ -1272,7 +1272,7 @@ pyControl4==0.0.6
|
||||
pyHS100==0.3.5.2
|
||||
|
||||
# homeassistant.components.met_eireann
|
||||
pyMetEireann==0.2
|
||||
pyMetEireann==2021.8.0
|
||||
|
||||
# homeassistant.components.met
|
||||
# homeassistant.components.norway_air
|
||||
|
@ -220,7 +220,7 @@ base36==0.1.1
|
||||
bellows==0.26.0
|
||||
|
||||
# homeassistant.components.bmw_connected_drive
|
||||
bimmer_connected==0.7.18
|
||||
bimmer_connected==0.7.19
|
||||
|
||||
# homeassistant.components.blebox
|
||||
blebox_uniapi==1.3.3
|
||||
@ -714,7 +714,7 @@ pyControl4==0.0.6
|
||||
pyHS100==0.3.5.2
|
||||
|
||||
# homeassistant.components.met_eireann
|
||||
pyMetEireann==0.2
|
||||
pyMetEireann==2021.8.0
|
||||
|
||||
# homeassistant.components.met
|
||||
# homeassistant.components.norway_air
|
||||
|
@ -149,6 +149,18 @@ def test_types(type_name, entity_id, state, attrs, config):
|
||||
"open",
|
||||
{ATTR_SUPPORTED_FEATURES: (cover.SUPPORT_OPEN | cover.SUPPORT_CLOSE)},
|
||||
),
|
||||
(
|
||||
"WindowCoveringBasic",
|
||||
"cover.open_window",
|
||||
"open",
|
||||
{
|
||||
ATTR_SUPPORTED_FEATURES: (
|
||||
cover.SUPPORT_OPEN
|
||||
| cover.SUPPORT_CLOSE
|
||||
| cover.SUPPORT_SET_TILT_POSITION
|
||||
)
|
||||
},
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_type_covers(type_name, entity_id, state, attrs):
|
||||
|
@ -1,6 +1,7 @@
|
||||
"""Tests for the TP-Link component."""
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
import time
|
||||
from typing import Any
|
||||
from unittest.mock import MagicMock, patch
|
||||
@ -222,6 +223,11 @@ async def test_platforms_are_initialized(hass: HomeAssistant):
|
||||
), patch(
|
||||
"homeassistant.components.tplink.common.SmartPlug.is_dimmable",
|
||||
False,
|
||||
), patch(
|
||||
"homeassistant.components.tplink.get_time_offset",
|
||||
return_value=(
|
||||
datetime.now() - datetime.now().replace(hour=0, minute=0, second=0)
|
||||
),
|
||||
):
|
||||
|
||||
light = SmartBulb("123.123.123.123")
|
||||
@ -412,7 +418,12 @@ async def test_unload(hass, platform):
|
||||
), patch(
|
||||
f"homeassistant.components.tplink.{platform}.async_setup_entry",
|
||||
return_value=mock_coro(True),
|
||||
) as async_setup_entry:
|
||||
) as async_setup_entry, patch(
|
||||
"homeassistant.components.tplink.get_time_offset",
|
||||
return_value=(
|
||||
datetime.now() - datetime.now().replace(hour=0, minute=0, second=0)
|
||||
),
|
||||
):
|
||||
config = {
|
||||
tplink.DOMAIN: {
|
||||
platform: [{CONF_HOST: "123.123.123.123"}],
|
||||
|
Loading…
x
Reference in New Issue
Block a user