mirror of
https://github.com/home-assistant/core.git
synced 2025-07-22 12:47:08 +00:00
Add Squeezebox service update entities (#125764)
* first cut at update entties * remove sensors for now * make update vserion less wordy * fix re escape * Use name * use Caps * fix translation * move all data manipulation to data prepare fn, refine regexes and provide as much info as possible * fix formatting * update return type * fix class inherit * Fix ruff * update tests * fix spelling * ruff * Update homeassistant/components/squeezebox/update.py Co-authored-by: Raj Laud <50647620+rajlaud@users.noreply.github.com> * Update tests/components/squeezebox/test_update.py Co-authored-by: Raj Laud <50647620+rajlaud@users.noreply.github.com> * Update tests/components/squeezebox/test_update.py Co-authored-by: Raj Laud <50647620+rajlaud@users.noreply.github.com> * Update tests/components/squeezebox/test_update.py Co-authored-by: Raj Laud <50647620+rajlaud@users.noreply.github.com> * Update tests/components/squeezebox/test_update.py Co-authored-by: Raj Laud <50647620+rajlaud@users.noreply.github.com> * Update tests/components/squeezebox/test_update.py Co-authored-by: Raj Laud <50647620+rajlaud@users.noreply.github.com> * fix tests * ruff * update text based on feedback from docs * make the plugin update entity smarter * update plugin updater tests * define attr * Callable type * callable guard * ruff * add local release info page * fix typing * refactor use release notes for LMS update * Make update simple and produce a release summary instead * Update tests * Fix tests * Tighten english * test for restart fail * be more explicit with coordinator error * remove unused regex * revert error msg unrealted * Fix newline * Fix socket usage during tests * Simplify based on new lib version * CI Fixes * fix typing * fix enitiy call back * fix enitiy call back types * remove some unrelated titdying --------- Co-authored-by: Raj Laud <50647620+rajlaud@users.noreply.github.com> Co-authored-by: Franck Nijhof <git@frenck.dev>
This commit is contained in:
parent
ed6cfa42f0
commit
bd28452807
@ -56,6 +56,7 @@ PLATFORMS = [
|
||||
Platform.BUTTON,
|
||||
Platform.MEDIA_PLAYER,
|
||||
Platform.SENSOR,
|
||||
Platform.UPDATE,
|
||||
]
|
||||
|
||||
|
||||
|
@ -13,8 +13,6 @@ SERVER_MODEL = "Lyrion Music Server"
|
||||
STATUS_API_TIMEOUT = 10
|
||||
STATUS_SENSOR_LASTSCAN = "lastscan"
|
||||
STATUS_SENSOR_NEEDSRESTART = "needsrestart"
|
||||
STATUS_SENSOR_NEWVERSION = "newversion"
|
||||
STATUS_SENSOR_NEWPLUGINS = "newplugins"
|
||||
STATUS_SENSOR_RESCAN = "rescan"
|
||||
STATUS_SENSOR_INFO_TOTAL_ALBUMS = "info total albums"
|
||||
STATUS_SENSOR_INFO_TOTAL_ARTISTS = "info total artists"
|
||||
@ -27,6 +25,8 @@ STATUS_QUERY_LIBRARYNAME = "libraryname"
|
||||
STATUS_QUERY_MAC = "mac"
|
||||
STATUS_QUERY_UUID = "uuid"
|
||||
STATUS_QUERY_VERSION = "version"
|
||||
STATUS_UPDATE_NEWVERSION = "newversion"
|
||||
STATUS_UPDATE_NEWPLUGINS = "newplugins"
|
||||
SQUEEZEBOX_SOURCE_STRINGS = (
|
||||
"source:",
|
||||
"wavin:",
|
||||
@ -44,3 +44,5 @@ DEFAULT_VOLUME_STEP = 5
|
||||
ATTR_ANNOUNCE_VOLUME = "announce_volume"
|
||||
ATTR_ANNOUNCE_TIMEOUT = "announce_timeout"
|
||||
UNPLAYABLE_TYPES = ("text", "actions")
|
||||
UPDATE_PLUGINS_RELEASE_SUMMARY = "update_plugins_release_summary"
|
||||
UPDATE_RELEASE_SUMMARY = "update_release_summary"
|
||||
|
@ -6,7 +6,6 @@ from asyncio import timeout
|
||||
from collections.abc import Callable
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
import re
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from pysqueezebox import Player, Server
|
||||
@ -14,7 +13,6 @@ from pysqueezebox import Player, Server
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from . import SqueezeboxConfigEntry
|
||||
@ -24,9 +22,6 @@ from .const import (
|
||||
SENSOR_UPDATE_INTERVAL,
|
||||
SIGNAL_PLAYER_REDISCOVERED,
|
||||
STATUS_API_TIMEOUT,
|
||||
STATUS_SENSOR_LASTSCAN,
|
||||
STATUS_SENSOR_NEEDSRESTART,
|
||||
STATUS_SENSOR_RESCAN,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@ -50,7 +45,16 @@ class LMSStatusDataUpdateCoordinator(DataUpdateCoordinator):
|
||||
always_update=False,
|
||||
)
|
||||
self.lms = lms
|
||||
self.newversion_regex = re.compile("<.*$")
|
||||
self.can_server_restart = False
|
||||
|
||||
async def _async_setup(self) -> None:
|
||||
"""Query LMS capabilities."""
|
||||
result = await self.lms.async_query("can", "restartserver", "?")
|
||||
if result and "_can" in result and result["_can"] == 1:
|
||||
_LOGGER.debug("Can restart %s", self.lms.name)
|
||||
self.can_server_restart = True
|
||||
else:
|
||||
_LOGGER.warning("Can't query server capabilities %s", self.lms.name)
|
||||
|
||||
async def _async_update_data(self) -> dict:
|
||||
"""Fetch data from LMS status call.
|
||||
@ -58,32 +62,12 @@ class LMSStatusDataUpdateCoordinator(DataUpdateCoordinator):
|
||||
Then we process only a subset to make then nice for HA
|
||||
"""
|
||||
async with timeout(STATUS_API_TIMEOUT):
|
||||
data = await self.lms.async_status()
|
||||
data: dict | None = await self.lms.async_prepared_status()
|
||||
|
||||
if not data:
|
||||
raise UpdateFailed("No data from status poll")
|
||||
_LOGGER.debug("Raw serverstatus %s=%s", self.lms.name, data)
|
||||
|
||||
return self._prepare_status_data(data)
|
||||
|
||||
def _prepare_status_data(self, data: dict) -> dict:
|
||||
"""Sensors that need the data changing for HA presentation."""
|
||||
|
||||
# Binary sensors
|
||||
# rescan bool are we rescanning alter poll not present if false
|
||||
data[STATUS_SENSOR_RESCAN] = STATUS_SENSOR_RESCAN in data
|
||||
# needsrestart bool pending lms plugin updates not present if false
|
||||
data[STATUS_SENSOR_NEEDSRESTART] = STATUS_SENSOR_NEEDSRESTART in data
|
||||
|
||||
# Sensors that need special handling
|
||||
# 'lastscan': '1718431678', epoc -> ISO 8601 not always present
|
||||
data[STATUS_SENSOR_LASTSCAN] = (
|
||||
dt_util.utc_from_timestamp(int(data[STATUS_SENSOR_LASTSCAN]))
|
||||
if STATUS_SENSOR_LASTSCAN in data
|
||||
else None
|
||||
)
|
||||
|
||||
_LOGGER.debug("Processed serverstatus %s=%s", self.lms.name, data)
|
||||
return data
|
||||
|
||||
|
||||
|
@ -125,6 +125,14 @@
|
||||
"name": "Player count off service",
|
||||
"unit_of_measurement": "[%key:component::squeezebox::entity::sensor::player_count::unit_of_measurement%]"
|
||||
}
|
||||
},
|
||||
"update": {
|
||||
"newversion": {
|
||||
"name": "Lyrion Music Server"
|
||||
},
|
||||
"newplugins": {
|
||||
"name": "Updated plugins"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
|
170
homeassistant/components/squeezebox/update.py
Normal file
170
homeassistant/components/squeezebox/update.py
Normal file
@ -0,0 +1,170 @@
|
||||
"""Platform for update integration for squeezebox."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from datetime import datetime
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.components.update import (
|
||||
UpdateEntity,
|
||||
UpdateEntityDescription,
|
||||
UpdateEntityFeature,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.event import async_call_later
|
||||
|
||||
from . import SqueezeboxConfigEntry
|
||||
from .const import (
|
||||
SERVER_MODEL,
|
||||
STATUS_QUERY_VERSION,
|
||||
STATUS_UPDATE_NEWPLUGINS,
|
||||
STATUS_UPDATE_NEWVERSION,
|
||||
UPDATE_PLUGINS_RELEASE_SUMMARY,
|
||||
UPDATE_RELEASE_SUMMARY,
|
||||
)
|
||||
from .entity import LMSStatusEntity
|
||||
|
||||
newserver = UpdateEntityDescription(
|
||||
key=STATUS_UPDATE_NEWVERSION,
|
||||
)
|
||||
|
||||
newplugins = UpdateEntityDescription(
|
||||
key=STATUS_UPDATE_NEWPLUGINS,
|
||||
)
|
||||
|
||||
POLL_AFTER_INSTALL = 120
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: SqueezeboxConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Platform setup using common elements."""
|
||||
|
||||
async_add_entities(
|
||||
[
|
||||
ServerStatusUpdateLMS(entry.runtime_data.coordinator, newserver),
|
||||
ServerStatusUpdatePlugins(entry.runtime_data.coordinator, newplugins),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
class ServerStatusUpdate(LMSStatusEntity, UpdateEntity):
|
||||
"""LMS Status update sensors via cooridnatior."""
|
||||
|
||||
@property
|
||||
def latest_version(self) -> str:
|
||||
"""LMS Status directly from coordinator data."""
|
||||
return str(self.coordinator.data[self.entity_description.key])
|
||||
|
||||
|
||||
class ServerStatusUpdateLMS(ServerStatusUpdate):
|
||||
"""LMS Status update sensor from LMS via cooridnatior."""
|
||||
|
||||
title: str = SERVER_MODEL
|
||||
|
||||
@property
|
||||
def installed_version(self) -> str:
|
||||
"""LMS Status directly from coordinator data."""
|
||||
return str(self.coordinator.data[STATUS_QUERY_VERSION])
|
||||
|
||||
@property
|
||||
def release_url(self) -> str:
|
||||
"""LMS Update info page."""
|
||||
return str(self.coordinator.lms.generate_image_url("updateinfo.html"))
|
||||
|
||||
@property
|
||||
def release_summary(self) -> None | str:
|
||||
"""If install is supported give some info."""
|
||||
return (
|
||||
str(self.coordinator.data[UPDATE_RELEASE_SUMMARY])
|
||||
if self.coordinator.data[UPDATE_RELEASE_SUMMARY]
|
||||
else None
|
||||
)
|
||||
|
||||
|
||||
class ServerStatusUpdatePlugins(ServerStatusUpdate):
|
||||
"""LMS Plugings update sensor from LMS via cooridnatior."""
|
||||
|
||||
auto_update = True
|
||||
title: str = SERVER_MODEL + " Plugins"
|
||||
installed_version = "Current"
|
||||
restart_triggered = False
|
||||
_cancel_update: Callable | None = None
|
||||
|
||||
@property
|
||||
def supported_features(self) -> UpdateEntityFeature:
|
||||
"""Support install if we can."""
|
||||
return (
|
||||
(UpdateEntityFeature.INSTALL | UpdateEntityFeature.PROGRESS)
|
||||
if self.coordinator.can_server_restart
|
||||
else UpdateEntityFeature(0)
|
||||
)
|
||||
|
||||
@property
|
||||
def release_summary(self) -> None | str:
|
||||
"""If install is supported give some info."""
|
||||
rs = self.coordinator.data[UPDATE_PLUGINS_RELEASE_SUMMARY]
|
||||
return (
|
||||
(rs or "")
|
||||
+ "The Plugins will be updated on the next restart triggred by selecting the Install button. Allow enough time for the service to restart. It will become briefly unavailable."
|
||||
if self.coordinator.can_server_restart
|
||||
else rs
|
||||
)
|
||||
|
||||
@property
|
||||
def release_url(self) -> str:
|
||||
"""LMS Plugins info page."""
|
||||
return str(
|
||||
self.coordinator.lms.generate_image_url(
|
||||
"/settings/index.html?activePage=SETUP_PLUGINS"
|
||||
)
|
||||
)
|
||||
|
||||
@property
|
||||
def in_progress(self) -> bool:
|
||||
"""Are we restarting."""
|
||||
if self.latest_version == self.installed_version and self.restart_triggered:
|
||||
_LOGGER.debug("plugin progress reset %s", self.coordinator.lms.name)
|
||||
if callable(self._cancel_update):
|
||||
self._cancel_update()
|
||||
self.restart_triggered = False
|
||||
return self.restart_triggered
|
||||
|
||||
async def async_install(
|
||||
self, version: str | None, backup: bool, **kwargs: Any
|
||||
) -> None:
|
||||
"""Install all plugin updates."""
|
||||
_LOGGER.debug(
|
||||
"server restart for plugin install on %s", self.coordinator.lms.name
|
||||
)
|
||||
self.restart_triggered = True
|
||||
self.async_write_ha_state()
|
||||
|
||||
result = await self.coordinator.lms.async_query("restartserver")
|
||||
_LOGGER.debug("restart server result %s", result)
|
||||
if not result:
|
||||
self._cancel_update = async_call_later(
|
||||
self.hass, POLL_AFTER_INSTALL, self._async_update_catchall
|
||||
)
|
||||
else:
|
||||
self.restart_triggered = False
|
||||
self.async_write_ha_state()
|
||||
raise HomeAssistantError(
|
||||
"Error trying to update LMS Plugins: Restart failed"
|
||||
)
|
||||
|
||||
async def _async_update_catchall(self, now: datetime | None = None) -> None:
|
||||
"""Request update. clear restart catchall."""
|
||||
if self.restart_triggered:
|
||||
_LOGGER.debug("server restart catchall for %s", self.coordinator.lms.name)
|
||||
self.restart_triggered = False
|
||||
self.async_write_ha_state()
|
||||
await self.async_update()
|
@ -25,6 +25,8 @@ from homeassistant.components.squeezebox.const import (
|
||||
STATUS_SENSOR_OTHER_PLAYER_COUNT,
|
||||
STATUS_SENSOR_PLAYER_COUNT,
|
||||
STATUS_SENSOR_RESCAN,
|
||||
STATUS_UPDATE_NEWPLUGINS,
|
||||
STATUS_UPDATE_NEWVERSION,
|
||||
)
|
||||
from homeassistant.const import CONF_HOST, CONF_PORT, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
@ -69,6 +71,9 @@ FAKE_QUERY_RESPONSE = {
|
||||
STATUS_SENSOR_INFO_TOTAL_SONGS: 42,
|
||||
STATUS_SENSOR_PLAYER_COUNT: 10,
|
||||
STATUS_SENSOR_OTHER_PLAYER_COUNT: 0,
|
||||
STATUS_UPDATE_NEWVERSION: 'A new version of Logitech Media Server is available (8.5.2 - 0). <a href="updateinfo.html?installerFile=/var/lib/squeezeboxserver/cache/updates/logitechmediaserver_8.5.2_amd64.deb" target="update">Click here for further information</a>.',
|
||||
STATUS_UPDATE_NEWPLUGINS: "Plugins have been updated - Restart Required (Big Sounds)",
|
||||
"_can": 1,
|
||||
"players_loop": [
|
||||
{
|
||||
"isplaying": 0,
|
||||
@ -299,7 +304,9 @@ def mock_pysqueezebox_server(
|
||||
mock_lms.uuid = uuid
|
||||
mock_lms.name = TEST_SERVER_NAME
|
||||
mock_lms.async_query = AsyncMock(return_value={"uuid": format_mac(uuid)})
|
||||
mock_lms.async_status = AsyncMock(return_value={"uuid": format_mac(uuid)})
|
||||
mock_lms.async_status = AsyncMock(
|
||||
return_value={"uuid": format_mac(uuid), "version": FAKE_VERSION}
|
||||
)
|
||||
return mock_lms
|
||||
|
||||
|
||||
|
232
tests/components/squeezebox/test_update.py
Normal file
232
tests/components/squeezebox/test_update.py
Normal file
@ -0,0 +1,232 @@
|
||||
"""Test squeezebox update platform."""
|
||||
|
||||
import copy
|
||||
from datetime import timedelta
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.squeezebox.const import (
|
||||
SENSOR_UPDATE_INTERVAL,
|
||||
STATUS_UPDATE_NEWPLUGINS,
|
||||
)
|
||||
from homeassistant.components.update import (
|
||||
ATTR_IN_PROGRESS,
|
||||
DOMAIN as UPDATE_DOMAIN,
|
||||
SERVICE_INSTALL,
|
||||
)
|
||||
from homeassistant.const import ATTR_ENTITY_ID, STATE_OFF, STATE_ON, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from .conftest import FAKE_QUERY_RESPONSE
|
||||
|
||||
from tests.common import MockConfigEntry, async_fire_time_changed
|
||||
|
||||
|
||||
async def test_update_lms(
|
||||
hass: HomeAssistant,
|
||||
config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test binary sensor states and attributes."""
|
||||
|
||||
# Setup component
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.squeezebox.PLATFORMS",
|
||||
[Platform.UPDATE],
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.squeezebox.Server.async_query",
|
||||
return_value=copy.deepcopy(FAKE_QUERY_RESPONSE),
|
||||
),
|
||||
):
|
||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
state = hass.states.get("update.fakelib_lyrion_music_server")
|
||||
|
||||
assert state is not None
|
||||
assert state.state == STATE_ON
|
||||
|
||||
|
||||
async def test_update_plugins_install_fallback(
|
||||
hass: HomeAssistant,
|
||||
config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test binary sensor states and attributes."""
|
||||
|
||||
entity_id = "update.fakelib_updated_plugins"
|
||||
# Setup component
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.squeezebox.PLATFORMS",
|
||||
[Platform.UPDATE],
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.squeezebox.Server.async_query",
|
||||
return_value=copy.deepcopy(FAKE_QUERY_RESPONSE),
|
||||
),
|
||||
):
|
||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state is not None
|
||||
assert state.state == STATE_ON
|
||||
|
||||
polltime = 30
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.squeezebox.Server.async_query",
|
||||
return_value=False,
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.squeezebox.update.POLL_AFTER_INSTALL",
|
||||
polltime,
|
||||
),
|
||||
):
|
||||
await hass.services.async_call(
|
||||
UPDATE_DOMAIN,
|
||||
SERVICE_INSTALL,
|
||||
{
|
||||
ATTR_ENTITY_ID: entity_id,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
attrs = state.attributes
|
||||
assert attrs[ATTR_IN_PROGRESS]
|
||||
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.squeezebox.Server.async_status",
|
||||
return_value=copy.deepcopy(FAKE_QUERY_RESPONSE),
|
||||
),
|
||||
):
|
||||
async_fire_time_changed(
|
||||
hass,
|
||||
dt_util.utcnow() + timedelta(seconds=polltime + 1),
|
||||
)
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state is not None
|
||||
assert state.state == STATE_ON
|
||||
|
||||
attrs = state.attributes
|
||||
assert not attrs[ATTR_IN_PROGRESS]
|
||||
|
||||
|
||||
async def test_update_plugins_install_restart_fail(
|
||||
hass: HomeAssistant,
|
||||
config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test binary sensor states and attributes."""
|
||||
|
||||
entity_id = "update.fakelib_updated_plugins"
|
||||
# Setup component
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.squeezebox.PLATFORMS",
|
||||
[Platform.UPDATE],
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.squeezebox.Server.async_query",
|
||||
return_value=copy.deepcopy(FAKE_QUERY_RESPONSE),
|
||||
),
|
||||
):
|
||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.squeezebox.Server.async_query",
|
||||
return_value=True,
|
||||
),
|
||||
pytest.raises(HomeAssistantError),
|
||||
):
|
||||
await hass.services.async_call(
|
||||
UPDATE_DOMAIN,
|
||||
SERVICE_INSTALL,
|
||||
{
|
||||
ATTR_ENTITY_ID: entity_id,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
state = hass.states.get(entity_id)
|
||||
assert state is not None
|
||||
assert state.state == STATE_ON
|
||||
|
||||
attrs = state.attributes
|
||||
assert not attrs[ATTR_IN_PROGRESS]
|
||||
|
||||
|
||||
async def test_update_plugins_install_ok(
|
||||
hass: HomeAssistant,
|
||||
config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test binary sensor states and attributes."""
|
||||
|
||||
entity_id = "update.fakelib_updated_plugins"
|
||||
# Setup component
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.squeezebox.PLATFORMS",
|
||||
[Platform.UPDATE],
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.squeezebox.Server.async_query",
|
||||
return_value=copy.deepcopy(FAKE_QUERY_RESPONSE),
|
||||
),
|
||||
):
|
||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.squeezebox.Server.async_query",
|
||||
return_value=False,
|
||||
),
|
||||
):
|
||||
await hass.services.async_call(
|
||||
UPDATE_DOMAIN,
|
||||
SERVICE_INSTALL,
|
||||
{
|
||||
ATTR_ENTITY_ID: entity_id,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
state = hass.states.get(entity_id)
|
||||
assert state is not None
|
||||
assert state.state == STATE_ON
|
||||
|
||||
attrs = state.attributes
|
||||
assert attrs[ATTR_IN_PROGRESS]
|
||||
|
||||
resp = copy.deepcopy(FAKE_QUERY_RESPONSE)
|
||||
del resp[STATUS_UPDATE_NEWPLUGINS]
|
||||
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.squeezebox.Server.async_status",
|
||||
return_value=resp,
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.squeezebox.Server.async_query",
|
||||
return_value=copy.deepcopy(FAKE_QUERY_RESPONSE),
|
||||
),
|
||||
):
|
||||
async_fire_time_changed(
|
||||
hass,
|
||||
dt_util.utcnow() + timedelta(seconds=SENSOR_UPDATE_INTERVAL + 1),
|
||||
)
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state is not None
|
||||
assert state.state == STATE_OFF
|
||||
|
||||
attrs = state.attributes
|
||||
assert not attrs[ATTR_IN_PROGRESS]
|
Loading…
x
Reference in New Issue
Block a user