mirror of
https://github.com/home-assistant/core.git
synced 2025-07-28 15:47:12 +00:00
Merge pull request #51768 from home-assistant/rc
This commit is contained in:
commit
2535f5c155
@ -169,7 +169,9 @@ class ONVIFDevice:
|
||||
cdate = device_time.UTCDateTime
|
||||
else:
|
||||
tzone = (
|
||||
dt_util.get_time_zone(device_time.TimeZone)
|
||||
dt_util.get_time_zone(
|
||||
device_time.TimeZone or str(dt_util.DEFAULT_TIME_ZONE)
|
||||
)
|
||||
or dt_util.DEFAULT_TIME_ZONE
|
||||
)
|
||||
cdate = device_time.LocalDateTime
|
||||
|
@ -64,7 +64,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
name="sensor",
|
||||
update_method=async_update_data,
|
||||
# Polling interval. Will only be polled if there are subscribers.
|
||||
update_interval=timedelta(seconds=300),
|
||||
update_interval=timedelta(seconds=3600),
|
||||
)
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
|
@ -78,7 +78,7 @@ class PlexSession:
|
||||
|
||||
if media.librarySectionID in SPECIAL_SECTIONS:
|
||||
self.media_library_title = SPECIAL_SECTIONS[media.librarySectionID]
|
||||
elif media.librarySectionID < 1:
|
||||
elif media.librarySectionID and media.librarySectionID < 1:
|
||||
self.media_library_title = UNKNOWN_SECTION
|
||||
_LOGGER.warning(
|
||||
"Unknown library section ID (%s) for title '%s', please create an issue",
|
||||
|
@ -291,13 +291,14 @@ class SamsungTVConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
bridge = SamsungTVBridge.get_bridge(
|
||||
self._reauth_entry.data[CONF_METHOD], self._reauth_entry.data[CONF_HOST]
|
||||
)
|
||||
result = bridge.try_connect()
|
||||
result = await self.hass.async_add_executor_job(bridge.try_connect)
|
||||
if result == RESULT_SUCCESS:
|
||||
new_data = dict(self._reauth_entry.data)
|
||||
new_data[CONF_TOKEN] = bridge.token
|
||||
self.hass.config_entries.async_update_entry(
|
||||
self._reauth_entry, data=new_data
|
||||
)
|
||||
await self.hass.config_entries.async_reload(self._reauth_entry.entry_id)
|
||||
return self.async_abort(reason="reauth_successful")
|
||||
if result not in (RESULT_AUTH_MISSING, RESULT_CANNOT_CONNECT):
|
||||
return self.async_abort(reason=result)
|
||||
|
@ -21,6 +21,7 @@ from homeassistant.components.media_player.const import (
|
||||
)
|
||||
from homeassistant.config_entries import SOURCE_REAUTH
|
||||
from homeassistant.const import CONF_HOST, CONF_MAC, CONF_NAME, STATE_OFF, STATE_ON
|
||||
from homeassistant.helpers import entity_component
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
|
||||
from homeassistant.helpers.script import Script
|
||||
@ -50,6 +51,13 @@ SUPPORT_SAMSUNGTV = (
|
||||
| SUPPORT_PLAY_MEDIA
|
||||
)
|
||||
|
||||
# Since the TV will take a few seconds to go to sleep
|
||||
# and actually be seen as off, we need to wait just a bit
|
||||
# more than the next scan interval
|
||||
SCAN_INTERVAL_PLUS_OFF_TIME = entity_component.DEFAULT_SCAN_INTERVAL + timedelta(
|
||||
seconds=5
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(hass, entry, async_add_entities):
|
||||
"""Set up the Samsung TV from a config entry."""
|
||||
@ -148,7 +156,12 @@ class SamsungTVDevice(MediaPlayerEntity):
|
||||
"""Return the availability of the device."""
|
||||
if self._auth_failed:
|
||||
return False
|
||||
return self._state == STATE_ON or self._on_script or self._mac
|
||||
return (
|
||||
self._state == STATE_ON
|
||||
or self._on_script
|
||||
or self._mac
|
||||
or self._power_off_in_progress()
|
||||
)
|
||||
|
||||
@property
|
||||
def device_info(self):
|
||||
@ -187,7 +200,7 @@ class SamsungTVDevice(MediaPlayerEntity):
|
||||
|
||||
def turn_off(self):
|
||||
"""Turn off media player."""
|
||||
self._end_of_power_off = dt_util.utcnow() + timedelta(seconds=15)
|
||||
self._end_of_power_off = dt_util.utcnow() + SCAN_INTERVAL_PLUS_OFF_TIME
|
||||
|
||||
self.send_key("KEY_POWEROFF")
|
||||
# Force closing of remote session to provide instant UI feedback
|
||||
|
@ -68,6 +68,18 @@ async def async_setup(hass: HomeAssistant, config: dict):
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
|
||||
"""Set up Shelly from a config entry."""
|
||||
# The custom component for Shelly devices uses shelly domain as well as core
|
||||
# integration. If the user removes the custom component but doesn't remove the
|
||||
# config entry, core integration will try to configure that config entry with an
|
||||
# error. The config entry data for this custom component doesn't contain host
|
||||
# value, so if host isn't present, config entry will not be configured.
|
||||
if not entry.data.get(CONF_HOST):
|
||||
_LOGGER.warning(
|
||||
"The config entry %s probably comes from a custom integration, please remove it if you want to use core Shelly integration",
|
||||
entry.title,
|
||||
)
|
||||
return False
|
||||
|
||||
hass.data[DOMAIN][DATA_CONFIG_ENTRY][entry.entry_id] = {}
|
||||
hass.data[DOMAIN][DATA_CONFIG_ENTRY][entry.entry_id][DEVICE] = None
|
||||
|
||||
|
@ -173,7 +173,7 @@ class SonosSpeaker:
|
||||
self.zone_name = speaker_info["zone_name"]
|
||||
|
||||
# Battery
|
||||
self.battery_info: dict[str, Any] | None = None
|
||||
self.battery_info: dict[str, Any] = {}
|
||||
self._last_battery_event: datetime.datetime | None = None
|
||||
self._battery_poll_timer: Callable | None = None
|
||||
|
||||
@ -208,21 +208,15 @@ class SonosSpeaker:
|
||||
self.hass, f"{SONOS_SEEN}-{self.soco.uid}", self.async_seen
|
||||
)
|
||||
|
||||
if (battery_info := fetch_battery_info_or_none(self.soco)) is None:
|
||||
self._platforms_ready.update({BINARY_SENSOR_DOMAIN, SENSOR_DOMAIN})
|
||||
else:
|
||||
if battery_info := fetch_battery_info_or_none(self.soco):
|
||||
self.battery_info = battery_info
|
||||
# Only create a polling task if successful, may fail on S1 firmware
|
||||
if battery_info:
|
||||
# Battery events can be infrequent, polling is still necessary
|
||||
self._battery_poll_timer = self.hass.helpers.event.track_time_interval(
|
||||
self.async_poll_battery, BATTERY_SCAN_INTERVAL
|
||||
)
|
||||
else:
|
||||
_LOGGER.warning(
|
||||
"S1 firmware detected, battery sensor may update infrequently"
|
||||
)
|
||||
# Battery events can be infrequent, polling is still necessary
|
||||
self._battery_poll_timer = self.hass.helpers.event.track_time_interval(
|
||||
self.async_poll_battery, BATTERY_SCAN_INTERVAL
|
||||
)
|
||||
dispatcher_send(self.hass, SONOS_CREATE_BATTERY, self)
|
||||
else:
|
||||
self._platforms_ready.update({BINARY_SENSOR_DOMAIN, SENSOR_DOMAIN})
|
||||
|
||||
if new_alarms := self.update_alarms_for_speaker():
|
||||
dispatcher_send(self.hass, SONOS_CREATE_ALARM, self, new_alarms)
|
||||
@ -386,7 +380,7 @@ class SonosSpeaker:
|
||||
|
||||
async def async_update_device_properties(self, event: SonosEvent) -> None:
|
||||
"""Update device properties from an event."""
|
||||
if (more_info := event.variables.get("more_info")) is not None:
|
||||
if more_info := event.variables.get("more_info"):
|
||||
battery_dict = dict(x.split(":") for x in more_info.split(","))
|
||||
await self.async_update_battery_info(battery_dict)
|
||||
self.async_write_entity_states()
|
||||
@ -514,12 +508,19 @@ class SonosSpeaker:
|
||||
|
||||
if not self._battery_poll_timer:
|
||||
# Battery info received for an S1 speaker
|
||||
new_battery = not self.battery_info
|
||||
self.battery_info.update(
|
||||
{
|
||||
"Level": int(battery_dict["BattPct"]),
|
||||
"PowerSource": "EXTERNAL" if is_charging else "BATTERY",
|
||||
}
|
||||
)
|
||||
if new_battery:
|
||||
_LOGGER.warning(
|
||||
"S1 firmware detected on %s, battery info may update infrequently",
|
||||
self.zone_name,
|
||||
)
|
||||
async_dispatcher_send(self.hass, SONOS_CREATE_BATTERY, self)
|
||||
return
|
||||
|
||||
if is_charging == self.charging:
|
||||
|
@ -5,7 +5,7 @@ from typing import Final
|
||||
|
||||
MAJOR_VERSION: Final = 2021
|
||||
MINOR_VERSION: Final = 6
|
||||
PATCH_VERSION: Final = "3"
|
||||
PATCH_VERSION: Final = "4"
|
||||
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
|
||||
__version__: Final = f"{__short_version__}.{PATCH_VERSION}"
|
||||
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 8, 0)
|
||||
|
@ -905,6 +905,8 @@ async def test_form_reauth_websocket(hass, remotews: Mock):
|
||||
"""Test reauthenticate websocket."""
|
||||
entry = MockConfigEntry(domain=DOMAIN, data=MOCK_WS_ENTRY)
|
||||
entry.add_to_hass(hass)
|
||||
assert entry.state == config_entries.ConfigEntryState.NOT_LOADED
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"entry_id": entry.entry_id, "source": config_entries.SOURCE_REAUTH},
|
||||
@ -920,6 +922,7 @@ async def test_form_reauth_websocket(hass, remotews: Mock):
|
||||
await hass.async_block_till_done()
|
||||
assert result2["type"] == "abort"
|
||||
assert result2["reason"] == "reauth_successful"
|
||||
assert entry.state == config_entries.ConfigEntryState.LOADED
|
||||
|
||||
|
||||
async def test_form_reauth_websocket_cannot_connect(hass, remotews: Mock):
|
||||
|
@ -419,6 +419,18 @@ async def test_state_without_turnon(hass, remote):
|
||||
assert await hass.services.async_call(
|
||||
DOMAIN, SERVICE_TURN_OFF, {ATTR_ENTITY_ID: ENTITY_ID_NOTURNON}, True
|
||||
)
|
||||
state = hass.states.get(ENTITY_ID_NOTURNON)
|
||||
# Should be STATE_UNAVAILABLE after the timer expires
|
||||
assert state.state == STATE_OFF
|
||||
|
||||
next_update = dt_util.utcnow() + timedelta(seconds=20)
|
||||
with patch(
|
||||
"homeassistant.components.samsungtv.bridge.Remote",
|
||||
side_effect=OSError,
|
||||
), patch("homeassistant.util.dt.utcnow", return_value=next_update):
|
||||
async_fire_time_changed(hass, next_update)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(ENTITY_ID_NOTURNON)
|
||||
# Should be STATE_UNAVAILABLE since there is no way to turn it back on
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
|
@ -3,7 +3,7 @@ from pysonos.exceptions import NotSupportedException
|
||||
|
||||
from homeassistant.components.sonos import DOMAIN
|
||||
from homeassistant.components.sonos.binary_sensor import ATTR_BATTERY_POWER_SOURCE
|
||||
from homeassistant.const import STATE_OFF, STATE_ON, STATE_UNAVAILABLE
|
||||
from homeassistant.const import STATE_OFF, STATE_ON
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
|
||||
@ -68,21 +68,18 @@ async def test_battery_on_S1(hass, config_entry, config, soco, battery_event):
|
||||
|
||||
entity_registry = await hass.helpers.entity_registry.async_get_registry()
|
||||
|
||||
battery = entity_registry.entities["sensor.zone_a_battery"]
|
||||
battery_state = hass.states.get(battery.entity_id)
|
||||
assert battery_state.state == STATE_UNAVAILABLE
|
||||
|
||||
power = entity_registry.entities["binary_sensor.zone_a_power"]
|
||||
power_state = hass.states.get(power.entity_id)
|
||||
assert power_state.state == STATE_UNAVAILABLE
|
||||
assert "sensor.zone_a_battery" not in entity_registry.entities
|
||||
assert "binary_sensor.zone_a_power" not in entity_registry.entities
|
||||
|
||||
# Update the speaker with a callback event
|
||||
sub_callback(battery_event)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
battery = entity_registry.entities["sensor.zone_a_battery"]
|
||||
battery_state = hass.states.get(battery.entity_id)
|
||||
assert battery_state.state == "100"
|
||||
|
||||
power = entity_registry.entities["binary_sensor.zone_a_power"]
|
||||
power_state = hass.states.get(power.entity_id)
|
||||
assert power_state.state == STATE_OFF
|
||||
assert power_state.attributes.get(ATTR_BATTERY_POWER_SOURCE) == "BATTERY"
|
||||
|
Loading…
x
Reference in New Issue
Block a user