diff --git a/homeassistant/components/cast/media_player.py b/homeassistant/components/cast/media_player.py index 6d05fa81f3afb..5d6f89586bf38 100644 --- a/homeassistant/components/cast/media_player.py +++ b/homeassistant/components/cast/media_player.py @@ -816,13 +816,20 @@ def state(self) -> MediaPlayerState | None: return MediaPlayerState.PAUSED if media_status.player_is_idle: return MediaPlayerState.IDLE - if self.app_id is not None and self.app_id != pychromecast.IDLE_APP_ID: - if self.app_id in APP_IDS_UNRELIABLE_MEDIA_INFO: - # Some apps don't report media status, show the player as playing - return MediaPlayerState.PLAYING - return MediaPlayerState.IDLE + if self._chromecast is not None and self._chromecast.is_idle: + # If library consider us idle, that is our off state + # it takes HDMI status into account for cast devices. return MediaPlayerState.OFF + + if self.app_id in APP_IDS_UNRELIABLE_MEDIA_INFO: + # Some apps don't report media status, show the player as playing + return MediaPlayerState.PLAYING + + if self.app_id is not None: + # We have an active app + return MediaPlayerState.IDLE + return None @property diff --git a/homeassistant/components/nuki/__init__.py b/homeassistant/components/nuki/__init__.py index 5c02b6e972eeb..6e89fd074b9c7 100644 --- a/homeassistant/components/nuki/__init__.py +++ b/homeassistant/components/nuki/__init__.py @@ -110,6 +110,8 @@ async def handle_webhook( translation_placeholders={ "base_url": hass_url, "network_link": "https://my.home-assistant.io/redirect/network/", + "sample_ip": "192.168.1.10", + "sample_url": "http://192.168.1.10:8123", }, ) else: diff --git a/homeassistant/components/nuki/config_flow.py b/homeassistant/components/nuki/config_flow.py index ac6771bb1bd96..f170d56feda50 100644 --- a/homeassistant/components/nuki/config_flow.py +++ b/homeassistant/components/nuki/config_flow.py @@ -177,4 +177,5 @@ async def async_step_validate( step_id="user", data_schema=self.add_suggested_values_to_schema(data_schema, user_input), errors=errors, + description_placeholders={"sample_ip": "192.168.1.25"}, ) diff --git a/homeassistant/components/nuki/strings.json b/homeassistant/components/nuki/strings.json index 84e66c3db964f..ecd5872aadd66 100644 --- a/homeassistant/components/nuki/strings.json +++ b/homeassistant/components/nuki/strings.json @@ -9,7 +9,7 @@ "encrypt_token": "Use an encrypted token for authentication." }, "data_description": { - "host": "The hostname or IP address of your Nuki bridge. For example: 192.168.1.25." + "host": "The hostname or IP address of your Nuki bridge. For example: {sample_ip}." } }, "reauth_confirm": { @@ -34,7 +34,7 @@ "issues": { "https_webhook": { "title": "Nuki webhook URL uses HTTPS (SSL)", - "description": "The Nuki bridge can not push events to an HTTPS address (SSL), please configure a (local) HTTP address under \"Home Assistant URL\" in the [network settings]({network_link}). The current (local) address is: `{base_url}`, a valid address could, for example, be `http://192.168.1.10:8123` where `192.168.1.10` is the IP of the Home Assistant device" + "description": "The Nuki bridge cannot push events to an HTTPS address (SSL), please configure a (local) HTTP address under \"Home Assistant URL\" in the [network settings]({network_link}). The current (local) address is: `{base_url}`, a valid address could, for example, be `{sample_url}` where `{sample_ip}` is the IP of the Home Assistant device" } }, "entity": { diff --git a/homeassistant/components/plugwise/manifest.json b/homeassistant/components/plugwise/manifest.json index e26034b772e0e..fb992f3754167 100644 --- a/homeassistant/components/plugwise/manifest.json +++ b/homeassistant/components/plugwise/manifest.json @@ -8,6 +8,6 @@ "iot_class": "local_polling", "loggers": ["plugwise"], "quality_scale": "platinum", - "requirements": ["plugwise==1.8.1"], + "requirements": ["plugwise==1.8.2"], "zeroconf": ["_plugwise._tcp.local."] } diff --git a/homeassistant/components/portainer/binary_sensor.py b/homeassistant/components/portainer/binary_sensor.py index 032b46ef8b45c..96d9727a3bc7b 100644 --- a/homeassistant/components/portainer/binary_sensor.py +++ b/homeassistant/components/portainer/binary_sensor.py @@ -142,5 +142,5 @@ def available(self) -> bool: def is_on(self) -> bool | None: """Return true if the binary sensor is on.""" return self.entity_description.state_fn( - self.coordinator.data[self.endpoint_id].containers[self.device_id] + self.coordinator.data[self.endpoint_id].containers[self.device_name] ) diff --git a/homeassistant/components/portainer/button.py b/homeassistant/components/portainer/button.py index 917bc9f467655..5bd9ba0f7f745 100644 --- a/homeassistant/components/portainer/button.py +++ b/homeassistant/components/portainer/button.py @@ -95,12 +95,7 @@ def __init__( self.entity_description = entity_description super().__init__(device_info, coordinator, via_device) - device_identifier = ( - self._device_info.names[0].replace("/", " ").strip() - if self._device_info.names - else None - ) - self._attr_unique_id = f"{coordinator.config_entry.entry_id}_{device_identifier}_{entity_description.key}" + self._attr_unique_id = f"{coordinator.config_entry.entry_id}_{self.device_name}_{entity_description.key}" async def async_press(self) -> None: """Trigger the Portainer button press service.""" diff --git a/homeassistant/components/portainer/coordinator.py b/homeassistant/components/portainer/coordinator.py index 84a2f60cd581a..f77e5ff3a7698 100644 --- a/homeassistant/components/portainer/coordinator.py +++ b/homeassistant/components/portainer/coordinator.py @@ -144,7 +144,10 @@ async def _async_update_data(self) -> dict[int, PortainerCoordinatorData]: id=endpoint.id, name=endpoint.name, endpoint=endpoint, - containers={container.id: container for container in containers}, + containers={ + container.names[0].replace("/", " ").strip(): container + for container in containers + }, docker_version=docker_version, docker_info=docker_info, ) diff --git a/homeassistant/components/portainer/sensor.py b/homeassistant/components/portainer/sensor.py index f92e2ace538dc..9e4d66927f1ca 100644 --- a/homeassistant/components/portainer/sensor.py +++ b/homeassistant/components/portainer/sensor.py @@ -212,7 +212,7 @@ def available(self) -> bool: def native_value(self) -> StateType: """Return the state of the sensor.""" return self.entity_description.value_fn( - self.coordinator.data[self.endpoint_id].containers[self.device_id] + self.coordinator.data[self.endpoint_id].containers[self.device_name] ) diff --git a/homeassistant/components/portainer/switch.py b/homeassistant/components/portainer/switch.py index eed33e43c0cd0..5c795bed11250 100644 --- a/homeassistant/components/portainer/switch.py +++ b/homeassistant/components/portainer/switch.py @@ -123,7 +123,7 @@ def __init__( def is_on(self) -> bool | None: """Return the state of the device.""" return self.entity_description.is_on_fn( - self.coordinator.data[self.endpoint_id].containers[self.device_id] + self.coordinator.data[self.endpoint_id].containers[self.device_name] ) async def async_turn_on(self, **kwargs: Any) -> None: diff --git a/homeassistant/components/teslemetry/manifest.json b/homeassistant/components/teslemetry/manifest.json index b6aff150a96a1..3fc12f788a546 100644 --- a/homeassistant/components/teslemetry/manifest.json +++ b/homeassistant/components/teslemetry/manifest.json @@ -6,5 +6,5 @@ "documentation": "https://www.home-assistant.io/integrations/teslemetry", "iot_class": "cloud_polling", "loggers": ["tesla-fleet-api"], - "requirements": ["tesla-fleet-api==1.2.3", "teslemetry-stream==0.7.9"] + "requirements": ["tesla-fleet-api==1.2.3", "teslemetry-stream==0.7.10"] } diff --git a/homeassistant/components/vodafone_station/__init__.py b/homeassistant/components/vodafone_station/__init__.py index ded8b2ec3b825..f070464800ce9 100644 --- a/homeassistant/components/vodafone_station/__init__.py +++ b/homeassistant/components/vodafone_station/__init__.py @@ -1,7 +1,7 @@ """Vodafone Station integration.""" from aiohttp import ClientSession, CookieJar -from aiovodafone.api import VodafoneStationCommonApi +from aiovodafone.models import get_device_type from homeassistant.const import CONF_HOST, Platform from homeassistant.core import HomeAssistant @@ -42,7 +42,7 @@ async def async_migrate_entry(hass: HomeAssistant, entry: VodafoneConfigEntry) - session = ClientSession(cookie_jar=jar) try: - device_type, url = await VodafoneStationCommonApi.get_device_type( + device_type, url = await get_device_type( entry.data[CONF_HOST], session, ) @@ -54,7 +54,7 @@ async def async_migrate_entry(hass: HomeAssistant, entry: VodafoneConfigEntry) - new_data.update( { CONF_DEVICE_DETAILS: { - DEVICE_TYPE: device_type, + DEVICE_TYPE: device_type.value, DEVICE_URL: str(url), } }, diff --git a/homeassistant/components/vodafone_station/config_flow.py b/homeassistant/components/vodafone_station/config_flow.py index ab2335e766977..2c4db8c48abb3 100644 --- a/homeassistant/components/vodafone_station/config_flow.py +++ b/homeassistant/components/vodafone_station/config_flow.py @@ -6,7 +6,7 @@ from typing import Any from aiovodafone import exceptions as aiovodafone_exceptions -from aiovodafone.api import VodafoneStationCommonApi, init_api_class +from aiovodafone.models import get_device_type, init_device_class import voluptuous as vol from homeassistant.components.device_tracker import ( @@ -54,12 +54,12 @@ async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> dict[str, session = await async_client_session(hass) - device_type, url = await VodafoneStationCommonApi.get_device_type( + device_type, url = await get_device_type( data[CONF_HOST], session, ) - api = init_api_class(url, device_type, data, session) + api = init_device_class(url, device_type, data, session) try: await api.login() @@ -69,7 +69,7 @@ async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> dict[str, return { "title": data[CONF_HOST], CONF_DEVICE_DETAILS: { - DEVICE_TYPE: device_type, + DEVICE_TYPE: device_type.value, DEVICE_URL: str(url), }, } diff --git a/homeassistant/components/vodafone_station/coordinator.py b/homeassistant/components/vodafone_station/coordinator.py index 3648dee779595..e03f6ebde5416 100644 --- a/homeassistant/components/vodafone_station/coordinator.py +++ b/homeassistant/components/vodafone_station/coordinator.py @@ -7,7 +7,8 @@ from aiohttp import ClientSession from aiovodafone import exceptions -from aiovodafone.api import VodafoneStationDevice, init_api_class +from aiovodafone.api import VodafoneStationDevice +from aiovodafone.models import init_device_class from yarl import URL from homeassistant.components.device_tracker import ( @@ -70,7 +71,7 @@ def __init__( data = config_entry.data - self.api = init_api_class( + self.api = init_device_class( URL(data[CONF_DEVICE_DETAILS][DEVICE_URL]), data[CONF_DEVICE_DETAILS][DEVICE_TYPE], data, diff --git a/homeassistant/components/vodafone_station/manifest.json b/homeassistant/components/vodafone_station/manifest.json index 001bfa4d5b715..6c827945da74a 100644 --- a/homeassistant/components/vodafone_station/manifest.json +++ b/homeassistant/components/vodafone_station/manifest.json @@ -8,5 +8,5 @@ "iot_class": "local_polling", "loggers": ["aiovodafone"], "quality_scale": "platinum", - "requirements": ["aiovodafone==2.0.1"] + "requirements": ["aiovodafone==3.0.0"] } diff --git a/requirements_all.txt b/requirements_all.txt index 07c685d4186bf..bd89be0ef56b6 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -432,7 +432,7 @@ aiousbwatcher==1.1.1 aiovlc==0.5.1 # homeassistant.components.vodafone_station -aiovodafone==2.0.1 +aiovodafone==3.0.0 # homeassistant.components.waqi aiowaqi==3.1.0 @@ -1732,7 +1732,7 @@ plexauth==0.0.6 plexwebsocket==0.0.14 # homeassistant.components.plugwise -plugwise==1.8.1 +plugwise==1.8.2 # homeassistant.components.serial_pm pmsensor==0.4 @@ -2979,7 +2979,7 @@ tesla-powerwall==0.5.2 tesla-wall-connector==1.0.2 # homeassistant.components.teslemetry -teslemetry-stream==0.7.9 +teslemetry-stream==0.7.10 # homeassistant.components.tessie tessie-api==0.1.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 13a5532b4f396..5c481dcfc3bb0 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -414,7 +414,7 @@ aiousbwatcher==1.1.1 aiovlc==0.5.1 # homeassistant.components.vodafone_station -aiovodafone==2.0.1 +aiovodafone==3.0.0 # homeassistant.components.waqi aiowaqi==3.1.0 @@ -1473,7 +1473,7 @@ plexauth==0.0.6 plexwebsocket==0.0.14 # homeassistant.components.plugwise -plugwise==1.8.1 +plugwise==1.8.2 # homeassistant.components.poolsense poolsense==0.0.8 @@ -2468,7 +2468,7 @@ tesla-powerwall==0.5.2 tesla-wall-connector==1.0.2 # homeassistant.components.teslemetry -teslemetry-stream==0.7.9 +teslemetry-stream==0.7.10 # homeassistant.components.tessie tessie-api==0.1.1 diff --git a/tests/components/cast/test_media_player.py b/tests/components/cast/test_media_player.py index c56904f1c48e1..767a95dbe9a42 100644 --- a/tests/components/cast/test_media_player.py +++ b/tests/components/cast/test_media_player.py @@ -68,6 +68,7 @@ def get_fake_chromecast(info: ChromecastInfo): mock = MagicMock(uuid=info.uuid) mock.app_id = None mock.media_controller.status = None + mock.is_idle = True return mock @@ -887,6 +888,7 @@ async def test_entity_cast_status( assert not state.attributes.get("is_volume_muted") chromecast.app_id = "1234" + chromecast.is_idle = False cast_status = MagicMock() cast_status.volume_level = 0.5 cast_status.volume_muted = False @@ -1599,6 +1601,7 @@ async def test_entity_media_states( # App id updated, but no media status chromecast.app_id = app_id + chromecast.is_idle = False cast_status = MagicMock() cast_status_cb(cast_status) await hass.async_block_till_done() @@ -1641,6 +1644,7 @@ async def test_entity_media_states( # App no longer running chromecast.app_id = pychromecast.IDLE_APP_ID + chromecast.is_idle = True cast_status = MagicMock() cast_status_cb(cast_status) await hass.async_block_till_done() @@ -1648,6 +1652,7 @@ async def test_entity_media_states( assert state.state == "off" # No cast status + chromecast.app_id = None chromecast.is_idle = False cast_status_cb(None) await hass.async_block_till_done() @@ -1722,6 +1727,7 @@ async def test_entity_media_states_lovelace_app( state = hass.states.get(entity_id) assert state.state == "off" + chromecast.app_id = None chromecast.is_idle = False media_status_cb(media_status) await hass.async_block_till_done() diff --git a/tests/components/vodafone_station/conftest.py b/tests/components/vodafone_station/conftest.py index 4790ddbe0b8d1..a3ffdf9f14eb9 100644 --- a/tests/components/vodafone_station/conftest.py +++ b/tests/components/vodafone_station/conftest.py @@ -2,7 +2,7 @@ from datetime import UTC, datetime -from aiovodafone.api import VodafoneStationCommonApi, VodafoneStationDevice +from aiovodafone.api import VodafoneStationDevice import pytest from yarl import URL @@ -49,16 +49,19 @@ def mock_vodafone_station_router() -> Generator[AsyncMock]: """Mock a Vodafone Station router.""" with ( patch( - "homeassistant.components.vodafone_station.coordinator.init_api_class", + "homeassistant.components.vodafone_station.coordinator.init_device_class", autospec=True, ) as mock_router, patch( - "homeassistant.components.vodafone_station.config_flow.init_api_class", + "homeassistant.components.vodafone_station.config_flow.init_device_class", new=mock_router, ), - patch.object( - VodafoneStationCommonApi, - "get_device_type", + patch( + "homeassistant.components.vodafone_station.config_flow.get_device_type", + new=AsyncMock(return_value=(TEST_TYPE, URL(TEST_URL))), + ), + patch( + "homeassistant.components.vodafone_station.get_device_type", new=AsyncMock(return_value=(TEST_TYPE, URL(TEST_URL))), ), ): diff --git a/tests/components/vodafone_station/const.py b/tests/components/vodafone_station/const.py index 744430e06f8fc..071ef7b509487 100644 --- a/tests/components/vodafone_station/const.py +++ b/tests/components/vodafone_station/const.py @@ -1,5 +1,7 @@ """Common stuff for Vodafone Station tests.""" +from aiovodafone.models import DeviceType + DEVICE_1_HOST = "WifiDevice0" DEVICE_1_MAC = "xx:xx:xx:xx:xx:xx" DEVICE_2_HOST = "LanDevice1" @@ -7,6 +9,6 @@ TEST_HOST = "fake_host" TEST_PASSWORD = "fake_password" -TEST_TYPE = "Sercomm" +TEST_TYPE = DeviceType.SERCOMM TEST_URL = f"https://{TEST_HOST}" TEST_USERNAME = "fake_username" diff --git a/tests/components/vodafone_station/test_config_flow.py b/tests/components/vodafone_station/test_config_flow.py index 9d9ed2fda85e6..748201db089b1 100644 --- a/tests/components/vodafone_station/test_config_flow.py +++ b/tests/components/vodafone_station/test_config_flow.py @@ -2,7 +2,7 @@ from unittest.mock import AsyncMock -from aiovodafone import ( +from aiovodafone.exceptions import ( AlreadyLogged, CannotAuthenticate, CannotConnect, diff --git a/tests/components/vodafone_station/test_coordinator.py b/tests/components/vodafone_station/test_coordinator.py index 5f75b53880356..cd78c4d5bd6df 100644 --- a/tests/components/vodafone_station/test_coordinator.py +++ b/tests/components/vodafone_station/test_coordinator.py @@ -3,7 +3,7 @@ import logging from unittest.mock import AsyncMock -from aiovodafone import VodafoneStationDevice +from aiovodafone.api import VodafoneStationDevice from freezegun.api import FrozenDateTimeFactory import pytest diff --git a/tests/components/vodafone_station/test_sensor.py b/tests/components/vodafone_station/test_sensor.py index 35c486a359fd2..b005f8eba44ac 100644 --- a/tests/components/vodafone_station/test_sensor.py +++ b/tests/components/vodafone_station/test_sensor.py @@ -2,8 +2,7 @@ from unittest.mock import AsyncMock, patch -from aiovodafone import CannotAuthenticate -from aiovodafone.exceptions import AlreadyLogged, CannotConnect +from aiovodafone.exceptions import AlreadyLogged, CannotAuthenticate, CannotConnect from freezegun.api import FrozenDateTimeFactory import pytest from syrupy.assertion import SnapshotAssertion