mirror of
https://github.com/home-assistant/core.git
synced 2025-07-12 15:57:06 +00:00
2024.12.5 (#133636)
This commit is contained in:
commit
6974f61703
@ -4,8 +4,6 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from fjaraskupan import COMMAND_LIGHT_ON_OFF
|
|
||||||
|
|
||||||
from homeassistant.components.light import ATTR_BRIGHTNESS, ColorMode, LightEntity
|
from homeassistant.components.light import ATTR_BRIGHTNESS, ColorMode, LightEntity
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
@ -62,7 +60,6 @@ class Light(CoordinatorEntity[FjaraskupanCoordinator], LightEntity):
|
|||||||
if self.is_on:
|
if self.is_on:
|
||||||
async with self.coordinator.async_connect_and_update() as device:
|
async with self.coordinator.async_connect_and_update() as device:
|
||||||
await device.send_dim(0)
|
await device.send_dim(0)
|
||||||
await device.send_command(COMMAND_LIGHT_ON_OFF)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_on(self) -> bool:
|
def is_on(self) -> bool:
|
||||||
|
@ -14,5 +14,5 @@
|
|||||||
"documentation": "https://www.home-assistant.io/integrations/fjaraskupan",
|
"documentation": "https://www.home-assistant.io/integrations/fjaraskupan",
|
||||||
"iot_class": "local_polling",
|
"iot_class": "local_polling",
|
||||||
"loggers": ["bleak", "fjaraskupan"],
|
"loggers": ["bleak", "fjaraskupan"],
|
||||||
"requirements": ["fjaraskupan==2.3.0"]
|
"requirements": ["fjaraskupan==2.3.2"]
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,6 @@
|
|||||||
"documentation": "https://www.home-assistant.io/integrations/freebox",
|
"documentation": "https://www.home-assistant.io/integrations/freebox",
|
||||||
"iot_class": "local_polling",
|
"iot_class": "local_polling",
|
||||||
"loggers": ["freebox_api"],
|
"loggers": ["freebox_api"],
|
||||||
"requirements": ["freebox-api==1.1.0"],
|
"requirements": ["freebox-api==1.2.1"],
|
||||||
"zeroconf": ["_fbx-api._tcp.local."]
|
"zeroconf": ["_fbx-api._tcp.local."]
|
||||||
}
|
}
|
||||||
|
@ -14,5 +14,5 @@
|
|||||||
"documentation": "https://www.home-assistant.io/integrations/gardena_bluetooth",
|
"documentation": "https://www.home-assistant.io/integrations/gardena_bluetooth",
|
||||||
"iot_class": "local_polling",
|
"iot_class": "local_polling",
|
||||||
"loggers": ["bleak", "bleak_esphome", "gardena_bluetooth"],
|
"loggers": ["bleak", "bleak_esphome", "gardena_bluetooth"],
|
||||||
"requirements": ["gardena-bluetooth==1.4.4"]
|
"requirements": ["gardena-bluetooth==1.5.0"]
|
||||||
}
|
}
|
||||||
|
@ -576,7 +576,7 @@ class IntegrationSensor(RestoreSensor):
|
|||||||
if (
|
if (
|
||||||
self._max_sub_interval is not None
|
self._max_sub_interval is not None
|
||||||
and source_state is not None
|
and source_state is not None
|
||||||
and (source_state_dec := _decimal_state(source_state.state))
|
and (source_state_dec := _decimal_state(source_state.state)) is not None
|
||||||
):
|
):
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
|
@ -661,7 +661,7 @@ class MQTT:
|
|||||||
self.conf.get(CONF_PORT, DEFAULT_PORT),
|
self.conf.get(CONF_PORT, DEFAULT_PORT),
|
||||||
self.conf.get(CONF_KEEPALIVE, DEFAULT_KEEPALIVE),
|
self.conf.get(CONF_KEEPALIVE, DEFAULT_KEEPALIVE),
|
||||||
)
|
)
|
||||||
except OSError as err:
|
except (OSError, mqtt.WebsocketConnectionError) as err:
|
||||||
_LOGGER.error("Failed to connect to MQTT server due to exception: %s", err)
|
_LOGGER.error("Failed to connect to MQTT server due to exception: %s", err)
|
||||||
self._async_connection_result(False)
|
self._async_connection_result(False)
|
||||||
finally:
|
finally:
|
||||||
|
@ -566,17 +566,13 @@ class MusicAssistantPlayer(MusicAssistantEntity, MediaPlayerEntity):
|
|||||||
# shuffle and repeat are not (yet) supported for external sources
|
# shuffle and repeat are not (yet) supported for external sources
|
||||||
self._attr_shuffle = None
|
self._attr_shuffle = None
|
||||||
self._attr_repeat = None
|
self._attr_repeat = None
|
||||||
if TYPE_CHECKING:
|
self._attr_media_position = int(player.elapsed_time or 0)
|
||||||
assert player.elapsed_time is not None
|
|
||||||
self._attr_media_position = int(player.elapsed_time)
|
|
||||||
self._attr_media_position_updated_at = (
|
self._attr_media_position_updated_at = (
|
||||||
utc_from_timestamp(player.elapsed_time_last_updated)
|
utc_from_timestamp(player.elapsed_time_last_updated)
|
||||||
if player.elapsed_time_last_updated
|
if player.elapsed_time_last_updated
|
||||||
else None
|
else None
|
||||||
)
|
)
|
||||||
if TYPE_CHECKING:
|
self._prev_time = player.elapsed_time or 0
|
||||||
assert player.elapsed_time is not None
|
|
||||||
self._prev_time = player.elapsed_time
|
|
||||||
return
|
return
|
||||||
|
|
||||||
if queue is None:
|
if queue is None:
|
||||||
|
@ -15,8 +15,8 @@ CONF_REFRESH_TOKEN_CREATION_TIME = "refresh_token_creation_time"
|
|||||||
REFRESH_TOKEN_EXPIRY_TIME = timedelta(days=30)
|
REFRESH_TOKEN_EXPIRY_TIME = timedelta(days=30)
|
||||||
|
|
||||||
SUPPORTED_DEVICE_TYPES = {
|
SUPPORTED_DEVICE_TYPES = {
|
||||||
Platform.LIGHT: ["WallStation"],
|
Platform.LIGHT: ["WallStation", "WallStation_ESP32"],
|
||||||
Platform.SWITCH: ["WallStation"],
|
Platform.SWITCH: ["WallStation", "WallStation_ESP32"],
|
||||||
}
|
}
|
||||||
KNOWN_UNSUPPORTED_DEVICE_TYPES = {
|
KNOWN_UNSUPPORTED_DEVICE_TYPES = {
|
||||||
Platform.LIGHT: ["Mms100"],
|
Platform.LIGHT: ["Mms100"],
|
||||||
|
@ -239,7 +239,6 @@ class NiceGOUpdateCoordinator(DataUpdateCoordinator[dict[str, NiceGODevice]]):
|
|||||||
].type, # Device type is not sent in device state update, and it can't change, so we just reuse the existing one
|
].type, # Device type is not sent in device state update, and it can't change, so we just reuse the existing one
|
||||||
BarrierState(
|
BarrierState(
|
||||||
deviceId=raw_data["deviceId"],
|
deviceId=raw_data["deviceId"],
|
||||||
desired=json.loads(raw_data["desired"]),
|
|
||||||
reported=json.loads(raw_data["reported"]),
|
reported=json.loads(raw_data["reported"]),
|
||||||
connectionState=ConnectionState(
|
connectionState=ConnectionState(
|
||||||
connected=raw_data["connectionState"]["connected"],
|
connected=raw_data["connectionState"]["connected"],
|
||||||
|
@ -21,6 +21,7 @@ from .entity import NiceGOEntity
|
|||||||
DEVICE_CLASSES = {
|
DEVICE_CLASSES = {
|
||||||
"WallStation": CoverDeviceClass.GARAGE,
|
"WallStation": CoverDeviceClass.GARAGE,
|
||||||
"Mms100": CoverDeviceClass.GATE,
|
"Mms100": CoverDeviceClass.GATE,
|
||||||
|
"WallStation_ESP32": CoverDeviceClass.GARAGE,
|
||||||
}
|
}
|
||||||
PARALLEL_UPDATES = 1
|
PARALLEL_UPDATES = 1
|
||||||
|
|
||||||
|
@ -7,5 +7,5 @@
|
|||||||
"integration_type": "hub",
|
"integration_type": "hub",
|
||||||
"iot_class": "cloud_push",
|
"iot_class": "cloud_push",
|
||||||
"loggers": ["nice_go"],
|
"loggers": ["nice_go"],
|
||||||
"requirements": ["nice-go==0.3.10"]
|
"requirements": ["nice-go==1.0.0"]
|
||||||
}
|
}
|
||||||
|
@ -76,7 +76,7 @@ class OverkizConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
for gateway in gateways:
|
for gateway in gateways:
|
||||||
if is_overkiz_gateway(gateway.id):
|
if is_overkiz_gateway(gateway.id):
|
||||||
gateway_id = gateway.id
|
gateway_id = gateway.id
|
||||||
await self.async_set_unique_id(gateway_id)
|
await self.async_set_unique_id(gateway_id, raise_on_progress=False)
|
||||||
|
|
||||||
return user_input
|
return user_input
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
"integration_type": "hub",
|
"integration_type": "hub",
|
||||||
"iot_class": "local_polling",
|
"iot_class": "local_polling",
|
||||||
"loggers": ["boto3", "botocore", "pyhumps", "pyoverkiz", "s3transfer"],
|
"loggers": ["boto3", "botocore", "pyhumps", "pyoverkiz", "s3transfer"],
|
||||||
"requirements": ["pyoverkiz==1.15.0"],
|
"requirements": ["pyoverkiz==1.15.3"],
|
||||||
"zeroconf": [
|
"zeroconf": [
|
||||||
{
|
{
|
||||||
"type": "_kizbox._tcp.local.",
|
"type": "_kizbox._tcp.local.",
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
"iot_class": "local_polling",
|
"iot_class": "local_polling",
|
||||||
"loggers": ["roborock"],
|
"loggers": ["roborock"],
|
||||||
"requirements": [
|
"requirements": [
|
||||||
"python-roborock==2.7.2",
|
"python-roborock==2.8.1",
|
||||||
"vacuum-map-parser-roborock==0.1.2"
|
"vacuum-map-parser-roborock==0.1.2"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import logging
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from screenlogicpy import ScreenLogicError, ScreenLogicGateway
|
from screenlogicpy import ScreenLogicError, ScreenLogicGateway
|
||||||
|
from screenlogicpy.const.common import ScreenLogicConnectionError
|
||||||
from screenlogicpy.const.data import SHARED_VALUES
|
from screenlogicpy.const.data import SHARED_VALUES
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
@ -64,7 +65,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ScreenLogicConfigEntry)
|
|||||||
try:
|
try:
|
||||||
await gateway.async_connect(**connect_info)
|
await gateway.async_connect(**connect_info)
|
||||||
await gateway.async_update()
|
await gateway.async_update()
|
||||||
except ScreenLogicError as ex:
|
except (ScreenLogicConnectionError, ScreenLogicError) as ex:
|
||||||
raise ConfigEntryNotReady(ex.msg) from ex
|
raise ConfigEntryNotReady(ex.msg) from ex
|
||||||
|
|
||||||
coordinator = ScreenlogicDataUpdateCoordinator(
|
coordinator = ScreenlogicDataUpdateCoordinator(
|
||||||
|
@ -45,7 +45,9 @@ class TwinklyConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
except (TimeoutError, ClientError):
|
except (TimeoutError, ClientError):
|
||||||
errors[CONF_HOST] = "cannot_connect"
|
errors[CONF_HOST] = "cannot_connect"
|
||||||
else:
|
else:
|
||||||
await self.async_set_unique_id(device_info[DEV_ID])
|
await self.async_set_unique_id(
|
||||||
|
device_info[DEV_ID], raise_on_progress=False
|
||||||
|
)
|
||||||
self._abort_if_unique_id_configured()
|
self._abort_if_unique_id_configured()
|
||||||
|
|
||||||
return self._create_entry_from_device(device_info, host)
|
return self._create_entry_from_device(device_info, host)
|
||||||
|
@ -25,7 +25,7 @@ if TYPE_CHECKING:
|
|||||||
APPLICATION_NAME: Final = "HomeAssistant"
|
APPLICATION_NAME: Final = "HomeAssistant"
|
||||||
MAJOR_VERSION: Final = 2024
|
MAJOR_VERSION: Final = 2024
|
||||||
MINOR_VERSION: Final = 12
|
MINOR_VERSION: Final = 12
|
||||||
PATCH_VERSION: Final = "4"
|
PATCH_VERSION: Final = "5"
|
||||||
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
|
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
|
||||||
__version__: Final = f"{__short_version__}.{PATCH_VERSION}"
|
__version__: Final = f"{__short_version__}.{PATCH_VERSION}"
|
||||||
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 12, 0)
|
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 12, 0)
|
||||||
|
@ -5,7 +5,7 @@ aiodiscover==2.1.0
|
|||||||
aiodns==3.2.0
|
aiodns==3.2.0
|
||||||
aiohasupervisor==0.2.1
|
aiohasupervisor==0.2.1
|
||||||
aiohttp-fast-zlib==0.2.0
|
aiohttp-fast-zlib==0.2.0
|
||||||
aiohttp==3.11.10
|
aiohttp==3.11.11
|
||||||
aiohttp_cors==0.7.0
|
aiohttp_cors==0.7.0
|
||||||
aiozoneinfo==0.2.1
|
aiozoneinfo==0.2.1
|
||||||
astral==2.2
|
astral==2.2
|
||||||
|
@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "homeassistant"
|
name = "homeassistant"
|
||||||
version = "2024.12.4"
|
version = "2024.12.5"
|
||||||
license = {text = "Apache-2.0"}
|
license = {text = "Apache-2.0"}
|
||||||
description = "Open-source home automation platform running on Python 3."
|
description = "Open-source home automation platform running on Python 3."
|
||||||
readme = "README.rst"
|
readme = "README.rst"
|
||||||
@ -29,7 +29,7 @@ dependencies = [
|
|||||||
# change behavior based on presence of supervisor. Deprecated with #127228
|
# change behavior based on presence of supervisor. Deprecated with #127228
|
||||||
# Lib can be removed with 2025.11
|
# Lib can be removed with 2025.11
|
||||||
"aiohasupervisor==0.2.1",
|
"aiohasupervisor==0.2.1",
|
||||||
"aiohttp==3.11.10",
|
"aiohttp==3.11.11",
|
||||||
"aiohttp_cors==0.7.0",
|
"aiohttp_cors==0.7.0",
|
||||||
"aiohttp-fast-zlib==0.2.0",
|
"aiohttp-fast-zlib==0.2.0",
|
||||||
"aiozoneinfo==0.2.1",
|
"aiozoneinfo==0.2.1",
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
# Home Assistant Core
|
# Home Assistant Core
|
||||||
aiodns==3.2.0
|
aiodns==3.2.0
|
||||||
aiohasupervisor==0.2.1
|
aiohasupervisor==0.2.1
|
||||||
aiohttp==3.11.10
|
aiohttp==3.11.11
|
||||||
aiohttp_cors==0.7.0
|
aiohttp_cors==0.7.0
|
||||||
aiohttp-fast-zlib==0.2.0
|
aiohttp-fast-zlib==0.2.0
|
||||||
aiozoneinfo==0.2.1
|
aiozoneinfo==0.2.1
|
||||||
|
@ -912,7 +912,7 @@ fivem-api==0.1.2
|
|||||||
fixerio==1.0.0a0
|
fixerio==1.0.0a0
|
||||||
|
|
||||||
# homeassistant.components.fjaraskupan
|
# homeassistant.components.fjaraskupan
|
||||||
fjaraskupan==2.3.0
|
fjaraskupan==2.3.2
|
||||||
|
|
||||||
# homeassistant.components.flexit_bacnet
|
# homeassistant.components.flexit_bacnet
|
||||||
flexit_bacnet==2.2.1
|
flexit_bacnet==2.2.1
|
||||||
@ -937,7 +937,7 @@ forecast-solar==4.0.0
|
|||||||
fortiosapi==1.0.5
|
fortiosapi==1.0.5
|
||||||
|
|
||||||
# homeassistant.components.freebox
|
# homeassistant.components.freebox
|
||||||
freebox-api==1.1.0
|
freebox-api==1.2.1
|
||||||
|
|
||||||
# homeassistant.components.free_mobile
|
# homeassistant.components.free_mobile
|
||||||
freesms==0.2.0
|
freesms==0.2.0
|
||||||
@ -953,7 +953,7 @@ fyta_cli==0.7.0
|
|||||||
gTTS==2.2.4
|
gTTS==2.2.4
|
||||||
|
|
||||||
# homeassistant.components.gardena_bluetooth
|
# homeassistant.components.gardena_bluetooth
|
||||||
gardena-bluetooth==1.4.4
|
gardena-bluetooth==1.5.0
|
||||||
|
|
||||||
# homeassistant.components.google_assistant_sdk
|
# homeassistant.components.google_assistant_sdk
|
||||||
gassist-text==0.0.11
|
gassist-text==0.0.11
|
||||||
@ -1460,7 +1460,7 @@ nextdns==4.0.0
|
|||||||
nibe==2.13.0
|
nibe==2.13.0
|
||||||
|
|
||||||
# homeassistant.components.nice_go
|
# homeassistant.components.nice_go
|
||||||
nice-go==0.3.10
|
nice-go==1.0.0
|
||||||
|
|
||||||
# homeassistant.components.niko_home_control
|
# homeassistant.components.niko_home_control
|
||||||
niko-home-control==0.2.1
|
niko-home-control==0.2.1
|
||||||
@ -2149,7 +2149,7 @@ pyotgw==2.2.2
|
|||||||
pyotp==2.8.0
|
pyotp==2.8.0
|
||||||
|
|
||||||
# homeassistant.components.overkiz
|
# homeassistant.components.overkiz
|
||||||
pyoverkiz==1.15.0
|
pyoverkiz==1.15.3
|
||||||
|
|
||||||
# homeassistant.components.onewire
|
# homeassistant.components.onewire
|
||||||
pyownet==0.10.0.post1
|
pyownet==0.10.0.post1
|
||||||
@ -2402,7 +2402,7 @@ python-rabbitair==0.0.8
|
|||||||
python-ripple-api==0.0.3
|
python-ripple-api==0.0.3
|
||||||
|
|
||||||
# homeassistant.components.roborock
|
# homeassistant.components.roborock
|
||||||
python-roborock==2.7.2
|
python-roborock==2.8.1
|
||||||
|
|
||||||
# homeassistant.components.smarttub
|
# homeassistant.components.smarttub
|
||||||
python-smarttub==0.0.38
|
python-smarttub==0.0.38
|
||||||
|
@ -771,7 +771,7 @@ fitbit==0.3.1
|
|||||||
fivem-api==0.1.2
|
fivem-api==0.1.2
|
||||||
|
|
||||||
# homeassistant.components.fjaraskupan
|
# homeassistant.components.fjaraskupan
|
||||||
fjaraskupan==2.3.0
|
fjaraskupan==2.3.2
|
||||||
|
|
||||||
# homeassistant.components.flexit_bacnet
|
# homeassistant.components.flexit_bacnet
|
||||||
flexit_bacnet==2.2.1
|
flexit_bacnet==2.2.1
|
||||||
@ -793,7 +793,7 @@ foobot_async==1.0.0
|
|||||||
forecast-solar==4.0.0
|
forecast-solar==4.0.0
|
||||||
|
|
||||||
# homeassistant.components.freebox
|
# homeassistant.components.freebox
|
||||||
freebox-api==1.1.0
|
freebox-api==1.2.1
|
||||||
|
|
||||||
# homeassistant.components.fritz
|
# homeassistant.components.fritz
|
||||||
# homeassistant.components.fritzbox_callmonitor
|
# homeassistant.components.fritzbox_callmonitor
|
||||||
@ -806,7 +806,7 @@ fyta_cli==0.7.0
|
|||||||
gTTS==2.2.4
|
gTTS==2.2.4
|
||||||
|
|
||||||
# homeassistant.components.gardena_bluetooth
|
# homeassistant.components.gardena_bluetooth
|
||||||
gardena-bluetooth==1.4.4
|
gardena-bluetooth==1.5.0
|
||||||
|
|
||||||
# homeassistant.components.google_assistant_sdk
|
# homeassistant.components.google_assistant_sdk
|
||||||
gassist-text==0.0.11
|
gassist-text==0.0.11
|
||||||
@ -1220,7 +1220,7 @@ nextdns==4.0.0
|
|||||||
nibe==2.13.0
|
nibe==2.13.0
|
||||||
|
|
||||||
# homeassistant.components.nice_go
|
# homeassistant.components.nice_go
|
||||||
nice-go==0.3.10
|
nice-go==1.0.0
|
||||||
|
|
||||||
# homeassistant.components.nfandroidtv
|
# homeassistant.components.nfandroidtv
|
||||||
notifications-android-tv==0.1.5
|
notifications-android-tv==0.1.5
|
||||||
@ -1736,7 +1736,7 @@ pyotgw==2.2.2
|
|||||||
pyotp==2.8.0
|
pyotp==2.8.0
|
||||||
|
|
||||||
# homeassistant.components.overkiz
|
# homeassistant.components.overkiz
|
||||||
pyoverkiz==1.15.0
|
pyoverkiz==1.15.3
|
||||||
|
|
||||||
# homeassistant.components.onewire
|
# homeassistant.components.onewire
|
||||||
pyownet==0.10.0.post1
|
pyownet==0.10.0.post1
|
||||||
@ -1923,7 +1923,7 @@ python-picnic-api==1.1.0
|
|||||||
python-rabbitair==0.0.8
|
python-rabbitair==0.0.8
|
||||||
|
|
||||||
# homeassistant.components.roborock
|
# homeassistant.components.roborock
|
||||||
python-roborock==2.7.2
|
python-roborock==2.8.1
|
||||||
|
|
||||||
# homeassistant.components.smarttub
|
# homeassistant.components.smarttub
|
||||||
python-smarttub==0.0.38
|
python-smarttub==0.0.38
|
||||||
|
@ -843,6 +843,39 @@ async def test_on_valid_source_expect_update_on_time(
|
|||||||
assert float(state.state) < 1.8
|
assert float(state.state) < 1.8
|
||||||
|
|
||||||
|
|
||||||
|
async def test_on_0_source_expect_0_and_update_when_source_gets_positive(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
) -> None:
|
||||||
|
"""Test whether time based integration updates the integral on a valid zero source."""
|
||||||
|
start_time = dt_util.utcnow()
|
||||||
|
|
||||||
|
with freeze_time(start_time) as freezer:
|
||||||
|
await _setup_integral_sensor(hass, max_sub_interval=DEFAULT_MAX_SUB_INTERVAL)
|
||||||
|
await _update_source_sensor(hass, 0)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
# wait one minute and one second
|
||||||
|
freezer.tick(61)
|
||||||
|
async_fire_time_changed(hass, dt_util.now())
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get("sensor.integration")
|
||||||
|
|
||||||
|
assert condition.async_numeric_state(hass, state) is True
|
||||||
|
assert float(state.state) == 0 # integral is 0 after integration of 0
|
||||||
|
|
||||||
|
# wait one second and update state
|
||||||
|
freezer.tick(1)
|
||||||
|
async_fire_time_changed(hass, dt_util.now())
|
||||||
|
await _update_source_sensor(hass, 100)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get("sensor.integration")
|
||||||
|
|
||||||
|
# approx 100*1/3600 (right method after 1 second since last integration)
|
||||||
|
assert 0.027 < float(state.state) < 0.029
|
||||||
|
|
||||||
|
|
||||||
async def test_on_unvailable_source_expect_no_update_on_time(
|
async def test_on_unvailable_source_expect_no_update_on_time(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
@ -1403,8 +1403,15 @@ async def test_handle_mqtt_timeout_on_callback(
|
|||||||
assert not mock_debouncer.is_set()
|
assert not mock_debouncer.is_set()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"exception",
|
||||||
|
[
|
||||||
|
OSError("Connection error"),
|
||||||
|
paho_mqtt.WebsocketConnectionError("Connection error"),
|
||||||
|
],
|
||||||
|
)
|
||||||
async def test_setup_raises_config_entry_not_ready_if_no_connect_broker(
|
async def test_setup_raises_config_entry_not_ready_if_no_connect_broker(
|
||||||
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
|
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, exception: Exception
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test for setup failure if connection to broker is missing."""
|
"""Test for setup failure if connection to broker is missing."""
|
||||||
entry = MockConfigEntry(domain=mqtt.DOMAIN, data={mqtt.CONF_BROKER: "test-broker"})
|
entry = MockConfigEntry(domain=mqtt.DOMAIN, data={mqtt.CONF_BROKER: "test-broker"})
|
||||||
@ -1413,7 +1420,7 @@ async def test_setup_raises_config_entry_not_ready_if_no_connect_broker(
|
|||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.mqtt.async_client.AsyncMQTTClient"
|
"homeassistant.components.mqtt.async_client.AsyncMQTTClient"
|
||||||
) as mock_client:
|
) as mock_client:
|
||||||
mock_client().connect = MagicMock(side_effect=OSError("Connection error"))
|
mock_client().connect = MagicMock(side_effect=exception)
|
||||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert "Failed to connect to MQTT server due to exception:" in caplog.text
|
assert "Failed to connect to MQTT server due to exception:" in caplog.text
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
"power",
|
"power",
|
||||||
"enqueue"
|
"enqueue"
|
||||||
],
|
],
|
||||||
"elapsed_time": 0,
|
"elapsed_time": null,
|
||||||
"elapsed_time_last_updated": 0,
|
"elapsed_time_last_updated": 0,
|
||||||
"state": "idle",
|
"state": "idle",
|
||||||
"volume_level": 20,
|
"volume_level": 20,
|
||||||
|
@ -11,7 +11,6 @@
|
|||||||
],
|
],
|
||||||
"state": {
|
"state": {
|
||||||
"deviceId": "1",
|
"deviceId": "1",
|
||||||
"desired": { "key": "value" },
|
|
||||||
"reported": {
|
"reported": {
|
||||||
"displayName": "Test Garage 1",
|
"displayName": "Test Garage 1",
|
||||||
"autoDisabled": false,
|
"autoDisabled": false,
|
||||||
@ -42,7 +41,6 @@
|
|||||||
],
|
],
|
||||||
"state": {
|
"state": {
|
||||||
"deviceId": "2",
|
"deviceId": "2",
|
||||||
"desired": { "key": "value" },
|
|
||||||
"reported": {
|
"reported": {
|
||||||
"displayName": "Test Garage 2",
|
"displayName": "Test Garage 2",
|
||||||
"autoDisabled": false,
|
"autoDisabled": false,
|
||||||
@ -73,7 +71,6 @@
|
|||||||
],
|
],
|
||||||
"state": {
|
"state": {
|
||||||
"deviceId": "3",
|
"deviceId": "3",
|
||||||
"desired": { "key": "value" },
|
|
||||||
"reported": {
|
"reported": {
|
||||||
"displayName": "Test Garage 3",
|
"displayName": "Test Garage 3",
|
||||||
"autoDisabled": false,
|
"autoDisabled": false,
|
||||||
@ -101,7 +98,6 @@
|
|||||||
],
|
],
|
||||||
"state": {
|
"state": {
|
||||||
"deviceId": "4",
|
"deviceId": "4",
|
||||||
"desired": { "key": "value" },
|
|
||||||
"reported": {
|
"reported": {
|
||||||
"displayName": "Test Garage 4",
|
"displayName": "Test Garage 4",
|
||||||
"autoDisabled": false,
|
"autoDisabled": false,
|
||||||
|
@ -81,7 +81,6 @@ async def test_firmware_update_required(
|
|||||||
"displayName": "test-display-name",
|
"displayName": "test-display-name",
|
||||||
"migrationStatus": "NOT_STARTED",
|
"migrationStatus": "NOT_STARTED",
|
||||||
},
|
},
|
||||||
desired=None,
|
|
||||||
connectionState=None,
|
connectionState=None,
|
||||||
version=None,
|
version=None,
|
||||||
timestamp=None,
|
timestamp=None,
|
||||||
|
@ -4,12 +4,14 @@ from dataclasses import dataclass
|
|||||||
from unittest.mock import DEFAULT, patch
|
from unittest.mock import DEFAULT, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from screenlogicpy import ScreenLogicGateway
|
from screenlogicpy import ScreenLogicError, ScreenLogicGateway
|
||||||
|
from screenlogicpy.const.common import ScreenLogicConnectionError
|
||||||
|
|
||||||
from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN
|
from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN
|
||||||
from homeassistant.components.number import DOMAIN as NUMBER_DOMAIN
|
from homeassistant.components.number import DOMAIN as NUMBER_DOMAIN
|
||||||
from homeassistant.components.screenlogic import DOMAIN
|
from homeassistant.components.screenlogic import DOMAIN
|
||||||
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
||||||
|
from homeassistant.config_entries import ConfigEntryState
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||||
from homeassistant.util import slugify
|
from homeassistant.util import slugify
|
||||||
@ -284,3 +286,35 @@ async def test_platform_setup(
|
|||||||
|
|
||||||
for entity_id in tested_entity_ids:
|
for entity_id in tested_entity_ids:
|
||||||
assert hass.states.get(entity_id) is not None
|
assert hass.states.get(entity_id) is not None
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"exception",
|
||||||
|
[ScreenLogicConnectionError, ScreenLogicError],
|
||||||
|
)
|
||||||
|
async def test_retry_on_connect_exception(
|
||||||
|
hass: HomeAssistant, mock_config_entry: MockConfigEntry, exception: Exception
|
||||||
|
) -> None:
|
||||||
|
"""Test setup retries on expected exceptions."""
|
||||||
|
|
||||||
|
def stub_connect(*args, **kwargs):
|
||||||
|
raise exception
|
||||||
|
|
||||||
|
mock_config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
with (
|
||||||
|
patch(
|
||||||
|
GATEWAY_DISCOVERY_IMPORT_PATH,
|
||||||
|
return_value={},
|
||||||
|
),
|
||||||
|
patch.multiple(
|
||||||
|
ScreenLogicGateway,
|
||||||
|
async_connect=stub_connect,
|
||||||
|
is_connected=False,
|
||||||
|
_async_connected_request=DEFAULT,
|
||||||
|
),
|
||||||
|
):
|
||||||
|
assert not await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY
|
||||||
|
@ -5,6 +5,7 @@ from unittest.mock import patch
|
|||||||
from homeassistant import config_entries
|
from homeassistant import config_entries
|
||||||
from homeassistant.components import dhcp
|
from homeassistant.components import dhcp
|
||||||
from homeassistant.components.twinkly.const import DOMAIN as TWINKLY_DOMAIN
|
from homeassistant.components.twinkly.const import DOMAIN as TWINKLY_DOMAIN
|
||||||
|
from homeassistant.config_entries import SOURCE_USER
|
||||||
from homeassistant.const import CONF_HOST, CONF_ID, CONF_MODEL, CONF_NAME
|
from homeassistant.const import CONF_HOST, CONF_ID, CONF_MODEL, CONF_NAME
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.data_entry_flow import FlowResultType
|
from homeassistant.data_entry_flow import FlowResultType
|
||||||
@ -157,3 +158,39 @@ async def test_dhcp_already_exists(hass: HomeAssistant) -> None:
|
|||||||
|
|
||||||
assert result["type"] is FlowResultType.ABORT
|
assert result["type"] is FlowResultType.ABORT
|
||||||
assert result["reason"] == "already_configured"
|
assert result["reason"] == "already_configured"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_user_flow_works_discovery(hass: HomeAssistant) -> None:
|
||||||
|
"""Test user flow can continue after discovery happened."""
|
||||||
|
client = ClientMock()
|
||||||
|
with (
|
||||||
|
patch(
|
||||||
|
"homeassistant.components.twinkly.config_flow.Twinkly", return_value=client
|
||||||
|
),
|
||||||
|
patch("homeassistant.components.twinkly.async_setup_entry", return_value=True),
|
||||||
|
):
|
||||||
|
await hass.config_entries.flow.async_init(
|
||||||
|
TWINKLY_DOMAIN,
|
||||||
|
context={"source": config_entries.SOURCE_DHCP},
|
||||||
|
data=dhcp.DhcpServiceInfo(
|
||||||
|
hostname="Twinkly_XYZ",
|
||||||
|
ip="1.2.3.4",
|
||||||
|
macaddress="aabbccddeeff",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
TWINKLY_DOMAIN,
|
||||||
|
context={"source": SOURCE_USER},
|
||||||
|
)
|
||||||
|
assert len(hass.config_entries.flow.async_progress(TWINKLY_DOMAIN)) == 2
|
||||||
|
assert result["type"] is FlowResultType.FORM
|
||||||
|
assert result["step_id"] == "user"
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
{CONF_HOST: "10.0.0.131"},
|
||||||
|
)
|
||||||
|
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||||
|
|
||||||
|
# Verify the discovery flow was aborted
|
||||||
|
assert not hass.config_entries.flow.async_progress(TWINKLY_DOMAIN)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user