diff --git a/homeassistant/components/bmw_connected_drive/manifest.json b/homeassistant/components/bmw_connected_drive/manifest.json index 8131ac1415c313..a7c4c5c837b4d4 100644 --- a/homeassistant/components/bmw_connected_drive/manifest.json +++ b/homeassistant/components/bmw_connected_drive/manifest.json @@ -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" diff --git a/homeassistant/components/homekit/accessories.py b/homeassistant/components/homekit/accessories.py index 836221ac7e93b9..7edeb7179eb04e 100644 --- a/homeassistant/components/homekit/accessories.py +++ b/homeassistant/components/homekit/accessories.py @@ -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" diff --git a/homeassistant/components/met_eireann/manifest.json b/homeassistant/components/met_eireann/manifest.json index 9d2e1857689d9f..36cc905eabf45c 100644 --- a/homeassistant/components/met_eireann/manifest.json +++ b/homeassistant/components/met_eireann/manifest.json @@ -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" } diff --git a/homeassistant/components/tplink/__init__.py b/homeassistant/components/tplink/__init__.py index 552e5666db89e1..64637b9cdb5236 100644 --- a/homeassistant/components/tplink/__init__.py +++ b/homeassistant/components/tplink/__init__.py @@ -28,9 +28,14 @@ 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_retry_devices(self) -> None: 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_retry_devices(self) -> None: 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_retry_devices(self) -> None: 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 @@ def __init__( 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 @@ async def _async_update_data(self) -> dict: 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 @@ async def _async_update_data(self) -> dict: 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 @@ async def _async_update_data(self) -> dict: 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 @@ async def _async_update_data(self) -> dict: 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) diff --git a/homeassistant/components/tplink/common.py b/homeassistant/components/tplink/common.py index 6f6fb0a14c2d36..8acd6f29cba974 100644 --- a/homeassistant/components/tplink/common.py +++ b/homeassistant/components/tplink/common.py @@ -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 diff --git a/homeassistant/components/tplink/sensor.py b/homeassistant/components/tplink/sensor.py index 697641915f7a05..3d7f26bb786791 100644 --- a/homeassistant/components/tplink/sensor.py +++ b/homeassistant/components/tplink/sensor.py @@ -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 @@ def device_info(self) -> DeviceInfo: "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 + ) diff --git a/homeassistant/components/vesync/light.py b/homeassistant/components/vesync/light.py index b747c10ee4e7c8..bd187f2f5904eb 100644 --- a/homeassistant/components/vesync/light.py +++ b/homeassistant/components/vesync/light.py @@ -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 @@ def turn_on(self, **kwargs): """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 diff --git a/homeassistant/const.py b/homeassistant/const.py index c96f621ade2c80..667c9c11389dec 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -5,7 +5,7 @@ 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) diff --git a/requirements_all.txt b/requirements_all.txt index 2d026810c3f440..74b0e375d7167c 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -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 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index f1619c2319c1b2..67573604d730a1 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -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 diff --git a/tests/components/homekit/test_get_accessories.py b/tests/components/homekit/test_get_accessories.py index 1b220153195a99..af98f6a45f9fdc 100644 --- a/tests/components/homekit/test_get_accessories.py +++ b/tests/components/homekit/test_get_accessories.py @@ -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): diff --git a/tests/components/tplink/test_init.py b/tests/components/tplink/test_init.py index d96d6846939a0e..6139fae9b11b7a 100644 --- a/tests/components/tplink/test_init.py +++ b/tests/components/tplink/test_init.py @@ -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"}],