diff --git a/.coveragerc b/.coveragerc
index 2f899999f4130e..533fd8de18dbad 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -898,6 +898,7 @@ omit =
homeassistant/components/opengarage/cover.py
homeassistant/components/opengarage/entity.py
homeassistant/components/opengarage/sensor.py
+ homeassistant/components/openhardwaremonitor/sensor.py
homeassistant/components/openhome/__init__.py
homeassistant/components/openhome/const.py
homeassistant/components/openhome/media_player.py
diff --git a/homeassistant/components/demo/climate.py b/homeassistant/components/demo/climate.py
index 6639c1256538d1..b0a53909a46a0b 100644
--- a/homeassistant/components/demo/climate.py
+++ b/homeassistant/components/demo/climate.py
@@ -4,6 +4,7 @@
from typing import Any
from homeassistant.components.climate import (
+ ATTR_HVAC_MODE,
ATTR_TARGET_TEMP_HIGH,
ATTR_TARGET_TEMP_LOW,
ClimateEntity,
@@ -258,6 +259,8 @@ async def async_set_temperature(self, **kwargs: Any) -> None:
):
self._target_temperature_high = kwargs.get(ATTR_TARGET_TEMP_HIGH)
self._target_temperature_low = kwargs.get(ATTR_TARGET_TEMP_LOW)
+ if kwargs.get(ATTR_HVAC_MODE) is not None:
+ self._hvac_mode = HVACMode(str(kwargs.get(ATTR_HVAC_MODE)))
self.async_write_ha_state()
async def async_set_humidity(self, humidity: int) -> None:
diff --git a/homeassistant/components/openhardwaremonitor/sensor.py b/homeassistant/components/openhardwaremonitor/sensor.py
index 70dbbd38fc881a..4206bc72c1da54 100644
--- a/homeassistant/components/openhardwaremonitor/sensor.py
+++ b/homeassistant/components/openhardwaremonitor/sensor.py
@@ -79,6 +79,8 @@ def native_unit_of_measurement(self):
@property
def native_value(self):
"""Return the state of the device."""
+ if self.value == "-":
+ return None
return self.value
@property
diff --git a/homeassistant/components/plex/manifest.json b/homeassistant/components/plex/manifest.json
index bc0c54c49bf195..6cf94793173190 100644
--- a/homeassistant/components/plex/manifest.json
+++ b/homeassistant/components/plex/manifest.json
@@ -8,7 +8,7 @@
"iot_class": "local_push",
"loggers": ["plexapi", "plexwebsocket"],
"requirements": [
- "PlexAPI==4.13.2",
+ "PlexAPI==4.15.3",
"plexauth==0.0.6",
"plexwebsocket==0.0.13"
],
diff --git a/homeassistant/components/zwave_js/update.py b/homeassistant/components/zwave_js/update.py
index 3dedd8bf370993..6efae29e46e9b8 100644
--- a/homeassistant/components/zwave_js/update.py
+++ b/homeassistant/components/zwave_js/update.py
@@ -211,11 +211,15 @@ async def _async_update(self, _: HomeAssistant | datetime | None = None) -> None
return
try:
- available_firmware_updates = (
- await self.driver.controller.async_get_available_firmware_updates(
- self.node, API_KEY_FIRMWARE_UPDATE_SERVICE
+ # Retrieve all firmware updates including non-stable ones but filter
+ # non-stable channels out
+ available_firmware_updates = [
+ update
+ for update in await self.driver.controller.async_get_available_firmware_updates(
+ self.node, API_KEY_FIRMWARE_UPDATE_SERVICE, True
)
- )
+ if update.channel == "stable"
+ ]
except FailedZWaveCommand as err:
LOGGER.debug(
"Failed to get firmware updates for node %s: %s",
diff --git a/requirements_all.txt b/requirements_all.txt
index c3c28dff73eb91..df05ddc12a69fb 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -46,7 +46,7 @@ Mastodon.py==1.5.1
Pillow==10.0.0
# homeassistant.components.plex
-PlexAPI==4.13.2
+PlexAPI==4.15.3
# homeassistant.components.progettihwsw
ProgettiHWSW==0.1.3
diff --git a/requirements_test_all.txt b/requirements_test_all.txt
index cba22b8a54dc68..fddeddd33c0916 100644
--- a/requirements_test_all.txt
+++ b/requirements_test_all.txt
@@ -42,7 +42,7 @@ HATasmota==0.7.3
Pillow==10.0.0
# homeassistant.components.plex
-PlexAPI==4.13.2
+PlexAPI==4.15.3
# homeassistant.components.progettihwsw
ProgettiHWSW==0.1.3
diff --git a/tests/components/demo/test_climate.py b/tests/components/demo/test_climate.py
index fa87c439a4d4f0..69e385ce242ad5 100644
--- a/tests/components/demo/test_climate.py
+++ b/tests/components/demo/test_climate.py
@@ -198,6 +198,28 @@ async def test_set_target_temp_range_bad_attr(hass: HomeAssistant) -> None:
assert state.attributes.get(ATTR_TARGET_TEMP_HIGH) == 24.0
+async def test_set_temp_with_hvac_mode(hass: HomeAssistant) -> None:
+ """Test the setting of the hvac_mode in set_temperature."""
+ state = hass.states.get(ENTITY_CLIMATE)
+ assert state.attributes.get(ATTR_TEMPERATURE) == 21
+ assert state.state == HVACMode.COOL
+
+ await hass.services.async_call(
+ DOMAIN,
+ SERVICE_SET_TEMPERATURE,
+ {
+ ATTR_ENTITY_ID: ENTITY_CLIMATE,
+ ATTR_TEMPERATURE: 23,
+ ATTR_HVAC_MODE: HVACMode.OFF,
+ },
+ blocking=True,
+ )
+
+ state = hass.states.get(ENTITY_CLIMATE)
+ assert state.state == HVACMode.OFF
+ assert state.attributes.get(ATTR_TEMPERATURE) == 23
+
+
async def test_set_target_humidity_bad_attr(hass: HomeAssistant) -> None:
"""Test setting the target humidity without required attribute."""
state = hass.states.get(ENTITY_CLIMATE)
diff --git a/tests/components/plex/conftest.py b/tests/components/plex/conftest.py
index e4bf61ccd9492c..78a3b7387ea3d9 100644
--- a/tests/components/plex/conftest.py
+++ b/tests/components/plex/conftest.py
@@ -232,6 +232,12 @@ def player_plexweb_resources_fixture():
return load_fixture("plex/player_plexweb_resources.xml")
+@pytest.fixture(name="player_plexhtpc_resources", scope="session")
+def player_plexhtpc_resources_fixture():
+ """Load resources payload for a Plex HTPC player and return it."""
+ return load_fixture("plex/player_plexhtpc_resources.xml")
+
+
@pytest.fixture(name="playlists", scope="session")
def playlists_fixture():
"""Load payload for all playlists and return it."""
@@ -450,8 +456,8 @@ def mock_plex_calls(
"""Mock Plex API calls."""
requests_mock.get("https://plex.tv/api/users/", text=plextv_shared_users)
requests_mock.get("https://plex.tv/api/invites/requested", text=empty_payload)
- requests_mock.get("https://plex.tv/users/account", text=plextv_account)
- requests_mock.get("https://plex.tv/api/resources", text=plextv_resources)
+ requests_mock.get("https://plex.tv/api/v2/user", text=plextv_account)
+ requests_mock.get("https://plex.tv/api/v2/resources", text=plextv_resources)
url = plex_server_url(entry)
diff --git a/tests/components/plex/fixtures/player_plexhtpc_resources.xml b/tests/components/plex/fixtures/player_plexhtpc_resources.xml
new file mode 100644
index 00000000000000..6cc9cc0afbdb4c
--- /dev/null
+++ b/tests/components/plex/fixtures/player_plexhtpc_resources.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/tests/components/plex/fixtures/plextv_account.xml b/tests/components/plex/fixtures/plextv_account.xml
index 32d6eec7c2d7f6..b47896de57749e 100644
--- a/tests/components/plex/fixtures/plextv_account.xml
+++ b/tests/components/plex/fixtures/plextv_account.xml
@@ -1,15 +1,18 @@
-
-
-
+
+
+
+
+
+
+
+
+
-
-
-
- testuser
- testuser@email.com
- 2000-01-01 12:34:56 UTC
- faketoken
+
+
+
+
diff --git a/tests/components/plex/fixtures/plextv_resources_one_server.xml b/tests/components/plex/fixtures/plextv_resources_one_server.xml
index ff2e458ff24edc..75b7e54b7e685d 100644
--- a/tests/components/plex/fixtures/plextv_resources_one_server.xml
+++ b/tests/components/plex/fixtures/plextv_resources_one_server.xml
@@ -1,18 +1,22 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/components/plex/fixtures/plextv_resources_two_servers.xml b/tests/components/plex/fixtures/plextv_resources_two_servers.xml
index 7da5df4c1df4d3..f14b55fe161f24 100644
--- a/tests/components/plex/fixtures/plextv_resources_two_servers.xml
+++ b/tests/components/plex/fixtures/plextv_resources_two_servers.xml
@@ -1,21 +1,27 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/components/plex/test_config_flow.py b/tests/components/plex/test_config_flow.py
index beb454e2e9c47d..235596715f4aac 100644
--- a/tests/components/plex/test_config_flow.py
+++ b/tests/components/plex/test_config_flow.py
@@ -143,7 +143,7 @@ async def test_no_servers_found(
current_request_with_host: None,
) -> None:
"""Test when no servers are on an account."""
- requests_mock.get("https://plex.tv/api/resources", text=empty_payload)
+ requests_mock.get("https://plex.tv/api/v2/resources", text=empty_payload)
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
@@ -225,7 +225,7 @@ async def test_multiple_servers_with_selection(
assert result["step_id"] == "user"
requests_mock.get(
- "https://plex.tv/api/resources",
+ "https://plex.tv/api/v2/resources",
text=plextv_resources_two_servers,
)
with patch("plexauth.PlexAuth.initiate_auth"), patch(
@@ -289,7 +289,7 @@ async def test_adding_last_unconfigured_server(
assert result["step_id"] == "user"
requests_mock.get(
- "https://plex.tv/api/resources",
+ "https://plex.tv/api/v2/resources",
text=plextv_resources_two_servers,
)
@@ -346,9 +346,9 @@ async def test_all_available_servers_configured(
assert result["type"] == FlowResultType.FORM
assert result["step_id"] == "user"
- requests_mock.get("https://plex.tv/users/account", text=plextv_account)
+ requests_mock.get("https://plex.tv/api/v2/user", text=plextv_account)
requests_mock.get(
- "https://plex.tv/api/resources",
+ "https://plex.tv/api/v2/resources",
text=plextv_resources_two_servers,
)
@@ -776,7 +776,7 @@ async def test_reauth_multiple_servers_available(
) -> None:
"""Test setup and reauthorization of a Plex token when multiple servers are available."""
requests_mock.get(
- "https://plex.tv/api/resources",
+ "https://plex.tv/api/v2/resources",
text=plextv_resources_two_servers,
)
diff --git a/tests/components/plex/test_init.py b/tests/components/plex/test_init.py
index bc43a1e0d89b6a..6e1043b5c522d6 100644
--- a/tests/components/plex/test_init.py
+++ b/tests/components/plex/test_init.py
@@ -231,7 +231,7 @@ def __init__(self):
# Test with account failure
requests_mock.get(
- "https://plex.tv/users/account", status_code=HTTPStatus.UNAUTHORIZED
+ "https://plex.tv/api/v2/user", status_code=HTTPStatus.UNAUTHORIZED
)
old_entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(old_entry.entry_id) is False
@@ -241,8 +241,8 @@ def __init__(self):
await hass.config_entries.async_unload(old_entry.entry_id)
# Test with no servers found
- requests_mock.get("https://plex.tv/users/account", text=plextv_account)
- requests_mock.get("https://plex.tv/api/resources", text=empty_payload)
+ requests_mock.get("https://plex.tv/api/v2/user", text=plextv_account)
+ requests_mock.get("https://plex.tv/api/v2/resources", text=empty_payload)
assert await hass.config_entries.async_setup(old_entry.entry_id) is False
await hass.async_block_till_done()
@@ -252,7 +252,7 @@ def __init__(self):
# Test with success
new_url = PLEX_DIRECT_URL
- requests_mock.get("https://plex.tv/api/resources", text=plextv_resources)
+ requests_mock.get("https://plex.tv/api/v2/resources", text=plextv_resources)
for resource_url in [new_url, "http://1.2.3.4:32400"]:
requests_mock.get(resource_url, text=plex_server_default)
requests_mock.get(f"{new_url}/accounts", text=plex_server_accounts)
@@ -287,7 +287,7 @@ async def test_bad_token_with_tokenless_server(
) -> None:
"""Test setup with a bad token and a server with token auth disabled."""
requests_mock.get(
- "https://plex.tv/users/account", status_code=HTTPStatus.UNAUTHORIZED
+ "https://plex.tv/api/v2/user", status_code=HTTPStatus.UNAUTHORIZED
)
await setup_plex_server()
diff --git a/tests/components/plex/test_media_players.py b/tests/components/plex/test_media_players.py
index 27fea36e3b0778..e9efc945f7142e 100644
--- a/tests/components/plex/test_media_players.py
+++ b/tests/components/plex/test_media_players.py
@@ -12,10 +12,10 @@ async def test_plex_tv_clients(
entry,
setup_plex_server,
requests_mock: requests_mock.Mocker,
- player_plexweb_resources,
+ player_plexhtpc_resources,
) -> None:
"""Test getting Plex clients from plex.tv."""
- requests_mock.get("/resources", text=player_plexweb_resources)
+ requests_mock.get("/resources", text=player_plexhtpc_resources)
with patch("plexapi.myplex.MyPlexResource.connect", side_effect=NotFound):
await setup_plex_server()
diff --git a/tests/components/plex/test_media_search.py b/tests/components/plex/test_media_search.py
index 0cc94134f1c9d1..21b50724786574 100644
--- a/tests/components/plex/test_media_search.py
+++ b/tests/components/plex/test_media_search.py
@@ -70,7 +70,10 @@ async def test_media_lookups(
)
assert "Library 'Not a Library' not found in" in str(excinfo.value)
- with patch("plexapi.library.LibrarySection.search") as search:
+ with patch(
+ "plexapi.library.LibrarySection.search",
+ __qualname__="search",
+ ) as search:
await hass.services.async_call(
MEDIA_PLAYER_DOMAIN,
SERVICE_PLAY_MEDIA,
@@ -261,7 +264,11 @@ async def test_media_lookups(
with pytest.raises(MediaNotFound) as excinfo:
payload = '{"library_name": "Movies", "title": "Not a Movie"}'
- with patch("plexapi.library.LibrarySection.search", side_effect=BadRequest):
+ with patch(
+ "plexapi.library.LibrarySection.search",
+ side_effect=BadRequest,
+ __qualname__="search",
+ ):
await hass.services.async_call(
MEDIA_PLAYER_DOMAIN,
SERVICE_PLAY_MEDIA,
diff --git a/tests/components/plex/test_playback.py b/tests/components/plex/test_playback.py
index c9dba4e4aca6d2..9ea684256c4327 100644
--- a/tests/components/plex/test_playback.py
+++ b/tests/components/plex/test_playback.py
@@ -49,14 +49,14 @@ async def test_media_player_playback(
setup_plex_server,
requests_mock: requests_mock.Mocker,
playqueue_created,
- player_plexweb_resources,
+ player_plexhtpc_resources,
) -> None:
"""Test playing media on a Plex media_player."""
- requests_mock.get("http://1.2.3.5:32400/resources", text=player_plexweb_resources)
+ requests_mock.get("http://1.2.3.6:32400/resources", text=player_plexhtpc_resources)
await setup_plex_server()
- media_player = "media_player.plex_plex_web_chrome"
+ media_player = "media_player.plex_plex_htpc_for_mac_plex_htpc"
requests_mock.post("/playqueues", text=playqueue_created)
playmedia_mock = requests_mock.get(
"/player/playback/playMedia", status_code=HTTPStatus.OK
@@ -65,7 +65,9 @@ async def test_media_player_playback(
# Test media lookup failure
payload = '{"library_name": "Movies", "title": "Movie 1" }'
with patch(
- "plexapi.library.LibrarySection.search", return_value=None
+ "plexapi.library.LibrarySection.search",
+ return_value=None,
+ __qualname__="search",
), pytest.raises(HomeAssistantError) as excinfo:
await hass.services.async_call(
MP_DOMAIN,
@@ -86,7 +88,11 @@ async def test_media_player_playback(
# Test movie success
movies = [movie1]
- with patch("plexapi.library.LibrarySection.search", return_value=movies):
+ with patch(
+ "plexapi.library.LibrarySection.search",
+ return_value=movies,
+ __qualname__="search",
+ ):
await hass.services.async_call(
MP_DOMAIN,
SERVICE_PLAY_MEDIA,
@@ -101,7 +107,11 @@ async def test_media_player_playback(
# Test movie success with resume
playmedia_mock.reset()
- with patch("plexapi.library.LibrarySection.search", return_value=movies):
+ with patch(
+ "plexapi.library.LibrarySection.search",
+ return_value=movies,
+ __qualname__="search",
+ ):
await hass.services.async_call(
MP_DOMAIN,
SERVICE_PLAY_MEDIA,
@@ -163,7 +173,11 @@ async def test_media_player_playback(
# Test multiple choices with exact match
playmedia_mock.reset()
movies = [movie1, movie2]
- with patch("plexapi.library.LibrarySection.search", return_value=movies):
+ with patch(
+ "plexapi.library.LibrarySection.search",
+ return_value=movies,
+ __qualname__="search",
+ ):
await hass.services.async_call(
MP_DOMAIN,
SERVICE_PLAY_MEDIA,
@@ -181,7 +195,11 @@ async def test_media_player_playback(
movies = [movie2, movie3]
with pytest.raises(HomeAssistantError) as excinfo:
payload = '{"library_name": "Movies", "title": "Movie" }'
- with patch("plexapi.library.LibrarySection.search", return_value=movies):
+ with patch(
+ "plexapi.library.LibrarySection.search",
+ return_value=movies,
+ __qualname__="search",
+ ):
await hass.services.async_call(
MP_DOMAIN,
SERVICE_PLAY_MEDIA,
@@ -197,7 +215,11 @@ async def test_media_player_playback(
# Test multiple choices with allow_multiple
movies = [movie1, movie2, movie3]
- with patch("plexapi.library.LibrarySection.search", return_value=movies), patch(
+ with patch(
+ "plexapi.library.LibrarySection.search",
+ return_value=movies,
+ __qualname__="search",
+ ), patch(
"homeassistant.components.plex.server.PlexServer.create_playqueue"
) as mock_create_playqueue:
await hass.services.async_call(
diff --git a/tests/components/plex/test_sensor.py b/tests/components/plex/test_sensor.py
index 9c73bf9f915a5d..5b9729792f40e5 100644
--- a/tests/components/plex/test_sensor.py
+++ b/tests/components/plex/test_sensor.py
@@ -129,7 +129,11 @@ async def test_library_sensor_values(
)
media = [MockPlexTVEpisode()]
- with patch("plexapi.library.LibrarySection.recentlyAdded", return_value=media):
+ with patch(
+ "plexapi.library.LibrarySection.recentlyAdded",
+ return_value=media,
+ __qualname__="recentlyAdded",
+ ):
await hass.async_block_till_done()
library_tv_sensor = hass.states.get("sensor.plex_server_1_library_tv_shows")
@@ -165,7 +169,11 @@ async def test_library_sensor_values(
trigger_plex_update(
mock_websocket, msgtype="status", payload=LIBRARY_UPDATE_PAYLOAD
)
- with patch("plexapi.library.LibrarySection.recentlyAdded", return_value=media):
+ with patch(
+ "plexapi.library.LibrarySection.recentlyAdded",
+ return_value=media,
+ __qualname__="recentlyAdded",
+ ):
await hass.async_block_till_done()
library_tv_sensor = hass.states.get("sensor.plex_server_1_library_tv_shows")
@@ -200,7 +208,11 @@ async def test_library_sensor_values(
)
media = [MockPlexMovie()]
- with patch("plexapi.library.LibrarySection.recentlyAdded", return_value=media):
+ with patch(
+ "plexapi.library.LibrarySection.recentlyAdded",
+ return_value=media,
+ __qualname__="recentlyAdded",
+ ):
await hass.async_block_till_done()
library_movies_sensor = hass.states.get("sensor.plex_server_1_library_movies")
@@ -210,7 +222,11 @@ async def test_library_sensor_values(
# Test with clip
media = [MockPlexClip()]
- with patch("plexapi.library.LibrarySection.recentlyAdded", return_value=media):
+ with patch(
+ "plexapi.library.LibrarySection.recentlyAdded",
+ return_value=media,
+ __qualname__="recentlyAdded",
+ ):
async_dispatcher_send(
hass, PLEX_UPDATE_LIBRARY_SIGNAL.format(mock_plex_server.machine_identifier)
)
@@ -236,7 +252,11 @@ async def test_library_sensor_values(
)
media = [MockPlexMusic()]
- with patch("plexapi.library.LibrarySection.recentlyAdded", return_value=media):
+ with patch(
+ "plexapi.library.LibrarySection.recentlyAdded",
+ return_value=media,
+ __qualname__="recentlyAdded",
+ ):
await hass.async_block_till_done()
library_music_sensor = hass.states.get("sensor.plex_server_1_library_music")
diff --git a/tests/components/plex/test_services.py b/tests/components/plex/test_services.py
index a74b3e91460ead..dfd02bb1d3fcfe 100644
--- a/tests/components/plex/test_services.py
+++ b/tests/components/plex/test_services.py
@@ -190,7 +190,11 @@ async def test_lookup_media_for_other_integrations(
assert result.shuffle
# Test with media not found
- with patch("plexapi.library.LibrarySection.search", return_value=None):
+ with patch(
+ "plexapi.library.LibrarySection.search",
+ return_value=None,
+ __qualname__="search",
+ ):
with pytest.raises(HomeAssistantError) as excinfo:
process_plex_payload(hass, MediaType.MUSIC, CONTENT_ID_BAD_MEDIA)
assert f"No {MediaType.MUSIC} results in 'Music' for" in str(excinfo.value)
diff --git a/tests/components/zwave_js/test_update.py b/tests/components/zwave_js/test_update.py
index 4c3aa9f54996b9..46dca7a35eca20 100644
--- a/tests/components/zwave_js/test_update.py
+++ b/tests/components/zwave_js/test_update.py
@@ -87,6 +87,24 @@
"rfRegion": 1,
},
},
+ # This firmware update should never show because it's in the beta channel
+ {
+ "version": "999.999.999",
+ "changelog": "blah 3",
+ "channel": "beta",
+ "files": [
+ {"target": 0, "url": "https://example3.com", "integrity": "sha3"}
+ ],
+ "downgrade": True,
+ "normalizedVersion": "999.999.999",
+ "device": {
+ "manufacturerId": 1,
+ "productType": 2,
+ "productId": 3,
+ "firmwareVersion": "0.4.4",
+ "rfRegion": 1,
+ },
+ },
]
}