Merge pull request #56116 from home-assistant/rc

This commit is contained in:
Paulus Schoutsen 2021-09-11 13:02:14 -07:00 committed by GitHub
commit ddb0a6092f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 64 additions and 70 deletions

View File

@ -2,7 +2,7 @@
"domain": "amcrest", "domain": "amcrest",
"name": "Amcrest", "name": "Amcrest",
"documentation": "https://www.home-assistant.io/integrations/amcrest", "documentation": "https://www.home-assistant.io/integrations/amcrest",
"requirements": ["amcrest==1.8.0"], "requirements": ["amcrest==1.8.1"],
"dependencies": ["ffmpeg"], "dependencies": ["ffmpeg"],
"codeowners": ["@flacjacket"], "codeowners": ["@flacjacket"],
"iot_class": "local_polling" "iot_class": "local_polling"

View File

@ -2,7 +2,7 @@
"domain": "myq", "domain": "myq",
"name": "MyQ", "name": "MyQ",
"documentation": "https://www.home-assistant.io/integrations/myq", "documentation": "https://www.home-assistant.io/integrations/myq",
"requirements": ["pymyq==3.1.3"], "requirements": ["pymyq==3.1.4"],
"codeowners": ["@bdraco","@ehendrix23"], "codeowners": ["@bdraco","@ehendrix23"],
"config_flow": true, "config_flow": true,
"homekit": { "homekit": {

View File

@ -209,6 +209,9 @@ class SensorEntity(Entity):
and not self._last_reset_reported and not self._last_reset_reported
): ):
self._last_reset_reported = True self._last_reset_reported = True
if self.platform and self.platform.platform_name == "energy":
return {ATTR_LAST_RESET: last_reset.isoformat()}
report_issue = self._suggest_report_issue() report_issue = self._suggest_report_issue()
_LOGGER.warning( _LOGGER.warning(
"Entity %s (%s) with state_class %s has set last_reset. Setting " "Entity %s (%s) with state_class %s has set last_reset. Setting "

View File

@ -441,8 +441,8 @@ def compile_statistics( # noqa: C901
_LOGGER.info( _LOGGER.info(
"Detected new cycle for %s, value dropped from %s to %s", "Detected new cycle for %s, value dropped from %s to %s",
entity_id, entity_id,
fstate,
new_state, new_state,
fstate,
) )
if reset: if reset:

View File

@ -4,7 +4,7 @@
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/yamaha_musiccast", "documentation": "https://www.home-assistant.io/integrations/yamaha_musiccast",
"requirements": [ "requirements": [
"aiomusiccast==0.9.1" "aiomusiccast==0.9.2"
], ],
"ssdp": [ "ssdp": [
{ {

View File

@ -5,7 +5,7 @@ from typing import Final
MAJOR_VERSION: Final = 2021 MAJOR_VERSION: Final = 2021
MINOR_VERSION: Final = 9 MINOR_VERSION: Final = 9
PATCH_VERSION: Final = "5" PATCH_VERSION: Final = "6"
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}" __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
__version__: Final = f"{__short_version__}.{PATCH_VERSION}" __version__: Final = f"{__short_version__}.{PATCH_VERSION}"
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 8, 0) REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 8, 0)

View File

@ -6,16 +6,10 @@ from datetime import datetime, timedelta
import logging import logging
from typing import Any, cast from typing import Any, cast
from homeassistant.const import EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP from homeassistant.const import EVENT_HOMEASSISTANT_STOP
from homeassistant.core import ( from homeassistant.core import HomeAssistant, State, callback, valid_entity_id
CoreState,
HomeAssistant,
State,
callback,
valid_entity_id,
)
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_registry from homeassistant.helpers import entity_registry, start
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
from homeassistant.helpers.event import async_track_time_interval from homeassistant.helpers.event import async_track_time_interval
from homeassistant.helpers.json import JSONEncoder from homeassistant.helpers.json import JSONEncoder
@ -63,42 +57,36 @@ class StoredState:
class RestoreStateData: class RestoreStateData:
"""Helper class for managing the helper saved data.""" """Helper class for managing the helper saved data."""
@classmethod @staticmethod
async def async_get_instance(cls, hass: HomeAssistant) -> RestoreStateData: @singleton(DATA_RESTORE_STATE_TASK)
async def async_get_instance(hass: HomeAssistant) -> RestoreStateData:
"""Get the singleton instance of this data helper.""" """Get the singleton instance of this data helper."""
data = RestoreStateData(hass)
@singleton(DATA_RESTORE_STATE_TASK) try:
async def load_instance(hass: HomeAssistant) -> RestoreStateData: stored_states = await data.store.async_load()
"""Get the singleton instance of this data helper.""" except HomeAssistantError as exc:
data = cls(hass) _LOGGER.error("Error loading last states", exc_info=exc)
stored_states = None
try: if stored_states is None:
stored_states = await data.store.async_load() _LOGGER.debug("Not creating cache - no saved states found")
except HomeAssistantError as exc: data.last_states = {}
_LOGGER.error("Error loading last states", exc_info=exc) else:
stored_states = None data.last_states = {
item["state"]["entity_id"]: StoredState.from_dict(item)
for item in stored_states
if valid_entity_id(item["state"]["entity_id"])
}
_LOGGER.debug("Created cache with %s", list(data.last_states))
if stored_states is None: async def hass_start(hass: HomeAssistant) -> None:
_LOGGER.debug("Not creating cache - no saved states found") """Start the restore state task."""
data.last_states = {} data.async_setup_dump()
else:
data.last_states = {
item["state"]["entity_id"]: StoredState.from_dict(item)
for item in stored_states
if valid_entity_id(item["state"]["entity_id"])
}
_LOGGER.debug("Created cache with %s", list(data.last_states))
if hass.state == CoreState.running: start.async_at_start(hass, hass_start)
data.async_setup_dump()
else:
hass.bus.async_listen_once(
EVENT_HOMEASSISTANT_START, data.async_setup_dump
)
return data return data
return cast(RestoreStateData, await load_instance(hass))
@classmethod @classmethod
async def async_save_persistent_states(cls, hass: HomeAssistant) -> None: async def async_save_persistent_states(cls, hass: HomeAssistant) -> None:
@ -269,7 +257,9 @@ class RestoreEntity(Entity):
# Return None if this entity isn't added to hass yet # Return None if this entity isn't added to hass yet
_LOGGER.warning("Cannot get last state. Entity not added to hass") # type: ignore[unreachable] _LOGGER.warning("Cannot get last state. Entity not added to hass") # type: ignore[unreachable]
return None return None
data = await RestoreStateData.async_get_instance(self.hass) data = cast(
RestoreStateData, await RestoreStateData.async_get_instance(self.hass)
)
if self.entity_id not in data.last_states: if self.entity_id not in data.last_states:
return None return None
return data.last_states[self.entity_id].state return data.last_states[self.entity_id].state

View File

@ -26,31 +26,27 @@ def singleton(data_key: str) -> Callable[[FUNC], FUNC]:
@bind_hass @bind_hass
@functools.wraps(func) @functools.wraps(func)
def wrapped(hass: HomeAssistant) -> T: def wrapped(hass: HomeAssistant) -> T:
obj: T | None = hass.data.get(data_key) if data_key not in hass.data:
if obj is None: hass.data[data_key] = func(hass)
obj = hass.data[data_key] = func(hass) return cast(T, hass.data[data_key])
return obj
return wrapped return wrapped
@bind_hass @bind_hass
@functools.wraps(func) @functools.wraps(func)
async def async_wrapped(hass: HomeAssistant) -> T: async def async_wrapped(hass: HomeAssistant) -> T:
obj_or_evt = hass.data.get(data_key) if data_key not in hass.data:
if not obj_or_evt:
evt = hass.data[data_key] = asyncio.Event() evt = hass.data[data_key] = asyncio.Event()
result = await func(hass) result = await func(hass)
hass.data[data_key] = result hass.data[data_key] = result
evt.set() evt.set()
return cast(T, result) return cast(T, result)
obj_or_evt = hass.data[data_key]
if isinstance(obj_or_evt, asyncio.Event): if isinstance(obj_or_evt, asyncio.Event):
evt = obj_or_evt await obj_or_evt.wait()
await evt.wait() return cast(T, hass.data[data_key])
return cast(T, hass.data.get(data_key))
return cast(T, obj_or_evt) return cast(T, obj_or_evt)

View File

@ -216,7 +216,7 @@ aiolyric==1.0.7
aiomodernforms==0.1.8 aiomodernforms==0.1.8
# homeassistant.components.yamaha_musiccast # homeassistant.components.yamaha_musiccast
aiomusiccast==0.9.1 aiomusiccast==0.9.2
# homeassistant.components.keyboard_remote # homeassistant.components.keyboard_remote
aionotify==0.2.0 aionotify==0.2.0
@ -276,7 +276,7 @@ ambee==0.3.0
ambiclimate==0.2.1 ambiclimate==0.2.1
# homeassistant.components.amcrest # homeassistant.components.amcrest
amcrest==1.8.0 amcrest==1.8.1
# homeassistant.components.androidtv # homeassistant.components.androidtv
androidtv[async]==0.0.60 androidtv[async]==0.0.60
@ -1629,7 +1629,7 @@ pymonoprice==0.3
pymsteams==0.1.12 pymsteams==0.1.12
# homeassistant.components.myq # homeassistant.components.myq
pymyq==3.1.3 pymyq==3.1.4
# homeassistant.components.mysensors # homeassistant.components.mysensors
pymysensors==0.21.0 pymysensors==0.21.0

View File

@ -140,7 +140,7 @@ aiolyric==1.0.7
aiomodernforms==0.1.8 aiomodernforms==0.1.8
# homeassistant.components.yamaha_musiccast # homeassistant.components.yamaha_musiccast
aiomusiccast==0.9.1 aiomusiccast==0.9.2
# homeassistant.components.notion # homeassistant.components.notion
aionotion==3.0.2 aionotion==3.0.2
@ -942,7 +942,7 @@ pymodbus==2.5.3rc1
pymonoprice==0.3 pymonoprice==0.3
# homeassistant.components.myq # homeassistant.components.myq
pymyq==3.1.3 pymyq==3.1.4
# homeassistant.components.mysensors # homeassistant.components.mysensors
pymysensors==0.21.0 pymysensors==0.21.0

View File

@ -32,7 +32,7 @@ async def test_caching_data(hass):
await data.store.async_save([state.as_dict() for state in stored_states]) await data.store.async_save([state.as_dict() for state in stored_states])
# Emulate a fresh load # Emulate a fresh load
hass.data[DATA_RESTORE_STATE_TASK] = None hass.data.pop(DATA_RESTORE_STATE_TASK)
entity = RestoreEntity() entity = RestoreEntity()
entity.hass = hass entity.hass = hass
@ -59,7 +59,7 @@ async def test_periodic_write(hass):
await data.store.async_save([]) await data.store.async_save([])
# Emulate a fresh load # Emulate a fresh load
hass.data[DATA_RESTORE_STATE_TASK] = None hass.data.pop(DATA_RESTORE_STATE_TASK)
entity = RestoreEntity() entity = RestoreEntity()
entity.hass = hass entity.hass = hass
@ -105,7 +105,7 @@ async def test_save_persistent_states(hass):
await data.store.async_save([]) await data.store.async_save([])
# Emulate a fresh load # Emulate a fresh load
hass.data[DATA_RESTORE_STATE_TASK] = None hass.data.pop(DATA_RESTORE_STATE_TASK)
entity = RestoreEntity() entity = RestoreEntity()
entity.hass = hass entity.hass = hass
@ -170,7 +170,8 @@ async def test_hass_starting(hass):
await data.store.async_save([state.as_dict() for state in stored_states]) await data.store.async_save([state.as_dict() for state in stored_states])
# Emulate a fresh load # Emulate a fresh load
hass.data[DATA_RESTORE_STATE_TASK] = None hass.state = CoreState.not_running
hass.data.pop(DATA_RESTORE_STATE_TASK)
entity = RestoreEntity() entity = RestoreEntity()
entity.hass = hass entity.hass = hass

View File

@ -12,29 +12,33 @@ def mock_hass():
return Mock(data={}) return Mock(data={})
async def test_singleton_async(mock_hass): @pytest.mark.parametrize("result", (object(), {}, []))
async def test_singleton_async(mock_hass, result):
"""Test singleton with async function.""" """Test singleton with async function."""
@singleton.singleton("test_key") @singleton.singleton("test_key")
async def something(hass): async def something(hass):
return object() return result
result1 = await something(mock_hass) result1 = await something(mock_hass)
result2 = await something(mock_hass) result2 = await something(mock_hass)
assert result1 is result
assert result1 is result2 assert result1 is result2
assert "test_key" in mock_hass.data assert "test_key" in mock_hass.data
assert mock_hass.data["test_key"] is result1 assert mock_hass.data["test_key"] is result1
def test_singleton(mock_hass): @pytest.mark.parametrize("result", (object(), {}, []))
def test_singleton(mock_hass, result):
"""Test singleton with function.""" """Test singleton with function."""
@singleton.singleton("test_key") @singleton.singleton("test_key")
def something(hass): def something(hass):
return object() return result
result1 = something(mock_hass) result1 = something(mock_hass)
result2 = something(mock_hass) result2 = something(mock_hass)
assert result1 is result
assert result1 is result2 assert result1 is result2
assert "test_key" in mock_hass.data assert "test_key" in mock_hass.data
assert mock_hass.data["test_key"] is result1 assert mock_hass.data["test_key"] is result1