Compare commits

..

11 Commits
dev ... 168007

Author SHA1 Message Date
Daniel Hjelseth Høyer
30a0b5812d Update homeassistant/components/tibber/coordinator.py
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-13 17:12:12 +02:00
Daniel Hjelseth Høyer
7d20308fda Update homeassistant/components/tibber/coordinator.py
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-13 17:11:37 +02:00
Daniel Hjelseth Høyer
6fe2132e88 Update homeassistant/components/tibber/coordinator.py
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-13 17:11:14 +02:00
Daniel Hjelseth Høyer
bbcaf8fd2d Merge branch 'dev' into 168007 2026-04-13 17:00:25 +02:00
Daniel Hjelseth Høyer
442df2733a Improve data updating for Tibber, WIP
Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>
2026-04-13 08:27:34 +02:00
Daniel Hjelseth Høyer
bb46c1c2ac Improve data updating for Tibber
Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>
2026-04-12 21:44:04 +02:00
Daniel Hjelseth Høyer
0052055db5 Improve data updating for Tibber
Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>
2026-04-12 21:26:30 +02:00
Daniel Hjelseth Høyer
300ed2ac1e Improve data updating for Tibber
Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>
2026-04-12 21:15:54 +02:00
Daniel Hjelseth Høyer
58031a87cc Improve data updating for Tibber
Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>
2026-04-12 21:00:03 +02:00
Daniel Hjelseth Høyer
a3fadbd7b9 Improve data updating for Tibber
Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>
2026-04-12 20:47:29 +02:00
Daniel Hjelseth Høyer
525335b022 Improve data updating for Tibber
Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>
2026-04-12 20:31:23 +02:00
25 changed files with 107 additions and 271 deletions

View File

@@ -8,5 +8,5 @@
"iot_class": "cloud_polling",
"loggers": ["aioamazondevices"],
"quality_scale": "platinum",
"requirements": ["aioamazondevices==13.4.1"]
"requirements": ["aioamazondevices==13.4.0"]
}

View File

@@ -14,8 +14,6 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from . import SIGNAL_CONNECTED, AppleTvConfigEntry
from .entity import AppleTVEntity
PARALLEL_UPDATES = 0
async def async_setup_entry(
hass: HomeAssistant,

View File

@@ -16,7 +16,6 @@ from .const import DOMAIN
from .coordinator import CometBlueConfigEntry, CometBlueDataUpdateCoordinator
PLATFORMS: list[Platform] = [
Platform.BUTTON,
Platform.CLIMATE,
]

View File

@@ -1,61 +0,0 @@
"""Comet Blue button platform."""
from __future__ import annotations
from homeassistant.components.button import ButtonEntity, ButtonEntityDescription
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.util import dt as dt_util
from .coordinator import CometBlueConfigEntry, CometBlueDataUpdateCoordinator
from .entity import CometBlueBluetoothEntity
PARALLEL_UPDATES = 1
DESCRIPTIONS = [
ButtonEntityDescription(
key="sync_time",
translation_key="sync_time",
entity_category=EntityCategory.CONFIG,
),
]
async def async_setup_entry(
hass: HomeAssistant,
entry: CometBlueConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up the client entities."""
coordinator = entry.runtime_data
async_add_entities(
[
CometBlueButtonEntity(coordinator, description)
for description in DESCRIPTIONS
]
)
class CometBlueButtonEntity(CometBlueBluetoothEntity, ButtonEntity):
"""Representation of a button."""
def __init__(
self,
coordinator: CometBlueDataUpdateCoordinator,
description: ButtonEntityDescription,
) -> None:
"""Initialize CometBlueButtonEntity."""
super().__init__(coordinator)
self.entity_description = description
self._attr_unique_id = f"{coordinator.address}-{description.key}"
async def async_press(self) -> None:
"""Handle the button press."""
if self.entity_description.key == "sync_time":
await self.coordinator.send_command(
self.coordinator.device.set_datetime_async, {"date": dt_util.now()}
)

View File

@@ -1,9 +0,0 @@
{
"entity": {
"button": {
"sync_time": {
"default": "mdi:calendar-clock"
}
}
}
}

View File

@@ -29,12 +29,5 @@
}
}
}
},
"entity": {
"button": {
"sync_time": {
"name": "Sync time"
}
}
}
}

View File

@@ -57,6 +57,8 @@ async def async_setup_entry(
class JellyfinMediaPlayer(JellyfinClientEntity, MediaPlayerEntity):
"""Represents a Jellyfin Player device."""
_attr_media_image_remotely_accessible = False
def __init__(
self,
coordinator: JellyfinDataUpdateCoordinator,

View File

@@ -282,16 +282,12 @@ def _humanify(
else:
continue
row_time_fired_ts = row[TIME_FIRED_TS_POS]
# Explicit None check: 0.0 is a valid epoch.
time_fired_ts: float = (
row_time_fired_ts if row_time_fired_ts is not None else time.time()
)
time_fired_ts = row[TIME_FIRED_TS_POS]
if timestamp:
when: str | float = time_fired_ts
when = time_fired_ts or time.time()
else:
when = process_timestamp_to_utc_isoformat(
dt_util.utc_from_timestamp(time_fired_ts)
dt_util.utc_from_timestamp(time_fired_ts) or dt_util.utcnow()
)
data[LOGBOOK_ENTRY_WHEN] = when

View File

@@ -9,7 +9,7 @@ from __future__ import annotations
import logging
from luftdaten import Luftdaten
from luftdaten.exceptions import LuftdatenConnectionError, LuftdatenError
from luftdaten.exceptions import LuftdatenError
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
@@ -47,22 +47,11 @@ class LuftdatenDataUpdateCoordinator(DataUpdateCoordinator[dict[str, float | int
"""Update sensor/binary sensor data."""
try:
await self._sensor_community.get_data()
except LuftdatenConnectionError as err:
raise UpdateFailed(
translation_domain=DOMAIN,
translation_key="communication_error",
) from err
except LuftdatenError as err:
raise UpdateFailed(
translation_domain=DOMAIN,
translation_key="unknown_error",
) from err
raise UpdateFailed("Unable to retrieve data from Sensor.Community") from err
if not self._sensor_community.values:
raise UpdateFailed(
translation_domain=DOMAIN,
translation_key="no_data_received",
)
raise UpdateFailed("Did not receive sensor data from Sensor.Community")
data: dict[str, float | int] = self._sensor_community.values
data.update(self._sensor_community.meta)

View File

@@ -21,16 +21,5 @@
"sensor": {
"pressure_at_sealevel": { "name": "Pressure at sea level" }
}
},
"exceptions": {
"communication_error": {
"message": "An error occurred while communicating with the Sensor.Community service."
},
"no_data_received": {
"message": "Did not receive sensor data from the Sensor.Community service."
},
"unknown_error": {
"message": "An unknown error occurred while communicating with the Sensor.Community service."
}
}
}

View File

@@ -15,7 +15,6 @@ DEFAULT_NAME: Final = "Mastodon"
ATTR_ACCOUNT_NAME = "account_name"
ATTR_STATUS = "status"
ATTR_VISIBILITY = "visibility"
ATTR_QUOTE_APPROVAL_POLICY = "quote_approval_policy"
ATTR_IDEMPOTENCY_KEY = "idempotency_key"
ATTR_CONTENT_WARNING = "content_warning"
ATTR_MEDIA_WARNING = "media_warning"

View File

@@ -52,7 +52,6 @@ from .const import (
ATTR_MEDIA_DESCRIPTION,
ATTR_MEDIA_WARNING,
ATTR_NOTE,
ATTR_QUOTE_APPROVAL_POLICY,
ATTR_STATUS,
ATTR_VALUE,
ATTR_VISIBILITY,
@@ -74,14 +73,6 @@ class StatusVisibility(StrEnum):
DIRECT = "direct"
class QuoteApprovalPolicy(StrEnum):
"""QuoteApprovalPolicy model."""
PUBLIC = "public"
FOLLOWERS = "followers"
NOBODY = "nobody"
SERVICE_GET_ACCOUNT = "get_account"
SERVICE_GET_ACCOUNT_SCHEMA = vol.Schema(
{
@@ -116,9 +107,6 @@ SERVICE_POST_SCHEMA = vol.Schema(
vol.Required(ATTR_CONFIG_ENTRY_ID): str,
vol.Required(ATTR_STATUS): str,
vol.Optional(ATTR_VISIBILITY): vol.In([x.lower() for x in StatusVisibility]),
vol.Optional(ATTR_QUOTE_APPROVAL_POLICY): vol.In(
[x.lower() for x in QuoteApprovalPolicy]
),
vol.Optional(ATTR_IDEMPOTENCY_KEY): str,
vol.Optional(ATTR_CONTENT_WARNING): str,
vol.Optional(ATTR_LANGUAGE): str,
@@ -299,11 +287,6 @@ async def _async_post(call: ServiceCall) -> ServiceResponse:
if ATTR_VISIBILITY in call.data
else None
)
quote_approval_policy: str | None = (
QuoteApprovalPolicy(call.data[ATTR_QUOTE_APPROVAL_POLICY])
if ATTR_QUOTE_APPROVAL_POLICY in call.data
else None
)
idempotency_key: str | None = call.data.get(ATTR_IDEMPOTENCY_KEY)
spoiler_text: str | None = call.data.get(ATTR_CONTENT_WARNING)
language: str | None = call.data.get(ATTR_LANGUAGE)
@@ -324,7 +307,6 @@ async def _async_post(call: ServiceCall) -> ServiceResponse:
client=client,
status=status,
visibility=visibility,
quote_approval_policy=quote_approval_policy,
idempotency_key=idempotency_key,
spoiler_text=spoiler_text,
language=language,

View File

@@ -50,14 +50,6 @@ post:
- private
- direct
translation_key: post_visibility
quote_approval_policy:
selector:
select:
options:
- public
- followers
- nobody
translation_key: quote_approval_policy
idempotency_key:
selector:
text:

View File

@@ -152,13 +152,6 @@
"public": "Public - Visible to everyone",
"unlisted": "Unlisted - Public but not shown in public timelines"
}
},
"quote_approval_policy": {
"options": {
"followers": "Followers - Only accounts that follow you can quote this post",
"nobody": "Nobody - No one but you can quote this post",
"public": "Public - Anyone can quote this post"
}
}
},
"services": {
@@ -229,10 +222,6 @@
"description": "If an image or video is attached, will mark the media as sensitive (default: no media warning).",
"name": "Media warning"
},
"quote_approval_policy": {
"description": "Who can quote this post (default: account setting).\nIgnored if visibility is private or direct.",
"name": "Who can quote"
},
"status": {
"description": "The status to post.",
"name": "Status"

View File

@@ -2,12 +2,12 @@
from __future__ import annotations
from vehicle import RDW, RDWConnectionError, RDWError, Vehicle
from vehicle import RDW, Vehicle
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from .const import CONF_LICENSE_PLATE, DOMAIN, LOGGER, SCAN_INTERVAL
@@ -35,15 +35,4 @@ class RDWDataUpdateCoordinator(DataUpdateCoordinator[Vehicle]):
async def _async_update_data(self) -> Vehicle:
"""Fetch data from RDW."""
try:
return await self._rdw.vehicle()
except RDWConnectionError as err:
raise UpdateFailed(
translation_domain=DOMAIN,
translation_key="communication_error",
) from err
except RDWError as err:
raise UpdateFailed(
translation_domain=DOMAIN,
translation_key="unknown_error",
) from err
return await self._rdw.vehicle()

View File

@@ -35,13 +35,5 @@
"name": "Ascription date"
}
}
},
"exceptions": {
"communication_error": {
"message": "An error occurred while communicating with the RDW service."
},
"unknown_error": {
"message": "An unknown error occurred while communicating with the RDW service."
}
}
}

View File

@@ -916,7 +916,6 @@ async def update_listener(hass: HomeAssistant, entry: TelegramBotConfigEntry) ->
if entry.runtime_data.old_config_data != entry.data:
# Reload if config data has changed
hass.config_entries.async_schedule_reload(entry.entry_id)
return
# reload entities
await hass.config_entries.async_unload_platforms(entry, PLATFORMS)

View File

@@ -24,8 +24,9 @@ from homeassistant.components.recorder.statistics import (
statistics_during_period,
)
from homeassistant.const import UnitOfEnergy
from homeassistant.core import HomeAssistant
from homeassistant.core import CALLBACK_TYPE, HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed
from homeassistant.helpers.event import async_track_point_in_utc_time
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from homeassistant.util import dt as dt_util
from homeassistant.util.unit_conversion import EnergyConverter
@@ -255,7 +256,7 @@ class TibberDataCoordinator(DataUpdateCoordinator[None]):
class TibberPriceCoordinator(DataUpdateCoordinator[dict[str, TibberHomeData]]):
"""Handle Tibber price data and insert statistics."""
"""Handle Tibber price sensor updates."""
config_entry: TibberConfigEntry
@@ -272,7 +273,41 @@ class TibberPriceCoordinator(DataUpdateCoordinator[dict[str, TibberHomeData]]):
name=f"{DOMAIN} price",
update_interval=timedelta(minutes=1),
)
self._tomorrow_price_poll_threshold_seconds = random.uniform(0, 3600 * 10)
self._tomorrow_price_poll_threshold_seconds = random.uniform(
3600 * 14, 3600 * 23
)
self._unsub_tomorrow_price_poll: CALLBACK_TYPE | None = None
initial_tomorrow_price_poll = dt_util.start_of_local_day() + timedelta(
seconds=self._tomorrow_price_poll_threshold_seconds
)
if initial_tomorrow_price_poll <= dt_util.utcnow():
initial_tomorrow_price_poll += timedelta(days=1)
self._schedule_tomorrow_price_poll(initial_tomorrow_price_poll)
self._tibber_homes: list[tibber.TibberHome] | None = None
async def async_shutdown(self) -> None:
"""Cancel any scheduled call, and ignore new runs."""
await super().async_shutdown()
if self._unsub_tomorrow_price_poll:
self._unsub_tomorrow_price_poll()
self._unsub_tomorrow_price_poll = None
def _schedule_tomorrow_price_poll(self, point_in_time: datetime) -> None:
"""Schedule the next one-shot tomorrow price poll."""
if point_in_time <= (now := dt_util.utcnow()):
point_in_time = now + timedelta(seconds=1)
if self._unsub_tomorrow_price_poll:
self._unsub_tomorrow_price_poll()
self._unsub_tomorrow_price_poll = async_track_point_in_utc_time(
self.hass,
self._async_handle_tomorrow_price_poll,
point_in_time,
)
async def _async_handle_tomorrow_price_poll(self, _: datetime) -> None:
"""Handle the scheduled tomorrow price poll."""
self._unsub_tomorrow_price_poll = None
await self._fetch_data()
def _time_until_next_15_minute(self) -> timedelta:
"""Return time until the next 15-minute boundary (0, 15, 30, 45) in UTC."""
@@ -289,7 +324,23 @@ class TibberPriceCoordinator(DataUpdateCoordinator[dict[str, TibberHomeData]]):
return next_run - now
async def _async_update_data(self) -> dict[str, TibberHomeData]:
"""Update data via API and return per-home data for sensors."""
if self._tibber_homes is None:
await self._fetch_data()
homes = self._tibber_homes
if homes is None:
raise UpdateFailed("No Tibber homes available")
result = {home.home_id: _build_home_data(home) for home in homes}
self.update_interval = self._time_until_next_15_minute()
return result
async def _fetch_data(self) -> None:
"""Fetch latest price data via API and update cached home data."""
self._schedule_tomorrow_price_poll(
dt_util.utcnow() + timedelta(seconds=random.uniform(60, 60 * 10))
)
tibber_connection = await self.config_entry.runtime_data.async_get_client(
self.hass
)
@@ -330,21 +381,31 @@ class TibberPriceCoordinator(DataUpdateCoordinator[dict[str, TibberHomeData]]):
return False
homes_to_update = [home for home in active_homes if _needs_update(home)]
try:
if homes_to_update:
await asyncio.gather(
*(home.update_info_and_price_info() for home in homes_to_update)
)
except tibber.RetryableHttpExceptionError as err:
raise UpdateFailed(f"Error communicating with API ({err.status})") from err
except tibber.FatalHttpExceptionError as err:
raise UpdateFailed(f"Error communicating with API ({err.status})") from err
except tibber.exceptions.RateLimitExceededError as err:
self._schedule_tomorrow_price_poll(
dt_util.utcnow() + timedelta(seconds=err.retry_after)
)
raise UpdateFailed(
f"Rate limit exceeded, retry after {err.retry_after} seconds",
retry_after=err.retry_after,
) from err
except tibber.exceptions.HttpExceptionError as err:
raise UpdateFailed(f"Error communicating with API ({err})") from err
result = {home.home_id: _build_home_data(home) for home in active_homes}
self._schedule_tomorrow_price_poll(
dt_util.start_of_local_day(now)
+ timedelta(days=1, seconds=self._tomorrow_price_poll_threshold_seconds)
)
self._tibber_homes = active_homes
self.update_interval = self._time_until_next_15_minute()
return result
class TibberFetchPriceCoordinator(TibberPriceCoordinator):
"""Backward-compatible alias for the merged price fetch coordinator."""
class TibberDataAPICoordinator(DataUpdateCoordinator[dict[str, TibberDevice]]):

View File

@@ -8,5 +8,5 @@
"integration_type": "hub",
"iot_class": "cloud_polling",
"loggers": ["tibber"],
"requirements": ["pyTibber==0.37.0"]
"requirements": ["pyTibber==0.37.1"]
}

4
requirements_all.txt generated
View File

@@ -190,7 +190,7 @@ aioairzone-cloud==0.7.2
aioairzone==1.0.5
# homeassistant.components.alexa_devices
aioamazondevices==13.4.1
aioamazondevices==13.4.0
# homeassistant.components.ambient_network
# homeassistant.components.ambient_station
@@ -1928,7 +1928,7 @@ pyRFXtrx==0.31.1
pySDCP==1
# homeassistant.components.tibber
pyTibber==0.37.0
pyTibber==0.37.1
# homeassistant.components.dlink
pyW215==0.8.0

View File

@@ -181,7 +181,7 @@ aioairzone-cloud==0.7.2
aioairzone==1.0.5
# homeassistant.components.alexa_devices
aioamazondevices==13.4.1
aioamazondevices==13.4.0
# homeassistant.components.ambient_network
# homeassistant.components.ambient_station
@@ -1671,7 +1671,7 @@ pyHomee==1.3.8
pyRFXtrx==0.31.1
# homeassistant.components.tibber
pyTibber==0.37.0
pyTibber==0.37.1
# homeassistant.components.dlink
pyW215==0.8.0

View File

@@ -1,37 +0,0 @@
"""Test the eurotronic_cometblue button platform."""
from unittest.mock import patch
from freezegun.api import FrozenDateTimeFactory
from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN
from homeassistant.const import ATTR_ENTITY_ID, Platform
from homeassistant.core import HomeAssistant
from homeassistant.util import dt as dt_util
from .conftest import setup_with_selected_platforms
from tests.common import MockConfigEntry
async def test_button_press_sync_time(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test that pressing the sync-time button sets the device datetime."""
await setup_with_selected_platforms(hass, mock_config_entry, [Platform.BUTTON])
# Check if button is called correctly
with patch.object(
mock_config_entry.runtime_data.device,
"set_datetime_async",
) as mock_set_datetime:
await hass.services.async_call(
BUTTON_DOMAIN,
"press",
{ATTR_ENTITY_ID: "button.comet_blue_aa_bb_cc_dd_ee_ff_sync_time"},
blocking=True,
)
mock_set_datetime.assert_called_once_with(date=dt_util.now())

View File

@@ -1,9 +1,8 @@
"""Tests for the Luftdaten integration."""
from unittest.mock import MagicMock
from unittest.mock import AsyncMock, MagicMock, patch
from luftdaten.exceptions import LuftdatenConnectionError, LuftdatenError
import pytest
from luftdaten.exceptions import LuftdatenError
from homeassistant.components.luftdaten.const import DOMAIN
from homeassistant.config_entries import ConfigEntryState
@@ -15,7 +14,7 @@ from tests.common import MockConfigEntry
async def test_load_unload_config_entry(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_luftdaten: MagicMock,
mock_luftdaten: AsyncMock,
) -> None:
"""Test the Luftdaten configuration entry loading/unloading."""
mock_config_entry.add_to_hass(hass)
@@ -31,21 +30,21 @@ async def test_load_unload_config_entry(
assert mock_config_entry.state is ConfigEntryState.NOT_LOADED
@pytest.mark.parametrize("side_effect", [LuftdatenConnectionError, LuftdatenError])
@patch(
"homeassistant.components.luftdaten.Luftdaten.get_data",
side_effect=LuftdatenError,
)
async def test_config_entry_not_ready(
mock_get_data: MagicMock,
hass: HomeAssistant,
mock_luftdaten: MagicMock,
mock_config_entry: MockConfigEntry,
side_effect: type[Exception],
) -> None:
"""Test the Luftdaten configuration entry not ready."""
mock_luftdaten.get_data.side_effect = side_effect
mock_config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()
assert mock_luftdaten.get_data.call_count == 1
assert mock_get_data.call_count == 1
assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY

View File

@@ -34,7 +34,6 @@ from homeassistant.components.mastodon.const import (
ATTR_MEDIA,
ATTR_MEDIA_DESCRIPTION,
ATTR_NOTE,
ATTR_QUOTE_APPROVAL_POLICY,
ATTR_STATUS,
ATTR_VISIBILITY,
DOMAIN,
@@ -379,7 +378,6 @@ async def test_unmute_account_failure_api_error(
"status": "test toot",
"spoiler_text": None,
"visibility": None,
"quote_approval_policy": None,
"idempotency_key": None,
"language": None,
"media_ids": None,
@@ -392,7 +390,6 @@ async def test_unmute_account_failure_api_error(
"status": "test toot",
"spoiler_text": None,
"visibility": "private",
"quote_approval_policy": None,
"idempotency_key": None,
"language": None,
"media_ids": None,
@@ -409,7 +406,6 @@ async def test_unmute_account_failure_api_error(
"status": "test toot",
"spoiler_text": "Spoiler",
"visibility": "private",
"quote_approval_policy": None,
"idempotency_key": None,
"language": None,
"media_ids": None,
@@ -427,7 +423,6 @@ async def test_unmute_account_failure_api_error(
"status": "test toot",
"spoiler_text": "Spoiler",
"visibility": None,
"quote_approval_policy": None,
"idempotency_key": None,
"language": "nl",
"media_ids": "1",
@@ -446,7 +441,6 @@ async def test_unmute_account_failure_api_error(
"status": "test toot",
"spoiler_text": "Spoiler",
"visibility": None,
"quote_approval_policy": None,
"idempotency_key": None,
"language": "en",
"media_ids": "1",
@@ -460,7 +454,6 @@ async def test_unmute_account_failure_api_error(
"language": "invalid-lang",
"spoiler_text": None,
"visibility": None,
"quote_approval_policy": None,
"idempotency_key": None,
"media_ids": None,
"sensitive": None,
@@ -477,20 +470,6 @@ async def test_unmute_account_failure_api_error(
"language": None,
"spoiler_text": None,
"visibility": None,
"quote_approval_policy": None,
"media_ids": None,
"sensitive": None,
},
),
(
{ATTR_STATUS: "test toot", ATTR_QUOTE_APPROVAL_POLICY: "followers"},
{
"status": "test toot",
"spoiler_text": None,
"visibility": None,
"quote_approval_policy": "followers",
"idempotency_key": None,
"language": None,
"media_ids": None,
"sensitive": None,
},
@@ -549,7 +528,6 @@ async def test_service_post(
"status": "test toot",
"spoiler_text": "Spoiler",
"visibility": None,
"quote_approval_policy": None,
"idempotency_key": None,
"media_ids": "1",
"media_description": None,

View File

@@ -1,9 +1,6 @@
"""Tests for the RDW integration."""
from unittest.mock import MagicMock
import pytest
from vehicle import RDWConnectionError, RDWError
from unittest.mock import AsyncMock, MagicMock, patch
from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant
@@ -14,7 +11,7 @@ from tests.common import MockConfigEntry
async def test_load_unload_config_entry(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_rdw: MagicMock,
mock_rdw: AsyncMock,
) -> None:
"""Test the RDW configuration entry loading/unloading."""
mock_config_entry.add_to_hass(hass)
@@ -29,19 +26,19 @@ async def test_load_unload_config_entry(
assert mock_config_entry.state is ConfigEntryState.NOT_LOADED
@pytest.mark.parametrize("side_effect", [RDWConnectionError, RDWError])
@patch(
"homeassistant.components.rdw.coordinator.RDW.vehicle",
side_effect=RuntimeError,
)
async def test_config_entry_not_ready(
mock_request: MagicMock,
hass: HomeAssistant,
mock_rdw: MagicMock,
mock_config_entry: MockConfigEntry,
side_effect: type[Exception],
) -> None:
"""Test the RDW configuration entry not ready."""
mock_rdw.vehicle.side_effect = side_effect
mock_config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()
assert mock_rdw.vehicle.call_count == 1
assert mock_request.call_count == 1
assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY