mirror of
https://github.com/home-assistant/core.git
synced 2026-01-11 17:48:37 +00:00
Compare commits
18 Commits
bump-front
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7f4063f91e | ||
|
|
080ba46885 | ||
|
|
2cb028ee79 | ||
|
|
72655dbf0b | ||
|
|
153278221d | ||
|
|
4942ce7e86 | ||
|
|
98e918cd8a | ||
|
|
1efc87bfef | ||
|
|
b4360ccbd9 | ||
|
|
ce234d69a7 | ||
|
|
b2a198e230 | ||
|
|
538009d2df | ||
|
|
99329851a2 | ||
|
|
f8ec395e96 | ||
|
|
98fe189edf | ||
|
|
7b413e3fd3 | ||
|
|
00ca5473d4 | ||
|
|
33c808713e |
@@ -43,6 +43,13 @@ BUTTON_TYPES: tuple[AirobotButtonEntityDescription, ...] = (
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
press_fn=lambda coordinator: coordinator.client.reboot_thermostat(),
|
||||
),
|
||||
AirobotButtonEntityDescription(
|
||||
key="recalibrate_co2",
|
||||
translation_key="recalibrate_co2",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
entity_registry_enabled_default=False,
|
||||
press_fn=lambda coordinator: coordinator.client.recalibrate_co2_sensor(),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
{
|
||||
"entity": {
|
||||
"button": {
|
||||
"recalibrate_co2": {
|
||||
"default": "mdi:molecule-co2"
|
||||
}
|
||||
},
|
||||
"number": {
|
||||
"hysteresis_band": {
|
||||
"default": "mdi:delta"
|
||||
|
||||
@@ -59,6 +59,11 @@
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"button": {
|
||||
"recalibrate_co2": {
|
||||
"name": "Recalibrate CO2 sensor"
|
||||
}
|
||||
},
|
||||
"number": {
|
||||
"hysteresis_band": {
|
||||
"name": "Hysteresis band"
|
||||
|
||||
@@ -12,5 +12,5 @@
|
||||
"integration_type": "hub",
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["aioairzone"],
|
||||
"requirements": ["aioairzone==1.0.4"]
|
||||
"requirements": ["aioairzone==1.0.5"]
|
||||
}
|
||||
|
||||
@@ -8,5 +8,5 @@
|
||||
"integration_type": "system",
|
||||
"iot_class": "local_push",
|
||||
"quality_scale": "internal",
|
||||
"requirements": ["pysilero-vad==3.1.0", "pyspeex-noise==1.0.2"]
|
||||
"requirements": ["pysilero-vad==3.2.0", "pyspeex-noise==1.0.2"]
|
||||
}
|
||||
|
||||
@@ -20,5 +20,5 @@
|
||||
"dependencies": ["bluetooth_adapters"],
|
||||
"documentation": "https://www.home-assistant.io/integrations/bthome",
|
||||
"iot_class": "local_push",
|
||||
"requirements": ["bthome-ble==3.17.0"]
|
||||
"requirements": ["bthome-ble==3.16.0"]
|
||||
}
|
||||
|
||||
@@ -169,6 +169,7 @@ FRIENDS_OF_HUE_SWITCH = {
|
||||
}
|
||||
|
||||
RODRET_REMOTE_MODEL = "RODRET Dimmer"
|
||||
RODRET_REMOTE_MODEL_2 = "RODRET wireless dimmer"
|
||||
RODRET_REMOTE = {
|
||||
(CONF_SHORT_RELEASE, CONF_TURN_ON): {CONF_EVENT: 1002},
|
||||
(CONF_LONG_PRESS, CONF_TURN_ON): {CONF_EVENT: 1001},
|
||||
@@ -624,6 +625,7 @@ REMOTES = {
|
||||
HUE_WALL_REMOTE_MODEL: HUE_WALL_REMOTE,
|
||||
FRIENDS_OF_HUE_SWITCH_MODEL: FRIENDS_OF_HUE_SWITCH,
|
||||
RODRET_REMOTE_MODEL: RODRET_REMOTE,
|
||||
RODRET_REMOTE_MODEL_2: RODRET_REMOTE,
|
||||
SOMRIG_REMOTE_MODEL: SOMRIG_REMOTE,
|
||||
STYRBAR_REMOTE_MODEL: STYRBAR_REMOTE,
|
||||
SYMFONISK_SOUND_CONTROLLER_MODEL: SYMFONISK_SOUND_CONTROLLER,
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/dnsip",
|
||||
"integration_type": "service",
|
||||
"iot_class": "cloud_polling",
|
||||
"requirements": ["aiodns==3.6.1"]
|
||||
"requirements": ["aiodns==4.0.0"]
|
||||
}
|
||||
|
||||
@@ -6,6 +6,6 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/easyenergy",
|
||||
"integration_type": "service",
|
||||
"iot_class": "cloud_polling",
|
||||
"requirements": ["easyenergy==2.1.2"],
|
||||
"requirements": ["easyenergy==2.2.0"],
|
||||
"single_config_entry": true
|
||||
}
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/gree",
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["greeclimate"],
|
||||
"requirements": ["greeclimate==2.1.0"]
|
||||
"requirements": ["greeclimate==2.1.1"]
|
||||
}
|
||||
|
||||
@@ -8,5 +8,5 @@
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["pyhik"],
|
||||
"quality_scale": "legacy",
|
||||
"requirements": ["pyHik==0.3.4"]
|
||||
"requirements": ["pyHik==0.4.0"]
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@ rules:
|
||||
Use load_json_object_fixture in tests
|
||||
Patch the library instead of the HTTP requests
|
||||
Create a shared fixture for the mock config entry
|
||||
Use snapshots for binary sensor tests
|
||||
Use init_integration in tests
|
||||
Evaluate the need of test_config_entry_not_ready
|
||||
|
||||
|
||||
@@ -128,15 +128,15 @@ class AtlanticElectricalHeaterWithAdjustableTemperatureSetpoint(
|
||||
|
||||
states = self.device.states
|
||||
|
||||
if (
|
||||
operating_mode := states[OverkizState.CORE_OPERATING_MODE]
|
||||
) and operating_mode.value_as_str == OverkizCommandParam.EXTERNAL:
|
||||
return PRESET_EXTERNAL
|
||||
|
||||
if (
|
||||
state := states[OverkizState.IO_TARGET_HEATING_LEVEL]
|
||||
) and state.value_as_str:
|
||||
return OVERKIZ_TO_PRESET_MODE[state.value_as_str]
|
||||
|
||||
if (
|
||||
operating_mode := states[OverkizState.CORE_OPERATING_MODE]
|
||||
) and operating_mode.value_as_str == OverkizCommandParam.EXTERNAL:
|
||||
return PRESET_EXTERNAL
|
||||
return None
|
||||
|
||||
async def async_set_preset_mode(self, preset_mode: str) -> None:
|
||||
|
||||
@@ -26,11 +26,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: LeilSaunaConfigEntry) ->
|
||||
"""Set up Saunum Leil Sauna from a config entry."""
|
||||
host = entry.data[CONF_HOST]
|
||||
|
||||
client = SaunumClient(host=host)
|
||||
|
||||
# Test connection
|
||||
try:
|
||||
await client.connect()
|
||||
client = await SaunumClient.create(host)
|
||||
except SaunumConnectionError as exc:
|
||||
raise ConfigEntryNotReady(f"Error connecting to {host}: {exc}") from exc
|
||||
|
||||
|
||||
@@ -30,10 +30,9 @@ async def validate_input(data: dict[str, Any]) -> None:
|
||||
"""
|
||||
host = data[CONF_HOST]
|
||||
|
||||
client = SaunumClient(host=host)
|
||||
client = await SaunumClient.create(host)
|
||||
|
||||
try:
|
||||
await client.connect()
|
||||
# Try to read data to verify communication
|
||||
await client.async_get_data()
|
||||
finally:
|
||||
|
||||
@@ -8,5 +8,5 @@
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["pysaunum"],
|
||||
"quality_scale": "gold",
|
||||
"requirements": ["pysaunum==0.1.0"]
|
||||
"requirements": ["pysaunum==0.2.0"]
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ rules:
|
||||
comment: Integration controls a single device; no dynamic device discovery needed.
|
||||
|
||||
# Platinum
|
||||
async-dependency: todo
|
||||
async-dependency: done
|
||||
inject-websession:
|
||||
status: exempt
|
||||
comment: Integration uses Modbus TCP protocol and does not make HTTP requests.
|
||||
|
||||
@@ -13,5 +13,5 @@
|
||||
"integration_type": "hub",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["switchbot_api"],
|
||||
"requirements": ["switchbot-api==2.9.0"]
|
||||
"requirements": ["switchbot-api==2.10.0"]
|
||||
}
|
||||
|
||||
@@ -15,5 +15,5 @@
|
||||
"integration_type": "hub",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["PyTado"],
|
||||
"requirements": ["python-tado==0.18.15"]
|
||||
"requirements": ["python-tado==0.18.16"]
|
||||
}
|
||||
|
||||
@@ -24,13 +24,13 @@ async def async_setup_platform(
|
||||
return pollbot
|
||||
|
||||
|
||||
async def process_error(update: object, context: CallbackContext) -> None:
|
||||
async def process_error(bot: Bot, update: object, context: CallbackContext) -> None:
|
||||
"""Telegram bot error handler."""
|
||||
if context.error:
|
||||
error_callback(context.error, update)
|
||||
error_callback(bot, context.error, update)
|
||||
|
||||
|
||||
def error_callback(error: Exception, update: object | None = None) -> None:
|
||||
def error_callback(bot: Bot, error: Exception, update: object | None = None) -> None:
|
||||
"""Log the error."""
|
||||
try:
|
||||
raise error
|
||||
@@ -39,9 +39,17 @@ def error_callback(error: Exception, update: object | None = None) -> None:
|
||||
pass
|
||||
except TelegramError:
|
||||
if update is not None:
|
||||
_LOGGER.error('Update "%s" caused error: "%s"', update, error)
|
||||
_LOGGER.error(
|
||||
'[%s %s] Update "%s" caused error: "%s"',
|
||||
bot.username,
|
||||
bot.id,
|
||||
update,
|
||||
error,
|
||||
)
|
||||
else:
|
||||
_LOGGER.error("%s: %s", error.__class__.__name__, error)
|
||||
_LOGGER.error(
|
||||
"[%s %s] %s: %s", bot.username, bot.id, error.__class__.__name__, error
|
||||
)
|
||||
|
||||
|
||||
class PollBot(BaseTelegramBot):
|
||||
@@ -58,7 +66,9 @@ class PollBot(BaseTelegramBot):
|
||||
self.bot = bot
|
||||
self.application = ApplicationBuilder().bot(self.bot).build()
|
||||
self.application.add_handler(TypeHandler(Update, self.handle_update))
|
||||
self.application.add_error_handler(process_error)
|
||||
self.application.add_error_handler(
|
||||
lambda update, context: process_error(self.bot, update, context)
|
||||
)
|
||||
|
||||
async def shutdown(self) -> None:
|
||||
"""Shutdown the app."""
|
||||
@@ -66,16 +76,18 @@ class PollBot(BaseTelegramBot):
|
||||
|
||||
async def start_polling(self) -> None:
|
||||
"""Start the polling task."""
|
||||
_LOGGER.debug("Starting polling")
|
||||
await self.application.initialize()
|
||||
if self.application.updater:
|
||||
await self.application.updater.start_polling(error_callback=error_callback)
|
||||
await self.application.updater.start_polling(
|
||||
error_callback=lambda error: error_callback(self.bot, error, None)
|
||||
)
|
||||
await self.application.start()
|
||||
_LOGGER.info("[%s %s] Started polling", self.bot.username, self.bot.id)
|
||||
|
||||
async def stop_polling(self) -> None:
|
||||
"""Stop the polling task."""
|
||||
_LOGGER.debug("Stopping polling")
|
||||
if self.application.updater:
|
||||
await self.application.updater.stop()
|
||||
await self.application.stop()
|
||||
await self.application.shutdown()
|
||||
_LOGGER.info("[%s %s] Stopped polling", self.bot.username, self.bot.id)
|
||||
|
||||
@@ -5,6 +5,7 @@ from collections.abc import Callable
|
||||
from typing import Final
|
||||
|
||||
from aiohttp import ClientResponseError
|
||||
from aiohttp.client_exceptions import ClientError
|
||||
from tesla_fleet_api.const import Scope
|
||||
from tesla_fleet_api.exceptions import (
|
||||
Forbidden,
|
||||
@@ -315,7 +316,7 @@ async def async_migrate_entry(
|
||||
data = await Teslemetry(session, access_token).migrate_to_oauth(
|
||||
CLIENT_ID, access_token, hass.config.location_name
|
||||
)
|
||||
except ClientResponseError as e:
|
||||
except (ClientError, TypeError) as e:
|
||||
raise ConfigEntryAuthFailed from e
|
||||
|
||||
# Add auth_implementation for OAuth2 flow compatibility
|
||||
|
||||
@@ -250,6 +250,12 @@ class TibberDataAPICoordinator(DataUpdateCoordinator[dict[str, TibberDevice]]):
|
||||
async def _async_update_data(self) -> dict[str, TibberDevice]:
|
||||
"""Fetch the latest device capabilities from the Tibber Data API."""
|
||||
client = await self._async_get_client()
|
||||
devices: dict[str, TibberDevice] = await client.update_devices()
|
||||
try:
|
||||
devices: dict[str, TibberDevice] = await client.update_devices()
|
||||
except tibber.exceptions.RateLimitExceededError as err:
|
||||
raise UpdateFailed(
|
||||
f"Rate limit exceeded, retry after {err.retry_after} seconds",
|
||||
retry_after=err.retry_after,
|
||||
) from err
|
||||
self._build_sensor_lookup(devices)
|
||||
return devices
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/tibber",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["tibber"],
|
||||
"requirements": ["pyTibber==0.34.1"]
|
||||
"requirements": ["pyTibber==0.34.4"]
|
||||
}
|
||||
|
||||
@@ -654,6 +654,7 @@ DISCOVERY_SCHEMAS: list[NewZWaveDiscoverySchema] = [
|
||||
key=NOTIFICATION_SMOKE_ALARM,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
not_states={
|
||||
0,
|
||||
SmokeAlarmNotificationEvent.SENSOR_STATUS_SMOKE_DETECTED_LOCATION_PROVIDED,
|
||||
SmokeAlarmNotificationEvent.SENSOR_STATUS_SMOKE_DETECTED,
|
||||
SmokeAlarmNotificationEvent.MAINTENANCE_STATUS_REPLACEMENT_REQUIRED,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
aiodhcpwatcher==1.2.1
|
||||
aiodiscover==2.7.1
|
||||
aiodns==3.6.1
|
||||
aiodns==4.0.0
|
||||
aiohasupervisor==0.3.3
|
||||
aiohttp-asyncmdnsresolver==0.1.1
|
||||
aiohttp-fast-zlib==0.3.0
|
||||
@@ -56,7 +56,7 @@ PyJWT==2.10.1
|
||||
PyNaCl==1.6.0
|
||||
pyOpenSSL==25.3.0
|
||||
pyserial==3.5
|
||||
pysilero-vad==3.1.0
|
||||
pysilero-vad==3.2.0
|
||||
pyspeex-noise==1.0.2
|
||||
python-slugify==8.0.4
|
||||
PyTurboJPEG==1.8.0
|
||||
|
||||
@@ -24,7 +24,7 @@ classifiers = [
|
||||
]
|
||||
requires-python = ">=3.13.2"
|
||||
dependencies = [
|
||||
"aiodns==3.6.1",
|
||||
"aiodns==4.0.0",
|
||||
# Integrations may depend on hassio integration without listing it to
|
||||
# change behavior based on presence of supervisor. Deprecated with #127228
|
||||
# Lib can be removed with 2025.11
|
||||
|
||||
4
requirements.txt
generated
4
requirements.txt
generated
@@ -3,7 +3,7 @@
|
||||
-c homeassistant/package_constraints.txt
|
||||
|
||||
# Home Assistant Core
|
||||
aiodns==3.6.1
|
||||
aiodns==4.0.0
|
||||
aiohasupervisor==0.3.3
|
||||
aiohttp-asyncmdnsresolver==0.1.1
|
||||
aiohttp-fast-zlib==0.3.0
|
||||
@@ -40,7 +40,7 @@ propcache==0.4.1
|
||||
psutil-home-assistant==0.0.1
|
||||
PyJWT==2.10.1
|
||||
pyOpenSSL==25.3.0
|
||||
pysilero-vad==3.1.0
|
||||
pysilero-vad==3.2.0
|
||||
pyspeex-noise==1.0.2
|
||||
python-slugify==8.0.4
|
||||
PyTurboJPEG==1.8.0
|
||||
|
||||
22
requirements_all.txt
generated
22
requirements_all.txt
generated
@@ -187,7 +187,7 @@ aioairq==0.4.7
|
||||
aioairzone-cloud==0.7.2
|
||||
|
||||
# homeassistant.components.airzone
|
||||
aioairzone==1.0.4
|
||||
aioairzone==1.0.5
|
||||
|
||||
# homeassistant.components.alexa_devices
|
||||
aioamazondevices==11.0.2
|
||||
@@ -231,7 +231,7 @@ aiodhcpwatcher==1.2.1
|
||||
aiodiscover==2.7.1
|
||||
|
||||
# homeassistant.components.dnsip
|
||||
aiodns==3.6.1
|
||||
aiodns==4.0.0
|
||||
|
||||
# homeassistant.components.duke_energy
|
||||
aiodukeenergy==0.3.0
|
||||
@@ -703,7 +703,7 @@ brottsplatskartan==1.0.5
|
||||
brunt==1.2.0
|
||||
|
||||
# homeassistant.components.bthome
|
||||
bthome-ble==3.17.0
|
||||
bthome-ble==3.16.0
|
||||
|
||||
# homeassistant.components.bt_home_hub_5
|
||||
bthomehub5-devicelist==0.1.1
|
||||
@@ -842,7 +842,7 @@ dynalite-panel==0.0.4
|
||||
eagle100==0.1.1
|
||||
|
||||
# homeassistant.components.easyenergy
|
||||
easyenergy==2.1.2
|
||||
easyenergy==2.2.0
|
||||
|
||||
# homeassistant.components.ebusd
|
||||
ebusdpy==0.0.17
|
||||
@@ -1127,7 +1127,7 @@ gpiozero==1.6.2
|
||||
gps3==0.33.3
|
||||
|
||||
# homeassistant.components.gree
|
||||
greeclimate==2.1.0
|
||||
greeclimate==2.1.1
|
||||
|
||||
# homeassistant.components.greeneye_monitor
|
||||
greeneye_monitor==3.0.3
|
||||
@@ -1855,7 +1855,7 @@ pyElectra==1.2.4
|
||||
pyEmby==1.10
|
||||
|
||||
# homeassistant.components.hikvision
|
||||
pyHik==0.3.4
|
||||
pyHik==0.4.0
|
||||
|
||||
# homeassistant.components.homee
|
||||
pyHomee==1.3.8
|
||||
@@ -1867,7 +1867,7 @@ pyRFXtrx==0.31.1
|
||||
pySDCP==1
|
||||
|
||||
# homeassistant.components.tibber
|
||||
pyTibber==0.34.1
|
||||
pyTibber==0.34.4
|
||||
|
||||
# homeassistant.components.dlink
|
||||
pyW215==0.8.0
|
||||
@@ -2375,7 +2375,7 @@ pysabnzbd==1.1.1
|
||||
pysaj==0.0.16
|
||||
|
||||
# homeassistant.components.saunum
|
||||
pysaunum==0.1.0
|
||||
pysaunum==0.2.0
|
||||
|
||||
# homeassistant.components.schlage
|
||||
pyschlage==2025.9.0
|
||||
@@ -2409,7 +2409,7 @@ pysiaalarm==3.1.1
|
||||
pysignalclirestapi==0.3.24
|
||||
|
||||
# homeassistant.components.assist_pipeline
|
||||
pysilero-vad==3.1.0
|
||||
pysilero-vad==3.2.0
|
||||
|
||||
# homeassistant.components.sky_hub
|
||||
pyskyqhub==0.1.4
|
||||
@@ -2596,7 +2596,7 @@ python-snoo==0.8.3
|
||||
python-songpal==0.16.2
|
||||
|
||||
# homeassistant.components.tado
|
||||
python-tado==0.18.15
|
||||
python-tado==0.18.16
|
||||
|
||||
# homeassistant.components.technove
|
||||
python-technove==2.0.0
|
||||
@@ -2956,7 +2956,7 @@ surepy==0.9.0
|
||||
swisshydrodata==0.1.0
|
||||
|
||||
# homeassistant.components.switchbot_cloud
|
||||
switchbot-api==2.9.0
|
||||
switchbot-api==2.10.0
|
||||
|
||||
# homeassistant.components.synology_srm
|
||||
synology-srm==0.2.0
|
||||
|
||||
22
requirements_test_all.txt
generated
22
requirements_test_all.txt
generated
@@ -178,7 +178,7 @@ aioairq==0.4.7
|
||||
aioairzone-cloud==0.7.2
|
||||
|
||||
# homeassistant.components.airzone
|
||||
aioairzone==1.0.4
|
||||
aioairzone==1.0.5
|
||||
|
||||
# homeassistant.components.alexa_devices
|
||||
aioamazondevices==11.0.2
|
||||
@@ -222,7 +222,7 @@ aiodhcpwatcher==1.2.1
|
||||
aiodiscover==2.7.1
|
||||
|
||||
# homeassistant.components.dnsip
|
||||
aiodns==3.6.1
|
||||
aiodns==4.0.0
|
||||
|
||||
# homeassistant.components.duke_energy
|
||||
aiodukeenergy==0.3.0
|
||||
@@ -633,7 +633,7 @@ brottsplatskartan==1.0.5
|
||||
brunt==1.2.0
|
||||
|
||||
# homeassistant.components.bthome
|
||||
bthome-ble==3.17.0
|
||||
bthome-ble==3.16.0
|
||||
|
||||
# homeassistant.components.buienradar
|
||||
buienradar==1.0.6
|
||||
@@ -748,7 +748,7 @@ dynalite-panel==0.0.4
|
||||
eagle100==0.1.1
|
||||
|
||||
# homeassistant.components.easyenergy
|
||||
easyenergy==2.1.2
|
||||
easyenergy==2.2.0
|
||||
|
||||
# homeassistant.components.egauge
|
||||
egauge-async==0.4.0
|
||||
@@ -1000,7 +1000,7 @@ govee-local-api==2.3.0
|
||||
gps3==0.33.3
|
||||
|
||||
# homeassistant.components.gree
|
||||
greeclimate==2.1.0
|
||||
greeclimate==2.1.1
|
||||
|
||||
# homeassistant.components.greeneye_monitor
|
||||
greeneye_monitor==3.0.3
|
||||
@@ -1589,7 +1589,7 @@ pyDuotecno==2024.10.1
|
||||
pyElectra==1.2.4
|
||||
|
||||
# homeassistant.components.hikvision
|
||||
pyHik==0.3.4
|
||||
pyHik==0.4.0
|
||||
|
||||
# homeassistant.components.homee
|
||||
pyHomee==1.3.8
|
||||
@@ -1598,7 +1598,7 @@ pyHomee==1.3.8
|
||||
pyRFXtrx==0.31.1
|
||||
|
||||
# homeassistant.components.tibber
|
||||
pyTibber==0.34.1
|
||||
pyTibber==0.34.4
|
||||
|
||||
# homeassistant.components.dlink
|
||||
pyW215==0.8.0
|
||||
@@ -2007,7 +2007,7 @@ pyrympro==0.0.9
|
||||
pysabnzbd==1.1.1
|
||||
|
||||
# homeassistant.components.saunum
|
||||
pysaunum==0.1.0
|
||||
pysaunum==0.2.0
|
||||
|
||||
# homeassistant.components.schlage
|
||||
pyschlage==2025.9.0
|
||||
@@ -2035,7 +2035,7 @@ pysiaalarm==3.1.1
|
||||
pysignalclirestapi==0.3.24
|
||||
|
||||
# homeassistant.components.assist_pipeline
|
||||
pysilero-vad==3.1.0
|
||||
pysilero-vad==3.2.0
|
||||
|
||||
# homeassistant.components.sma
|
||||
pysma==1.1.0
|
||||
@@ -2183,7 +2183,7 @@ python-snoo==0.8.3
|
||||
python-songpal==0.16.2
|
||||
|
||||
# homeassistant.components.tado
|
||||
python-tado==0.18.15
|
||||
python-tado==0.18.16
|
||||
|
||||
# homeassistant.components.technove
|
||||
python-technove==2.0.0
|
||||
@@ -2477,7 +2477,7 @@ subarulink==0.7.15
|
||||
surepy==0.9.0
|
||||
|
||||
# homeassistant.components.switchbot_cloud
|
||||
switchbot-api==2.9.0
|
||||
switchbot-api==2.10.0
|
||||
|
||||
# homeassistant.components.system_bridge
|
||||
systembridgeconnector==5.3.1
|
||||
|
||||
@@ -1,4 +1,52 @@
|
||||
# serializer version: 1
|
||||
# name: test_buttons[button.test_thermostat_recalibrate_co2_sensor-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'button',
|
||||
'entity_category': <EntityCategory.CONFIG: 'config'>,
|
||||
'entity_id': 'button.test_thermostat_recalibrate_co2_sensor',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Recalibrate CO2 sensor',
|
||||
'platform': 'airobot',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'recalibrate_co2',
|
||||
'unique_id': 'T01A1B2C3_recalibrate_co2',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[button.test_thermostat_recalibrate_co2_sensor-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Test Thermostat Recalibrate CO2 sensor',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'button.test_thermostat_recalibrate_co2_sensor',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[button.test_thermostat_restart-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
|
||||
@@ -25,7 +25,7 @@ def platforms() -> list[Platform]:
|
||||
return [Platform.BUTTON]
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("init_integration")
|
||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default", "init_integration")
|
||||
async def test_buttons(
|
||||
hass: HomeAssistant,
|
||||
snapshot: SnapshotAssertion,
|
||||
@@ -93,3 +93,38 @@ async def test_restart_button_connection_errors(
|
||||
)
|
||||
|
||||
mock_airobot_client.reboot_thermostat.assert_called_once()
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default", "init_integration")
|
||||
async def test_recalibrate_co2_button(
|
||||
hass: HomeAssistant,
|
||||
mock_airobot_client: AsyncMock,
|
||||
) -> None:
|
||||
"""Test recalibrate CO2 sensor button."""
|
||||
await hass.services.async_call(
|
||||
BUTTON_DOMAIN,
|
||||
SERVICE_PRESS,
|
||||
{ATTR_ENTITY_ID: "button.test_thermostat_recalibrate_co2_sensor"},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
mock_airobot_client.recalibrate_co2_sensor.assert_called_once()
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default", "init_integration")
|
||||
async def test_recalibrate_co2_button_error(
|
||||
hass: HomeAssistant,
|
||||
mock_airobot_client: AsyncMock,
|
||||
) -> None:
|
||||
"""Test recalibrate CO2 sensor button error handling."""
|
||||
mock_airobot_client.recalibrate_co2_sensor.side_effect = AirobotError("Test error")
|
||||
|
||||
with pytest.raises(HomeAssistantError):
|
||||
await hass.services.async_call(
|
||||
BUTTON_DOMAIN,
|
||||
SERVICE_PRESS,
|
||||
{ATTR_ENTITY_ID: "button.test_thermostat_recalibrate_co2_sensor"},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
mock_airobot_client.recalibrate_co2_sensor.assert_called_once()
|
||||
|
||||
@@ -2,8 +2,24 @@
|
||||
|
||||
import json
|
||||
from typing import Any
|
||||
from unittest.mock import patch
|
||||
|
||||
from tests.common import load_fixture
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from tests.common import MockConfigEntry, load_fixture
|
||||
|
||||
|
||||
async def setup_platform(hass: HomeAssistant, config_entry: MockConfigEntry) -> None:
|
||||
"""Set up the NINA platform."""
|
||||
with patch(
|
||||
"pynina.baseApi.BaseAPI._makeRequest",
|
||||
wraps=mocked_request_function,
|
||||
):
|
||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert config_entry.state is ConfigEntryState.LOADED
|
||||
|
||||
|
||||
def mocked_request_function(url: str) -> dict[str, Any]:
|
||||
|
||||
257
tests/components/nina/snapshots/test_binary_sensor.ambr
Normal file
257
tests/components/nina/snapshots/test_binary_sensor.ambr
Normal file
@@ -0,0 +1,257 @@
|
||||
# serializer version: 1
|
||||
# name: test_sensors[binary_sensor.nina_warning_aach_1-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'binary_sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'binary_sensor.nina_warning_aach_1',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <BinarySensorDeviceClass.SAFETY: 'safety'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Warning: Aach 1',
|
||||
'platform': 'nina',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '095760000000-1',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[binary_sensor.nina_warning_aach_1-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'affected_areas': 'Gemeinde Oberreichenbach, Gemeinde Neuweiler, Stadt Nagold, Stadt Neubulach, Gemeinde Schömberg, Gemeinde Simmersfeld, Gemeinde Simmozheim, Gemeinde Rohrdorf, Gemeinde Ostelsheim, Gemeinde Ebhausen, Gemeinde Egenhausen, Gemeinde Dobel, Stadt Bad Liebenzell, Stadt Solingen, Stadt Haiterbach, Stadt Bad Herrenalb, Gemeinde Höfen an der Enz, Gemeinde Gechingen, Gemeinde Enzklösterle, Gemeinde Gutach (Schwarzwaldbahn) und 3392 weitere.',
|
||||
'description': 'Es treten Sturmböen mit Geschwindigkeiten zwischen 70 km/h (20m/s, 38kn, Bft 8) und 85 km/h (24m/s, 47kn, Bft 9) aus westlicher Richtung auf. In Schauernähe sowie in exponierten Lagen muss mit schweren Sturmböen bis 90 km/h (25m/s, 48kn, Bft 10) gerechnet werden.',
|
||||
'device_class': 'safety',
|
||||
'expires': '3021-11-22T05:19:00+01:00',
|
||||
'friendly_name': 'NINA Warning: Aach 1',
|
||||
'headline': 'Ausfall Notruf 112',
|
||||
'id': 'mow.DE-NW-BN-SE030-20201014-30-000',
|
||||
'recommended_actions': '',
|
||||
'sender': 'Deutscher Wetterdienst',
|
||||
'sent': '2021-10-11T05:20:00+01:00',
|
||||
'severity': 'Minor',
|
||||
'start': '2021-11-01T05:20:00+01:00',
|
||||
'web': 'https://www.wettergefahren.de',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'binary_sensor.nina_warning_aach_1',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'on',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[binary_sensor.nina_warning_aach_2-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'binary_sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'binary_sensor.nina_warning_aach_2',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <BinarySensorDeviceClass.SAFETY: 'safety'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Warning: Aach 2',
|
||||
'platform': 'nina',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '095760000000-2',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[binary_sensor.nina_warning_aach_2-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'safety',
|
||||
'friendly_name': 'NINA Warning: Aach 2',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'binary_sensor.nina_warning_aach_2',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[binary_sensor.nina_warning_aach_3-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'binary_sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'binary_sensor.nina_warning_aach_3',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <BinarySensorDeviceClass.SAFETY: 'safety'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Warning: Aach 3',
|
||||
'platform': 'nina',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '095760000000-3',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[binary_sensor.nina_warning_aach_3-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'safety',
|
||||
'friendly_name': 'NINA Warning: Aach 3',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'binary_sensor.nina_warning_aach_3',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[binary_sensor.nina_warning_aach_4-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'binary_sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'binary_sensor.nina_warning_aach_4',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <BinarySensorDeviceClass.SAFETY: 'safety'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Warning: Aach 4',
|
||||
'platform': 'nina',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '095760000000-4',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[binary_sensor.nina_warning_aach_4-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'safety',
|
||||
'friendly_name': 'NINA Warning: Aach 4',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'binary_sensor.nina_warning_aach_4',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[binary_sensor.nina_warning_aach_5-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'binary_sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'binary_sensor.nina_warning_aach_5',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <BinarySensorDeviceClass.SAFETY: 'safety'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Warning: Aach 5',
|
||||
'platform': 'nina',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '095760000000-5',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[binary_sensor.nina_warning_aach_5-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'safety',
|
||||
'friendly_name': 'NINA Warning: Aach 5',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'binary_sensor.nina_warning_aach_5',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
||||
@@ -3,40 +3,17 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
from unittest.mock import patch
|
||||
|
||||
from homeassistant.components.binary_sensor import BinarySensorDeviceClass
|
||||
from homeassistant.components.nina.const import (
|
||||
ATTR_AFFECTED_AREAS,
|
||||
ATTR_DESCRIPTION,
|
||||
ATTR_EXPIRES,
|
||||
ATTR_HEADLINE,
|
||||
ATTR_ID,
|
||||
ATTR_RECOMMENDED_ACTIONS,
|
||||
ATTR_SENDER,
|
||||
ATTR_SENT,
|
||||
ATTR_SEVERITY,
|
||||
ATTR_START,
|
||||
ATTR_WEB,
|
||||
DOMAIN,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.nina.const import ATTR_HEADLINE, DOMAIN
|
||||
from homeassistant.const import STATE_OFF, STATE_ON
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from . import mocked_request_function
|
||||
from . import setup_platform
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
ENTRY_DATA: dict[str, Any] = {
|
||||
"slots": 5,
|
||||
"regions": {"083350000000": "Aach, Stadt"},
|
||||
"filters": {
|
||||
"headline_filter": ".*corona.*",
|
||||
"area_filter": ".*",
|
||||
},
|
||||
}
|
||||
from tests.common import MockConfigEntry, snapshot_platform
|
||||
|
||||
ENTRY_DATA_NO_CORONA: dict[str, Any] = {
|
||||
"slots": 5,
|
||||
@@ -47,7 +24,7 @@ ENTRY_DATA_NO_CORONA: dict[str, Any] = {
|
||||
},
|
||||
}
|
||||
|
||||
ENTRY_DATA_NO_AREA: dict[str, Any] = {
|
||||
ENTRY_DATA_SPECIFIC_AREA: dict[str, Any] = {
|
||||
"slots": 5,
|
||||
"regions": {"083350000000": "Aach, Stadt"},
|
||||
"filters": {
|
||||
@@ -57,321 +34,93 @@ ENTRY_DATA_NO_AREA: dict[str, Any] = {
|
||||
}
|
||||
|
||||
|
||||
async def test_sensors(hass: HomeAssistant, entity_registry: er.EntityRegistry) -> None:
|
||||
async def test_sensors(
|
||||
hass: HomeAssistant,
|
||||
entity_registry: er.EntityRegistry,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test the creation and values of the NINA sensors."""
|
||||
|
||||
with patch(
|
||||
"pynina.baseApi.BaseAPI._makeRequest",
|
||||
wraps=mocked_request_function,
|
||||
):
|
||||
conf_entry: MockConfigEntry = MockConfigEntry(
|
||||
domain=DOMAIN, title="NINA", data=ENTRY_DATA, version=1, minor_version=3
|
||||
)
|
||||
conf_entry.add_to_hass(hass)
|
||||
|
||||
await hass.config_entries.async_setup(conf_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert conf_entry.state is ConfigEntryState.LOADED
|
||||
|
||||
state_w1 = hass.states.get("binary_sensor.nina_warning_aach_stadt_1")
|
||||
entry_w1 = entity_registry.async_get("binary_sensor.nina_warning_aach_stadt_1")
|
||||
|
||||
assert state_w1.state == STATE_ON
|
||||
assert state_w1.attributes.get(ATTR_HEADLINE) == "Ausfall Notruf 112"
|
||||
assert (
|
||||
state_w1.attributes.get(ATTR_DESCRIPTION)
|
||||
== "Es treten Sturmböen mit Geschwindigkeiten zwischen 70 km/h (20m/s, 38kn, Bft 8) und 85 km/h (24m/s, 47kn, Bft 9) aus westlicher Richtung auf. In Schauernähe sowie in exponierten Lagen muss mit schweren Sturmböen bis 90 km/h (25m/s, 48kn, Bft 10) gerechnet werden."
|
||||
)
|
||||
assert state_w1.attributes.get(ATTR_SENDER) == "Deutscher Wetterdienst"
|
||||
assert state_w1.attributes.get(ATTR_SEVERITY) == "Minor"
|
||||
assert state_w1.attributes.get(ATTR_RECOMMENDED_ACTIONS) == ""
|
||||
assert state_w1.attributes.get(ATTR_WEB) == "https://www.wettergefahren.de"
|
||||
assert (
|
||||
state_w1.attributes.get(ATTR_AFFECTED_AREAS)
|
||||
== "Gemeinde Oberreichenbach, Gemeinde Neuweiler, Stadt Nagold, Stadt Neubulach, Gemeinde Schömberg, Gemeinde Simmersfeld, Gemeinde Simmozheim, Gemeinde Rohrdorf, Gemeinde Ostelsheim, Gemeinde Ebhausen, Gemeinde Egenhausen, Gemeinde Dobel, Stadt Bad Liebenzell, Stadt Solingen, Stadt Haiterbach, Stadt Bad Herrenalb, Gemeinde Höfen an der Enz, Gemeinde Gechingen, Gemeinde Enzklösterle, Gemeinde Gutach (Schwarzwaldbahn) und 3392 weitere."
|
||||
)
|
||||
assert state_w1.attributes.get(ATTR_ID) == "mow.DE-NW-BN-SE030-20201014-30-000"
|
||||
assert state_w1.attributes.get(ATTR_SENT) == "2021-10-11T05:20:00+01:00"
|
||||
assert state_w1.attributes.get(ATTR_START) == "2021-11-01T05:20:00+01:00"
|
||||
assert state_w1.attributes.get(ATTR_EXPIRES) == "3021-11-22T05:19:00+01:00"
|
||||
|
||||
assert entry_w1.unique_id == "083350000000-1"
|
||||
assert state_w1.attributes.get("device_class") == BinarySensorDeviceClass.SAFETY
|
||||
|
||||
state_w2 = hass.states.get("binary_sensor.nina_warning_aach_stadt_2")
|
||||
entry_w2 = entity_registry.async_get("binary_sensor.nina_warning_aach_stadt_2")
|
||||
|
||||
assert state_w2.state == STATE_OFF
|
||||
assert state_w2.attributes.get(ATTR_HEADLINE) is None
|
||||
assert state_w2.attributes.get(ATTR_DESCRIPTION) is None
|
||||
assert state_w2.attributes.get(ATTR_SENDER) is None
|
||||
assert state_w2.attributes.get(ATTR_SEVERITY) is None
|
||||
assert state_w2.attributes.get(ATTR_RECOMMENDED_ACTIONS) is None
|
||||
assert state_w2.attributes.get(ATTR_WEB) is None
|
||||
assert state_w2.attributes.get(ATTR_AFFECTED_AREAS) is None
|
||||
assert state_w2.attributes.get(ATTR_ID) is None
|
||||
assert state_w2.attributes.get(ATTR_SENT) is None
|
||||
assert state_w2.attributes.get(ATTR_START) is None
|
||||
assert state_w2.attributes.get(ATTR_EXPIRES) is None
|
||||
|
||||
assert entry_w2.unique_id == "083350000000-2"
|
||||
assert state_w2.attributes.get("device_class") == BinarySensorDeviceClass.SAFETY
|
||||
|
||||
state_w3 = hass.states.get("binary_sensor.nina_warning_aach_stadt_3")
|
||||
entry_w3 = entity_registry.async_get("binary_sensor.nina_warning_aach_stadt_3")
|
||||
|
||||
assert state_w3.state == STATE_OFF
|
||||
assert state_w3.attributes.get(ATTR_HEADLINE) is None
|
||||
assert state_w3.attributes.get(ATTR_DESCRIPTION) is None
|
||||
assert state_w3.attributes.get(ATTR_SENDER) is None
|
||||
assert state_w3.attributes.get(ATTR_SEVERITY) is None
|
||||
assert state_w3.attributes.get(ATTR_RECOMMENDED_ACTIONS) is None
|
||||
assert state_w3.attributes.get(ATTR_WEB) is None
|
||||
assert state_w3.attributes.get(ATTR_AFFECTED_AREAS) is None
|
||||
assert state_w3.attributes.get(ATTR_ID) is None
|
||||
assert state_w3.attributes.get(ATTR_SENT) is None
|
||||
assert state_w3.attributes.get(ATTR_START) is None
|
||||
assert state_w3.attributes.get(ATTR_EXPIRES) is None
|
||||
|
||||
assert entry_w3.unique_id == "083350000000-3"
|
||||
assert state_w3.attributes.get("device_class") == BinarySensorDeviceClass.SAFETY
|
||||
|
||||
state_w4 = hass.states.get("binary_sensor.nina_warning_aach_stadt_4")
|
||||
entry_w4 = entity_registry.async_get("binary_sensor.nina_warning_aach_stadt_4")
|
||||
|
||||
assert state_w4.state == STATE_OFF
|
||||
assert state_w4.attributes.get(ATTR_HEADLINE) is None
|
||||
assert state_w4.attributes.get(ATTR_DESCRIPTION) is None
|
||||
assert state_w4.attributes.get(ATTR_SENDER) is None
|
||||
assert state_w4.attributes.get(ATTR_SEVERITY) is None
|
||||
assert state_w4.attributes.get(ATTR_RECOMMENDED_ACTIONS) is None
|
||||
assert state_w4.attributes.get(ATTR_WEB) is None
|
||||
assert state_w4.attributes.get(ATTR_AFFECTED_AREAS) is None
|
||||
assert state_w4.attributes.get(ATTR_ID) is None
|
||||
assert state_w4.attributes.get(ATTR_SENT) is None
|
||||
assert state_w4.attributes.get(ATTR_START) is None
|
||||
assert state_w4.attributes.get(ATTR_EXPIRES) is None
|
||||
|
||||
assert entry_w4.unique_id == "083350000000-4"
|
||||
assert state_w4.attributes.get("device_class") == BinarySensorDeviceClass.SAFETY
|
||||
|
||||
state_w5 = hass.states.get("binary_sensor.nina_warning_aach_stadt_5")
|
||||
entry_w5 = entity_registry.async_get("binary_sensor.nina_warning_aach_stadt_5")
|
||||
|
||||
assert state_w5.state == STATE_OFF
|
||||
assert state_w5.attributes.get(ATTR_HEADLINE) is None
|
||||
assert state_w5.attributes.get(ATTR_DESCRIPTION) is None
|
||||
assert state_w5.attributes.get(ATTR_SENDER) is None
|
||||
assert state_w5.attributes.get(ATTR_SEVERITY) is None
|
||||
assert state_w5.attributes.get(ATTR_RECOMMENDED_ACTIONS) is None
|
||||
assert state_w5.attributes.get(ATTR_WEB) is None
|
||||
assert state_w5.attributes.get(ATTR_AFFECTED_AREAS) is None
|
||||
assert state_w5.attributes.get(ATTR_ID) is None
|
||||
assert state_w5.attributes.get(ATTR_SENT) is None
|
||||
assert state_w5.attributes.get(ATTR_START) is None
|
||||
assert state_w5.attributes.get(ATTR_EXPIRES) is None
|
||||
|
||||
assert entry_w5.unique_id == "083350000000-5"
|
||||
assert state_w5.attributes.get("device_class") == BinarySensorDeviceClass.SAFETY
|
||||
await setup_platform(hass, mock_config_entry)
|
||||
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
|
||||
|
||||
|
||||
async def test_sensors_without_corona_filter(
|
||||
hass: HomeAssistant, entity_registry: er.EntityRegistry
|
||||
hass: HomeAssistant, entity_registry: er.EntityRegistry, snapshot: SnapshotAssertion
|
||||
) -> None:
|
||||
"""Test the creation and values of the NINA sensors without the corona filter."""
|
||||
|
||||
with patch(
|
||||
"pynina.baseApi.BaseAPI._makeRequest",
|
||||
wraps=mocked_request_function,
|
||||
):
|
||||
conf_entry: MockConfigEntry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
title="NINA",
|
||||
data=ENTRY_DATA_NO_CORONA,
|
||||
version=1,
|
||||
minor_version=3,
|
||||
)
|
||||
conf_entry.add_to_hass(hass)
|
||||
conf_entry: MockConfigEntry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
title="NINA",
|
||||
data=ENTRY_DATA_NO_CORONA,
|
||||
version=1,
|
||||
minor_version=3,
|
||||
)
|
||||
conf_entry.add_to_hass(hass)
|
||||
|
||||
await hass.config_entries.async_setup(conf_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
await setup_platform(hass, conf_entry)
|
||||
|
||||
assert conf_entry.state is ConfigEntryState.LOADED
|
||||
state_w1 = hass.states.get("binary_sensor.nina_warning_aach_stadt_1")
|
||||
|
||||
state_w1 = hass.states.get("binary_sensor.nina_warning_aach_stadt_1")
|
||||
entry_w1 = entity_registry.async_get("binary_sensor.nina_warning_aach_stadt_1")
|
||||
assert state_w1.state == STATE_ON
|
||||
assert (
|
||||
state_w1.attributes.get(ATTR_HEADLINE)
|
||||
== "Corona-Verordnung des Landes: Warnstufe durch Landesgesundheitsamt ausgerufen"
|
||||
)
|
||||
|
||||
assert state_w1.state == STATE_ON
|
||||
assert (
|
||||
state_w1.attributes.get(ATTR_HEADLINE)
|
||||
== "Corona-Verordnung des Landes: Warnstufe durch Landesgesundheitsamt ausgerufen"
|
||||
)
|
||||
assert (
|
||||
state_w1.attributes.get(ATTR_DESCRIPTION)
|
||||
== "Die Zahl der mit dem Corona-Virus infizierten Menschen steigt gegenwärtig stark an. Es wächst daher die Gefahr einer weiteren Verbreitung der Infektion und - je nach Einzelfall - auch von schweren Erkrankungen."
|
||||
)
|
||||
assert state_w1.attributes.get(ATTR_SENDER) == ""
|
||||
assert state_w1.attributes.get(ATTR_SEVERITY) == "Minor"
|
||||
assert (
|
||||
state_w1.attributes.get(ATTR_RECOMMENDED_ACTIONS)
|
||||
== "Waschen sich regelmäßig und gründlich die Hände."
|
||||
)
|
||||
assert state_w1.attributes.get(ATTR_WEB) == ""
|
||||
assert (
|
||||
state_w1.attributes.get(ATTR_AFFECTED_AREAS)
|
||||
== "Bundesland: Freie Hansestadt Bremen, Land Berlin, Land Hessen, Land Nordrhein-Westfalen, Land Brandenburg, Freistaat Bayern, Land Mecklenburg-Vorpommern, Land Rheinland-Pfalz, Freistaat Sachsen, Land Schleswig-Holstein, Freie und Hansestadt Hamburg, Freistaat Thüringen, Land Niedersachsen, Land Saarland, Land Sachsen-Anhalt, Land Baden-Württemberg"
|
||||
)
|
||||
assert state_w1.attributes.get(ATTR_ID) == "mow.DE-BW-S-SE018-20211102-18-001"
|
||||
assert state_w1.attributes.get(ATTR_SENT) == "2021-11-02T20:07:16+01:00"
|
||||
assert state_w1.attributes.get(ATTR_START) == ""
|
||||
assert state_w1.attributes.get(ATTR_EXPIRES) == ""
|
||||
state_w2 = hass.states.get("binary_sensor.nina_warning_aach_stadt_2")
|
||||
|
||||
assert entry_w1.unique_id == "083350000000-1"
|
||||
assert state_w1.attributes.get("device_class") == BinarySensorDeviceClass.SAFETY
|
||||
assert state_w2.state == STATE_ON
|
||||
assert state_w2.attributes.get(ATTR_HEADLINE) == "Ausfall Notruf 112"
|
||||
|
||||
state_w2 = hass.states.get("binary_sensor.nina_warning_aach_stadt_2")
|
||||
entry_w2 = entity_registry.async_get("binary_sensor.nina_warning_aach_stadt_2")
|
||||
state_w3 = hass.states.get("binary_sensor.nina_warning_aach_stadt_3")
|
||||
|
||||
assert state_w2.state == STATE_ON
|
||||
assert state_w2.attributes.get(ATTR_HEADLINE) == "Ausfall Notruf 112"
|
||||
assert (
|
||||
state_w2.attributes.get(ATTR_DESCRIPTION)
|
||||
== "Es treten Sturmböen mit Geschwindigkeiten zwischen 70 km/h (20m/s, 38kn, Bft 8) und 85 km/h (24m/s, 47kn, Bft 9) aus westlicher Richtung auf. In Schauernähe sowie in exponierten Lagen muss mit schweren Sturmböen bis 90 km/h (25m/s, 48kn, Bft 10) gerechnet werden."
|
||||
)
|
||||
assert (
|
||||
state_w2.attributes.get(ATTR_AFFECTED_AREAS)
|
||||
== "Gemeinde Oberreichenbach, Gemeinde Neuweiler, Stadt Nagold, Stadt Neubulach, Gemeinde Schömberg, Gemeinde Simmersfeld, Gemeinde Simmozheim, Gemeinde Rohrdorf, Gemeinde Ostelsheim, Gemeinde Ebhausen, Gemeinde Egenhausen, Gemeinde Dobel, Stadt Bad Liebenzell, Stadt Solingen, Stadt Haiterbach, Stadt Bad Herrenalb, Gemeinde Höfen an der Enz, Gemeinde Gechingen, Gemeinde Enzklösterle, Gemeinde Gutach (Schwarzwaldbahn) und 3392 weitere."
|
||||
)
|
||||
assert state_w2.attributes.get(ATTR_SENDER) == "Deutscher Wetterdienst"
|
||||
assert state_w2.attributes.get(ATTR_SEVERITY) == "Minor"
|
||||
assert state_w2.attributes.get(ATTR_RECOMMENDED_ACTIONS) == ""
|
||||
assert state_w2.attributes.get(ATTR_WEB) == "https://www.wettergefahren.de"
|
||||
assert state_w2.attributes.get(ATTR_ID) == "mow.DE-NW-BN-SE030-20201014-30-000"
|
||||
assert state_w2.attributes.get(ATTR_SENT) == "2021-10-11T05:20:00+01:00"
|
||||
assert state_w2.attributes.get(ATTR_START) == "2021-11-01T05:20:00+01:00"
|
||||
assert state_w2.attributes.get(ATTR_EXPIRES) == "3021-11-22T05:19:00+01:00"
|
||||
assert state_w3.state == STATE_OFF
|
||||
|
||||
assert entry_w2.unique_id == "083350000000-2"
|
||||
assert state_w2.attributes.get("device_class") == BinarySensorDeviceClass.SAFETY
|
||||
state_w4 = hass.states.get("binary_sensor.nina_warning_aach_stadt_4")
|
||||
|
||||
state_w3 = hass.states.get("binary_sensor.nina_warning_aach_stadt_3")
|
||||
entry_w3 = entity_registry.async_get("binary_sensor.nina_warning_aach_stadt_3")
|
||||
assert state_w4.state == STATE_OFF
|
||||
|
||||
assert state_w3.state == STATE_OFF
|
||||
assert state_w3.attributes.get(ATTR_HEADLINE) is None
|
||||
assert state_w3.attributes.get(ATTR_DESCRIPTION) is None
|
||||
assert state_w3.attributes.get(ATTR_SENDER) is None
|
||||
assert state_w3.attributes.get(ATTR_SEVERITY) is None
|
||||
assert state_w3.attributes.get(ATTR_RECOMMENDED_ACTIONS) is None
|
||||
assert state_w3.attributes.get(ATTR_WEB) is None
|
||||
assert state_w3.attributes.get(ATTR_AFFECTED_AREAS) is None
|
||||
assert state_w3.attributes.get(ATTR_ID) is None
|
||||
assert state_w3.attributes.get(ATTR_SENT) is None
|
||||
assert state_w3.attributes.get(ATTR_START) is None
|
||||
assert state_w3.attributes.get(ATTR_EXPIRES) is None
|
||||
state_w5 = hass.states.get("binary_sensor.nina_warning_aach_stadt_5")
|
||||
|
||||
assert entry_w3.unique_id == "083350000000-3"
|
||||
assert state_w3.attributes.get("device_class") == BinarySensorDeviceClass.SAFETY
|
||||
|
||||
state_w4 = hass.states.get("binary_sensor.nina_warning_aach_stadt_4")
|
||||
entry_w4 = entity_registry.async_get("binary_sensor.nina_warning_aach_stadt_4")
|
||||
|
||||
assert state_w4.state == STATE_OFF
|
||||
assert state_w4.attributes.get(ATTR_HEADLINE) is None
|
||||
assert state_w4.attributes.get(ATTR_DESCRIPTION) is None
|
||||
assert state_w4.attributes.get(ATTR_SENDER) is None
|
||||
assert state_w4.attributes.get(ATTR_SEVERITY) is None
|
||||
assert state_w4.attributes.get(ATTR_RECOMMENDED_ACTIONS) is None
|
||||
assert state_w4.attributes.get(ATTR_WEB) is None
|
||||
assert state_w4.attributes.get(ATTR_AFFECTED_AREAS) is None
|
||||
assert state_w4.attributes.get(ATTR_ID) is None
|
||||
assert state_w4.attributes.get(ATTR_SENT) is None
|
||||
assert state_w4.attributes.get(ATTR_START) is None
|
||||
assert state_w4.attributes.get(ATTR_EXPIRES) is None
|
||||
|
||||
assert entry_w4.unique_id == "083350000000-4"
|
||||
assert state_w4.attributes.get("device_class") == BinarySensorDeviceClass.SAFETY
|
||||
|
||||
state_w5 = hass.states.get("binary_sensor.nina_warning_aach_stadt_5")
|
||||
entry_w5 = entity_registry.async_get("binary_sensor.nina_warning_aach_stadt_5")
|
||||
|
||||
assert state_w5.state == STATE_OFF
|
||||
assert state_w5.attributes.get(ATTR_HEADLINE) is None
|
||||
assert state_w5.attributes.get(ATTR_DESCRIPTION) is None
|
||||
assert state_w5.attributes.get(ATTR_SENDER) is None
|
||||
assert state_w5.attributes.get(ATTR_SEVERITY) is None
|
||||
assert state_w5.attributes.get(ATTR_RECOMMENDED_ACTIONS) is None
|
||||
assert state_w5.attributes.get(ATTR_WEB) is None
|
||||
assert state_w5.attributes.get(ATTR_AFFECTED_AREAS) is None
|
||||
assert state_w5.attributes.get(ATTR_ID) is None
|
||||
assert state_w5.attributes.get(ATTR_SENT) is None
|
||||
assert state_w5.attributes.get(ATTR_START) is None
|
||||
assert state_w5.attributes.get(ATTR_EXPIRES) is None
|
||||
|
||||
assert entry_w5.unique_id == "083350000000-5"
|
||||
assert state_w5.attributes.get("device_class") == BinarySensorDeviceClass.SAFETY
|
||||
assert state_w5.state == STATE_OFF
|
||||
|
||||
|
||||
async def test_sensors_with_area_filter(
|
||||
hass: HomeAssistant, entity_registry: er.EntityRegistry
|
||||
hass: HomeAssistant, entity_registry: er.EntityRegistry, snapshot: SnapshotAssertion
|
||||
) -> None:
|
||||
"""Test the creation and values of the NINA sensors with an area filter."""
|
||||
"""Test the creation and values of the NINA sensors with a restrictive area filter."""
|
||||
|
||||
with patch(
|
||||
"pynina.baseApi.BaseAPI._makeRequest",
|
||||
wraps=mocked_request_function,
|
||||
):
|
||||
conf_entry: MockConfigEntry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
title="NINA",
|
||||
data=ENTRY_DATA_NO_AREA,
|
||||
version=1,
|
||||
minor_version=3,
|
||||
)
|
||||
conf_entry.add_to_hass(hass)
|
||||
conf_entry: MockConfigEntry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
title="NINA",
|
||||
data=ENTRY_DATA_SPECIFIC_AREA,
|
||||
version=1,
|
||||
minor_version=3,
|
||||
)
|
||||
conf_entry.add_to_hass(hass)
|
||||
|
||||
await hass.config_entries.async_setup(conf_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
await setup_platform(hass, conf_entry)
|
||||
|
||||
assert conf_entry.state is ConfigEntryState.LOADED
|
||||
state_w1 = hass.states.get("binary_sensor.nina_warning_aach_stadt_1")
|
||||
|
||||
state_w1 = hass.states.get("binary_sensor.nina_warning_aach_stadt_1")
|
||||
entry_w1 = entity_registry.async_get("binary_sensor.nina_warning_aach_stadt_1")
|
||||
assert state_w1.state == STATE_ON
|
||||
assert state_w1.attributes.get(ATTR_HEADLINE) == "Ausfall Notruf 112"
|
||||
|
||||
assert state_w1.state == STATE_ON
|
||||
state_w2 = hass.states.get("binary_sensor.nina_warning_aach_stadt_2")
|
||||
|
||||
assert entry_w1.unique_id == "083350000000-1"
|
||||
assert state_w1.attributes.get("device_class") == BinarySensorDeviceClass.SAFETY
|
||||
assert state_w2.state == STATE_OFF
|
||||
|
||||
state_w2 = hass.states.get("binary_sensor.nina_warning_aach_stadt_2")
|
||||
entry_w2 = entity_registry.async_get("binary_sensor.nina_warning_aach_stadt_2")
|
||||
state_w3 = hass.states.get("binary_sensor.nina_warning_aach_stadt_3")
|
||||
|
||||
assert state_w2.state == STATE_OFF
|
||||
assert state_w3.state == STATE_OFF
|
||||
|
||||
assert entry_w2.unique_id == "083350000000-2"
|
||||
assert state_w2.attributes.get("device_class") == BinarySensorDeviceClass.SAFETY
|
||||
state_w4 = hass.states.get("binary_sensor.nina_warning_aach_stadt_4")
|
||||
|
||||
state_w3 = hass.states.get("binary_sensor.nina_warning_aach_stadt_3")
|
||||
entry_w3 = entity_registry.async_get("binary_sensor.nina_warning_aach_stadt_3")
|
||||
assert state_w4.state == STATE_OFF
|
||||
|
||||
assert state_w3.state == STATE_OFF
|
||||
state_w5 = hass.states.get("binary_sensor.nina_warning_aach_stadt_5")
|
||||
|
||||
assert entry_w3.unique_id == "083350000000-3"
|
||||
assert state_w3.attributes.get("device_class") == BinarySensorDeviceClass.SAFETY
|
||||
|
||||
state_w4 = hass.states.get("binary_sensor.nina_warning_aach_stadt_4")
|
||||
entry_w4 = entity_registry.async_get("binary_sensor.nina_warning_aach_stadt_4")
|
||||
|
||||
assert state_w4.state == STATE_OFF
|
||||
|
||||
assert entry_w4.unique_id == "083350000000-4"
|
||||
assert state_w4.attributes.get("device_class") == BinarySensorDeviceClass.SAFETY
|
||||
|
||||
state_w5 = hass.states.get("binary_sensor.nina_warning_aach_stadt_5")
|
||||
entry_w5 = entity_registry.async_get("binary_sensor.nina_warning_aach_stadt_5")
|
||||
|
||||
assert state_w5.state == STATE_OFF
|
||||
|
||||
assert entry_w5.unique_id == "083350000000-5"
|
||||
assert state_w5.attributes.get("device_class") == BinarySensorDeviceClass.SAFETY
|
||||
assert state_w5.state == STATE_OFF
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from copy import deepcopy
|
||||
import json
|
||||
from typing import Any
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
@@ -28,14 +27,10 @@ from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import FlowResultType
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from . import mocked_request_function
|
||||
from . import mocked_request_function, setup_platform
|
||||
from .const import DUMMY_CONFIG_ENTRY, DUMMY_USER_INPUT
|
||||
|
||||
from tests.common import MockConfigEntry, load_fixture
|
||||
|
||||
DUMMY_RESPONSE_REGIONS: dict[str, Any] = json.loads(
|
||||
load_fixture("sample_regions.json", "nina")
|
||||
)
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
def assert_dummy_entry_created(result: dict[str, Any]) -> None:
|
||||
@@ -141,15 +136,15 @@ async def test_options_flow_init(
|
||||
hass: HomeAssistant, mock_setup_entry: AsyncMock, mock_config_entry: MockConfigEntry
|
||||
) -> None:
|
||||
"""Test config flow options."""
|
||||
|
||||
await setup_platform(hass, mock_config_entry)
|
||||
|
||||
with (
|
||||
patch(
|
||||
"pynina.baseApi.BaseAPI._makeRequest",
|
||||
wraps=mocked_request_function,
|
||||
),
|
||||
):
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
result = await hass.config_entries.options.async_init(
|
||||
mock_config_entry.entry_id
|
||||
)
|
||||
@@ -195,15 +190,15 @@ async def test_options_flow_with_no_selection(
|
||||
hass: HomeAssistant, mock_setup_entry: AsyncMock, mock_config_entry: MockConfigEntry
|
||||
) -> None:
|
||||
"""Test config flow options with no selection."""
|
||||
|
||||
await setup_platform(hass, mock_config_entry)
|
||||
|
||||
with (
|
||||
patch(
|
||||
"pynina.baseApi.BaseAPI._makeRequest",
|
||||
wraps=mocked_request_function,
|
||||
),
|
||||
):
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
result = await hass.config_entries.options.async_init(
|
||||
mock_config_entry.entry_id
|
||||
)
|
||||
@@ -264,13 +259,13 @@ async def test_options_flow_connection_error(
|
||||
hass: HomeAssistant, mock_setup_entry: AsyncMock, mock_config_entry: MockConfigEntry
|
||||
) -> None:
|
||||
"""Test config flow options but no connection."""
|
||||
|
||||
await setup_platform(hass, mock_config_entry)
|
||||
|
||||
with patch(
|
||||
"pynina.baseApi.BaseAPI._makeRequest",
|
||||
side_effect=ApiError("Could not connect to Api"),
|
||||
):
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
result = await hass.config_entries.options.async_init(
|
||||
mock_config_entry.entry_id
|
||||
)
|
||||
@@ -283,15 +278,15 @@ async def test_options_flow_unexpected_exception(
|
||||
hass: HomeAssistant, mock_setup_entry: AsyncMock, mock_config_entry: MockConfigEntry
|
||||
) -> None:
|
||||
"""Test config flow options but with an unexpected exception."""
|
||||
|
||||
await setup_platform(hass, mock_config_entry)
|
||||
|
||||
with (
|
||||
patch(
|
||||
"pynina.baseApi.BaseAPI._makeRequest",
|
||||
side_effect=Exception("DUMMY"),
|
||||
),
|
||||
):
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
result = await hass.config_entries.options.async_init(
|
||||
mock_config_entry.entry_id
|
||||
)
|
||||
@@ -313,15 +308,14 @@ async def test_options_flow_entity_removal(
|
||||
)
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
await setup_platform(hass, config_entry)
|
||||
|
||||
with (
|
||||
patch(
|
||||
"pynina.baseApi.BaseAPI._makeRequest",
|
||||
wraps=mocked_request_function,
|
||||
),
|
||||
):
|
||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
result = await hass.config_entries.options.async_init(config_entry.entry_id)
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
from collections.abc import Generator
|
||||
from datetime import timedelta
|
||||
from unittest.mock import MagicMock, patch
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
from pysaunum import SaunumData
|
||||
import pytest
|
||||
@@ -42,8 +42,8 @@ def mock_config_entry() -> MockConfigEntry:
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_saunum_client() -> Generator[MagicMock]:
|
||||
"""Return a mocked Saunum client for config flow and integration tests."""
|
||||
def mock_saunum_client_class() -> Generator[MagicMock]:
|
||||
"""Return a mocked Saunum client class for config flow and integration tests."""
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.saunum.config_flow.SaunumClient", autospec=True
|
||||
@@ -53,6 +53,8 @@ def mock_saunum_client() -> Generator[MagicMock]:
|
||||
mock_client = mock_client_class.return_value
|
||||
mock_client.is_connected = True
|
||||
|
||||
mock_client_class.create = AsyncMock(return_value=mock_client)
|
||||
|
||||
# Create mock data for async_get_data
|
||||
mock_data = SaunumData(
|
||||
session_active=False,
|
||||
@@ -76,7 +78,13 @@ def mock_saunum_client() -> Generator[MagicMock]:
|
||||
|
||||
mock_client.async_get_data.return_value = mock_data
|
||||
|
||||
yield mock_client
|
||||
yield mock_client_class
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_saunum_client(mock_saunum_client_class: MagicMock) -> MagicMock:
|
||||
"""Return a mocked Saunum client instance."""
|
||||
return mock_saunum_client_class.return_value
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
||||
@@ -49,14 +49,13 @@ async def test_full_flow(hass: HomeAssistant, mock_setup_entry: AsyncMock) -> No
|
||||
)
|
||||
async def test_form_errors(
|
||||
hass: HomeAssistant,
|
||||
mock_saunum_client,
|
||||
mock_saunum_client_class,
|
||||
side_effect: Exception,
|
||||
error_base: str,
|
||||
mock_setup_entry: AsyncMock,
|
||||
) -> None:
|
||||
"""Test error handling and recovery."""
|
||||
mock_saunum_client.connect.side_effect = side_effect
|
||||
|
||||
mock_saunum_client_class.create.side_effect = side_effect
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
@@ -69,8 +68,8 @@ async def test_form_errors(
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["errors"] == {"base": error_base}
|
||||
|
||||
# Test recovery - clear the error and try again
|
||||
mock_saunum_client.connect.side_effect = None
|
||||
# Test recovery - try again without the error
|
||||
mock_saunum_client_class.create.side_effect = None
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
@@ -139,20 +138,20 @@ async def test_reconfigure_flow(
|
||||
async def test_reconfigure_errors(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_saunum_client,
|
||||
mock_saunum_client_class,
|
||||
side_effect: Exception,
|
||||
error_base: str,
|
||||
mock_setup_entry: AsyncMock,
|
||||
) -> None:
|
||||
"""Test reconfigure flow error handling."""
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
mock_saunum_client.connect.side_effect = side_effect
|
||||
|
||||
result = await mock_config_entry.start_reconfigure_flow(hass)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
mock_saunum_client_class.create.side_effect = side_effect
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
TEST_RECONFIGURE_INPUT,
|
||||
@@ -161,8 +160,8 @@ async def test_reconfigure_errors(
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["errors"] == {"base": error_base}
|
||||
|
||||
# Test recovery - clear the error and try again
|
||||
mock_saunum_client.connect.side_effect = None
|
||||
# Test recovery - try again without the error
|
||||
mock_saunum_client_class.create.side_effect = None
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
"""Test Saunum Leil integration setup and teardown."""
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
from pysaunum import SaunumConnectionError
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
@@ -35,9 +37,11 @@ async def test_async_setup_entry_connection_failed(
|
||||
"""Test integration setup fails when connection cannot be established."""
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
|
||||
mock_saunum_client.connect.side_effect = SaunumConnectionError("Connection failed")
|
||||
|
||||
assert not await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
with patch(
|
||||
"homeassistant.components.saunum.SaunumClient.create",
|
||||
side_effect=SaunumConnectionError("Connection failed"),
|
||||
):
|
||||
assert not await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
|
||||
assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY
|
||||
|
||||
|
||||
@@ -383,6 +383,13 @@ async def test_smoke_co_notification_sensors(
|
||||
assert entity_entry
|
||||
assert entity_entry.entity_category == EntityCategory.DIAGNOSTIC
|
||||
|
||||
# Test that no idle states are created as entities
|
||||
entity_id = "binary_sensor.zcombo_g_smoke_co_alarm_idle"
|
||||
state = hass.states.get(entity_id)
|
||||
assert state is None
|
||||
entity_entry = entity_registry.async_get(entity_id)
|
||||
assert entity_entry is None
|
||||
|
||||
# Test state updates for smoke alarm
|
||||
event = Event(
|
||||
type="value updated",
|
||||
|
||||
Reference in New Issue
Block a user