mirror of
https://github.com/home-assistant/core.git
synced 2025-07-29 16:17:20 +00:00
Merge pull request #73334 from home-assistant/rc
This commit is contained in:
commit
3759adcf2b
@ -70,6 +70,7 @@ class FeedManager:
|
|||||||
self._last_entry_timestamp = None
|
self._last_entry_timestamp = None
|
||||||
self._last_update_successful = False
|
self._last_update_successful = False
|
||||||
self._has_published_parsed = False
|
self._has_published_parsed = False
|
||||||
|
self._has_updated_parsed = False
|
||||||
self._event_type = EVENT_FEEDREADER
|
self._event_type = EVENT_FEEDREADER
|
||||||
self._feed_id = url
|
self._feed_id = url
|
||||||
hass.bus.listen_once(EVENT_HOMEASSISTANT_START, lambda _: self._update())
|
hass.bus.listen_once(EVENT_HOMEASSISTANT_START, lambda _: self._update())
|
||||||
@ -122,7 +123,7 @@ class FeedManager:
|
|||||||
)
|
)
|
||||||
self._filter_entries()
|
self._filter_entries()
|
||||||
self._publish_new_entries()
|
self._publish_new_entries()
|
||||||
if self._has_published_parsed:
|
if self._has_published_parsed or self._has_updated_parsed:
|
||||||
self._storage.put_timestamp(
|
self._storage.put_timestamp(
|
||||||
self._feed_id, self._last_entry_timestamp
|
self._feed_id, self._last_entry_timestamp
|
||||||
)
|
)
|
||||||
@ -143,7 +144,7 @@ class FeedManager:
|
|||||||
|
|
||||||
def _update_and_fire_entry(self, entry):
|
def _update_and_fire_entry(self, entry):
|
||||||
"""Update last_entry_timestamp and fire entry."""
|
"""Update last_entry_timestamp and fire entry."""
|
||||||
# Check if the entry has a published date.
|
# Check if the entry has a published or updated date.
|
||||||
if "published_parsed" in entry and entry.published_parsed:
|
if "published_parsed" in entry and entry.published_parsed:
|
||||||
# We are lucky, `published_parsed` data available, let's make use of
|
# We are lucky, `published_parsed` data available, let's make use of
|
||||||
# it to publish only new available entries since the last run
|
# it to publish only new available entries since the last run
|
||||||
@ -151,9 +152,20 @@ class FeedManager:
|
|||||||
self._last_entry_timestamp = max(
|
self._last_entry_timestamp = max(
|
||||||
entry.published_parsed, self._last_entry_timestamp
|
entry.published_parsed, self._last_entry_timestamp
|
||||||
)
|
)
|
||||||
|
elif "updated_parsed" in entry and entry.updated_parsed:
|
||||||
|
# We are lucky, `updated_parsed` data available, let's make use of
|
||||||
|
# it to publish only new available entries since the last run
|
||||||
|
self._has_updated_parsed = True
|
||||||
|
self._last_entry_timestamp = max(
|
||||||
|
entry.updated_parsed, self._last_entry_timestamp
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
self._has_published_parsed = False
|
self._has_published_parsed = False
|
||||||
_LOGGER.debug("No published_parsed info available for entry %s", entry)
|
self._has_updated_parsed = False
|
||||||
|
_LOGGER.debug(
|
||||||
|
"No published_parsed or updated_parsed info available for entry %s",
|
||||||
|
entry,
|
||||||
|
)
|
||||||
entry.update({"feed_url": self._url})
|
entry.update({"feed_url": self._url})
|
||||||
self._hass.bus.fire(self._event_type, entry)
|
self._hass.bus.fire(self._event_type, entry)
|
||||||
|
|
||||||
@ -167,9 +179,16 @@ class FeedManager:
|
|||||||
# Set last entry timestamp as epoch time if not available
|
# Set last entry timestamp as epoch time if not available
|
||||||
self._last_entry_timestamp = datetime.utcfromtimestamp(0).timetuple()
|
self._last_entry_timestamp = datetime.utcfromtimestamp(0).timetuple()
|
||||||
for entry in self._feed.entries:
|
for entry in self._feed.entries:
|
||||||
if self._firstrun or (
|
if (
|
||||||
|
self._firstrun
|
||||||
|
or (
|
||||||
"published_parsed" in entry
|
"published_parsed" in entry
|
||||||
and entry.published_parsed > self._last_entry_timestamp
|
and entry.published_parsed > self._last_entry_timestamp
|
||||||
|
)
|
||||||
|
or (
|
||||||
|
"updated_parsed" in entry
|
||||||
|
and entry.updated_parsed > self._last_entry_timestamp
|
||||||
|
)
|
||||||
):
|
):
|
||||||
self._update_and_fire_entry(entry)
|
self._update_and_fire_entry(entry)
|
||||||
new_entries = True
|
new_entries = True
|
||||||
|
@ -460,7 +460,7 @@ async def _async_setup_themes(
|
|||||||
async def reload_themes(_: ServiceCall) -> None:
|
async def reload_themes(_: ServiceCall) -> None:
|
||||||
"""Reload themes."""
|
"""Reload themes."""
|
||||||
config = await async_hass_config_yaml(hass)
|
config = await async_hass_config_yaml(hass)
|
||||||
new_themes = config[DOMAIN].get(CONF_THEMES, {})
|
new_themes = config.get(DOMAIN, {}).get(CONF_THEMES, {})
|
||||||
hass.data[DATA_THEMES] = new_themes
|
hass.data[DATA_THEMES] = new_themes
|
||||||
if hass.data[DATA_DEFAULT_THEME] not in new_themes:
|
if hass.data[DATA_DEFAULT_THEME] not in new_themes:
|
||||||
hass.data[DATA_DEFAULT_THEME] = DEFAULT_THEME
|
hass.data[DATA_DEFAULT_THEME] = DEFAULT_THEME
|
||||||
|
@ -27,6 +27,7 @@ class HiveFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
self.data = {}
|
self.data = {}
|
||||||
self.tokens = {}
|
self.tokens = {}
|
||||||
self.entry = None
|
self.entry = None
|
||||||
|
self.device_registration = False
|
||||||
|
|
||||||
async def async_step_user(self, user_input=None):
|
async def async_step_user(self, user_input=None):
|
||||||
"""Prompt user input. Create or edit entry."""
|
"""Prompt user input. Create or edit entry."""
|
||||||
@ -88,6 +89,7 @@ class HiveFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
|
|
||||||
if not errors:
|
if not errors:
|
||||||
try:
|
try:
|
||||||
|
self.device_registration = True
|
||||||
return await self.async_setup_hive_entry()
|
return await self.async_setup_hive_entry()
|
||||||
except UnknownHiveError:
|
except UnknownHiveError:
|
||||||
errors["base"] = "unknown"
|
errors["base"] = "unknown"
|
||||||
@ -102,9 +104,10 @@ class HiveFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
raise UnknownHiveError
|
raise UnknownHiveError
|
||||||
|
|
||||||
# Setup the config entry
|
# Setup the config entry
|
||||||
|
if self.device_registration:
|
||||||
await self.hive_auth.device_registration("Home Assistant")
|
await self.hive_auth.device_registration("Home Assistant")
|
||||||
self.data["tokens"] = self.tokens
|
|
||||||
self.data["device_data"] = await self.hive_auth.getDeviceData()
|
self.data["device_data"] = await self.hive_auth.getDeviceData()
|
||||||
|
self.data["tokens"] = self.tokens
|
||||||
if self.context["source"] == config_entries.SOURCE_REAUTH:
|
if self.context["source"] == config_entries.SOURCE_REAUTH:
|
||||||
self.hass.config_entries.async_update_entry(
|
self.hass.config_entries.async_update_entry(
|
||||||
self.entry, title=self.data["username"], data=self.data
|
self.entry, title=self.data["username"], data=self.data
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"name": "Hive",
|
"name": "Hive",
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/hive",
|
"documentation": "https://www.home-assistant.io/integrations/hive",
|
||||||
"requirements": ["pyhiveapi==0.5.5"],
|
"requirements": ["pyhiveapi==0.5.9"],
|
||||||
"codeowners": ["@Rendili", "@KJonline"],
|
"codeowners": ["@Rendili", "@KJonline"],
|
||||||
"iot_class": "cloud_polling",
|
"iot_class": "cloud_polling",
|
||||||
"loggers": ["apyhiveapi"]
|
"loggers": ["apyhiveapi"]
|
||||||
|
@ -28,7 +28,14 @@ from homeassistant.data_entry_flow import BaseServiceInfo
|
|||||||
from homeassistant.exceptions import TemplateError, Unauthorized
|
from homeassistant.exceptions import TemplateError, Unauthorized
|
||||||
from homeassistant.helpers import config_validation as cv, event, template
|
from homeassistant.helpers import config_validation as cv, event, template
|
||||||
from homeassistant.helpers.device_registry import DeviceEntry
|
from homeassistant.helpers.device_registry import DeviceEntry
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
from homeassistant.helpers.dispatcher import (
|
||||||
|
async_dispatcher_connect,
|
||||||
|
async_dispatcher_send,
|
||||||
|
)
|
||||||
|
from homeassistant.helpers.reload import (
|
||||||
|
async_integration_yaml_config,
|
||||||
|
async_setup_reload_service,
|
||||||
|
)
|
||||||
from homeassistant.helpers.typing import ConfigType
|
from homeassistant.helpers.typing import ConfigType
|
||||||
|
|
||||||
# Loading the config flow file will register the flow
|
# Loading the config flow file will register the flow
|
||||||
@ -60,12 +67,14 @@ from .const import ( # noqa: F401
|
|||||||
DATA_MQTT,
|
DATA_MQTT,
|
||||||
DATA_MQTT_CONFIG,
|
DATA_MQTT_CONFIG,
|
||||||
DATA_MQTT_RELOAD_NEEDED,
|
DATA_MQTT_RELOAD_NEEDED,
|
||||||
|
DATA_MQTT_UPDATED_CONFIG,
|
||||||
DEFAULT_ENCODING,
|
DEFAULT_ENCODING,
|
||||||
DEFAULT_QOS,
|
DEFAULT_QOS,
|
||||||
DEFAULT_RETAIN,
|
DEFAULT_RETAIN,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
MQTT_CONNECTED,
|
MQTT_CONNECTED,
|
||||||
MQTT_DISCONNECTED,
|
MQTT_DISCONNECTED,
|
||||||
|
MQTT_RELOADED,
|
||||||
PLATFORMS,
|
PLATFORMS,
|
||||||
)
|
)
|
||||||
from .models import ( # noqa: F401
|
from .models import ( # noqa: F401
|
||||||
@ -227,7 +236,9 @@ async def _async_config_entry_updated(hass: HomeAssistant, entry: ConfigEntry) -
|
|||||||
await _async_setup_discovery(hass, mqtt_client.conf, entry)
|
await _async_setup_discovery(hass, mqtt_client.conf, entry)
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_setup_entry( # noqa: C901
|
||||||
|
hass: HomeAssistant, entry: ConfigEntry
|
||||||
|
) -> bool:
|
||||||
"""Load a config entry."""
|
"""Load a config entry."""
|
||||||
# Merge basic configuration, and add missing defaults for basic options
|
# Merge basic configuration, and add missing defaults for basic options
|
||||||
_merge_basic_config(hass, entry, hass.data.get(DATA_MQTT_CONFIG, {}))
|
_merge_basic_config(hass, entry, hass.data.get(DATA_MQTT_CONFIG, {}))
|
||||||
@ -364,6 +375,17 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
hass.data[DATA_CONFIG_ENTRY_LOCK] = asyncio.Lock()
|
hass.data[DATA_CONFIG_ENTRY_LOCK] = asyncio.Lock()
|
||||||
hass.data[CONFIG_ENTRY_IS_SETUP] = set()
|
hass.data[CONFIG_ENTRY_IS_SETUP] = set()
|
||||||
|
|
||||||
|
# Setup reload service. Once support for legacy config is removed in 2022.9, we
|
||||||
|
# should no longer call async_setup_reload_service but instead implement a custom
|
||||||
|
# service
|
||||||
|
await async_setup_reload_service(hass, DOMAIN, PLATFORMS)
|
||||||
|
|
||||||
|
async def _async_reload_platforms(_: Event | None) -> None:
|
||||||
|
"""Discover entities for a platform."""
|
||||||
|
config_yaml = await async_integration_yaml_config(hass, DOMAIN) or {}
|
||||||
|
hass.data[DATA_MQTT_UPDATED_CONFIG] = config_yaml.get(DOMAIN, {})
|
||||||
|
async_dispatcher_send(hass, MQTT_RELOADED)
|
||||||
|
|
||||||
async def async_forward_entry_setup():
|
async def async_forward_entry_setup():
|
||||||
"""Forward the config entry setup to the platforms."""
|
"""Forward the config entry setup to the platforms."""
|
||||||
async with hass.data[DATA_CONFIG_ENTRY_LOCK]:
|
async with hass.data[DATA_CONFIG_ENTRY_LOCK]:
|
||||||
@ -374,6 +396,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
await hass.config_entries.async_forward_entry_setup(
|
await hass.config_entries.async_forward_entry_setup(
|
||||||
entry, component
|
entry, component
|
||||||
)
|
)
|
||||||
|
# Setup reload service after all platforms have loaded
|
||||||
|
entry.async_on_unload(
|
||||||
|
hass.bus.async_listen("event_mqtt_reloaded", _async_reload_platforms)
|
||||||
|
)
|
||||||
|
|
||||||
hass.async_create_task(async_forward_entry_setup())
|
hass.async_create_task(async_forward_entry_setup())
|
||||||
|
|
||||||
|
@ -35,6 +35,7 @@ DATA_CONFIG_ENTRY_LOCK = "mqtt_config_entry_lock"
|
|||||||
DATA_MQTT = "mqtt"
|
DATA_MQTT = "mqtt"
|
||||||
DATA_MQTT_CONFIG = "mqtt_config"
|
DATA_MQTT_CONFIG = "mqtt_config"
|
||||||
DATA_MQTT_RELOAD_NEEDED = "mqtt_reload_needed"
|
DATA_MQTT_RELOAD_NEEDED = "mqtt_reload_needed"
|
||||||
|
DATA_MQTT_UPDATED_CONFIG = "mqtt_updated_config"
|
||||||
|
|
||||||
DEFAULT_PREFIX = "homeassistant"
|
DEFAULT_PREFIX = "homeassistant"
|
||||||
DEFAULT_BIRTH_WILL_TOPIC = DEFAULT_PREFIX + "/status"
|
DEFAULT_BIRTH_WILL_TOPIC = DEFAULT_PREFIX + "/status"
|
||||||
@ -63,6 +64,7 @@ DOMAIN = "mqtt"
|
|||||||
|
|
||||||
MQTT_CONNECTED = "mqtt_connected"
|
MQTT_CONNECTED = "mqtt_connected"
|
||||||
MQTT_DISCONNECTED = "mqtt_disconnected"
|
MQTT_DISCONNECTED = "mqtt_disconnected"
|
||||||
|
MQTT_RELOADED = "mqtt_reloaded"
|
||||||
|
|
||||||
PAYLOAD_EMPTY_JSON = "{}"
|
PAYLOAD_EMPTY_JSON = "{}"
|
||||||
PAYLOAD_NONE = "None"
|
PAYLOAD_NONE = "None"
|
||||||
|
@ -25,7 +25,6 @@ from homeassistant.const import (
|
|||||||
STATE_CLOSING,
|
STATE_CLOSING,
|
||||||
STATE_OPEN,
|
STATE_OPEN,
|
||||||
STATE_OPENING,
|
STATE_OPENING,
|
||||||
STATE_UNKNOWN,
|
|
||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
@ -470,7 +469,6 @@ class MqttCover(MqttEntity, CoverEntity):
|
|||||||
}
|
}
|
||||||
|
|
||||||
if self._config.get(CONF_TILT_STATUS_TOPIC) is not None:
|
if self._config.get(CONF_TILT_STATUS_TOPIC) is not None:
|
||||||
self._tilt_value = STATE_UNKNOWN
|
|
||||||
topics["tilt_status_topic"] = {
|
topics["tilt_status_topic"] = {
|
||||||
"topic": self._config.get(CONF_TILT_STATUS_TOPIC),
|
"topic": self._config.get(CONF_TILT_STATUS_TOPIC),
|
||||||
"msg_callback": tilt_message_received,
|
"msg_callback": tilt_message_received,
|
||||||
|
@ -48,10 +48,6 @@ from homeassistant.helpers.entity import (
|
|||||||
async_generate_entity_id,
|
async_generate_entity_id,
|
||||||
)
|
)
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.reload import (
|
|
||||||
async_integration_yaml_config,
|
|
||||||
async_setup_reload_service,
|
|
||||||
)
|
|
||||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||||
|
|
||||||
from . import debug_info, subscription
|
from . import debug_info, subscription
|
||||||
@ -67,13 +63,14 @@ from .const import (
|
|||||||
DATA_MQTT,
|
DATA_MQTT,
|
||||||
DATA_MQTT_CONFIG,
|
DATA_MQTT_CONFIG,
|
||||||
DATA_MQTT_RELOAD_NEEDED,
|
DATA_MQTT_RELOAD_NEEDED,
|
||||||
|
DATA_MQTT_UPDATED_CONFIG,
|
||||||
DEFAULT_ENCODING,
|
DEFAULT_ENCODING,
|
||||||
DEFAULT_PAYLOAD_AVAILABLE,
|
DEFAULT_PAYLOAD_AVAILABLE,
|
||||||
DEFAULT_PAYLOAD_NOT_AVAILABLE,
|
DEFAULT_PAYLOAD_NOT_AVAILABLE,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
MQTT_CONNECTED,
|
MQTT_CONNECTED,
|
||||||
MQTT_DISCONNECTED,
|
MQTT_DISCONNECTED,
|
||||||
PLATFORMS,
|
MQTT_RELOADED,
|
||||||
)
|
)
|
||||||
from .debug_info import log_message, log_messages
|
from .debug_info import log_message, log_messages
|
||||||
from .discovery import (
|
from .discovery import (
|
||||||
@ -270,14 +267,11 @@ async def async_setup_platform_discovery(
|
|||||||
) -> CALLBACK_TYPE:
|
) -> CALLBACK_TYPE:
|
||||||
"""Set up platform discovery for manual config."""
|
"""Set up platform discovery for manual config."""
|
||||||
|
|
||||||
async def _async_discover_entities(event: Event | None) -> None:
|
async def _async_discover_entities() -> None:
|
||||||
"""Discover entities for a platform."""
|
"""Discover entities for a platform."""
|
||||||
if event:
|
if DATA_MQTT_UPDATED_CONFIG in hass.data:
|
||||||
# The platform has been reloaded
|
# The platform has been reloaded
|
||||||
config_yaml = await async_integration_yaml_config(hass, DOMAIN)
|
config_yaml = hass.data[DATA_MQTT_UPDATED_CONFIG]
|
||||||
if not config_yaml:
|
|
||||||
return
|
|
||||||
config_yaml = config_yaml.get(DOMAIN, {})
|
|
||||||
else:
|
else:
|
||||||
config_yaml = hass.data.get(DATA_MQTT_CONFIG, {})
|
config_yaml = hass.data.get(DATA_MQTT_CONFIG, {})
|
||||||
if not config_yaml:
|
if not config_yaml:
|
||||||
@ -293,8 +287,8 @@ async def async_setup_platform_discovery(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
unsub = hass.bus.async_listen("event_mqtt_reloaded", _async_discover_entities)
|
unsub = async_dispatcher_connect(hass, MQTT_RELOADED, _async_discover_entities)
|
||||||
await _async_discover_entities(None)
|
await _async_discover_entities()
|
||||||
return unsub
|
return unsub
|
||||||
|
|
||||||
|
|
||||||
@ -359,7 +353,6 @@ async def async_setup_platform_helper(
|
|||||||
async_setup_entities: SetupEntity,
|
async_setup_entities: SetupEntity,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Return true if platform setup should be aborted."""
|
"""Return true if platform setup should be aborted."""
|
||||||
await async_setup_reload_service(hass, DOMAIN, PLATFORMS)
|
|
||||||
if not bool(hass.config_entries.async_entries(DOMAIN)):
|
if not bool(hass.config_entries.async_entries(DOMAIN)):
|
||||||
hass.data[DATA_MQTT_RELOAD_NEEDED] = None
|
hass.data[DATA_MQTT_RELOAD_NEEDED] = None
|
||||||
_LOGGER.warning(
|
_LOGGER.warning(
|
||||||
|
@ -18,7 +18,6 @@ from .const import (
|
|||||||
KEY_COORDINATOR_SPEED,
|
KEY_COORDINATOR_SPEED,
|
||||||
KEY_COORDINATOR_TRAFFIC,
|
KEY_COORDINATOR_TRAFFIC,
|
||||||
KEY_ROUTER,
|
KEY_ROUTER,
|
||||||
MODE_ROUTER,
|
|
||||||
PLATFORMS,
|
PLATFORMS,
|
||||||
)
|
)
|
||||||
from .errors import CannotLoginException
|
from .errors import CannotLoginException
|
||||||
@ -72,7 +71,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
|
|
||||||
async def async_update_devices() -> bool:
|
async def async_update_devices() -> bool:
|
||||||
"""Fetch data from the router."""
|
"""Fetch data from the router."""
|
||||||
if router.mode == MODE_ROUTER:
|
if router.track_devices:
|
||||||
return await router.async_update_device_trackers()
|
return await router.async_update_device_trackers()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -107,7 +106,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
update_interval=SPEED_TEST_INTERVAL,
|
update_interval=SPEED_TEST_INTERVAL,
|
||||||
)
|
)
|
||||||
|
|
||||||
if router.mode == MODE_ROUTER:
|
if router.track_devices:
|
||||||
await coordinator.async_config_entry_first_refresh()
|
await coordinator.async_config_entry_first_refresh()
|
||||||
await coordinator_traffic_meter.async_config_entry_first_refresh()
|
await coordinator_traffic_meter.async_config_entry_first_refresh()
|
||||||
|
|
||||||
@ -134,7 +133,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
if not hass.data[DOMAIN]:
|
if not hass.data[DOMAIN]:
|
||||||
hass.data.pop(DOMAIN)
|
hass.data.pop(DOMAIN)
|
||||||
|
|
||||||
if router.mode != MODE_ROUTER:
|
if not router.track_devices:
|
||||||
router_id = None
|
router_id = None
|
||||||
# Remove devices that are no longer tracked
|
# Remove devices that are no longer tracked
|
||||||
device_registry = dr.async_get(hass)
|
device_registry = dr.async_get(hass)
|
||||||
|
@ -80,6 +80,7 @@ class NetgearRouter:
|
|||||||
self.hardware_version = ""
|
self.hardware_version = ""
|
||||||
self.serial_number = ""
|
self.serial_number = ""
|
||||||
|
|
||||||
|
self.track_devices = True
|
||||||
self.method_version = 1
|
self.method_version = 1
|
||||||
consider_home_int = entry.options.get(
|
consider_home_int = entry.options.get(
|
||||||
CONF_CONSIDER_HOME, DEFAULT_CONSIDER_HOME.total_seconds()
|
CONF_CONSIDER_HOME, DEFAULT_CONSIDER_HOME.total_seconds()
|
||||||
@ -112,11 +113,23 @@ class NetgearRouter:
|
|||||||
self.serial_number = self._info["SerialNumber"]
|
self.serial_number = self._info["SerialNumber"]
|
||||||
self.mode = self._info.get("DeviceMode", MODE_ROUTER)
|
self.mode = self._info.get("DeviceMode", MODE_ROUTER)
|
||||||
|
|
||||||
|
enabled_entries = [
|
||||||
|
entry
|
||||||
|
for entry in self.hass.config_entries.async_entries(DOMAIN)
|
||||||
|
if entry.disabled_by is None
|
||||||
|
]
|
||||||
|
self.track_devices = self.mode == MODE_ROUTER or len(enabled_entries) == 1
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Netgear track_devices = '%s', device mode '%s'",
|
||||||
|
self.track_devices,
|
||||||
|
self.mode,
|
||||||
|
)
|
||||||
|
|
||||||
for model in MODELS_V2:
|
for model in MODELS_V2:
|
||||||
if self.model.startswith(model):
|
if self.model.startswith(model):
|
||||||
self.method_version = 2
|
self.method_version = 2
|
||||||
|
|
||||||
if self.method_version == 2 and self.mode == MODE_ROUTER:
|
if self.method_version == 2 and self.track_devices:
|
||||||
if not self._api.get_attached_devices_2():
|
if not self._api.get_attached_devices_2():
|
||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
"Netgear Model '%s' in MODELS_V2 list, but failed to get attached devices using V2",
|
"Netgear Model '%s' in MODELS_V2 list, but failed to get attached devices using V2",
|
||||||
@ -133,7 +146,7 @@ class NetgearRouter:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
# set already known devices to away instead of unavailable
|
# set already known devices to away instead of unavailable
|
||||||
if self.mode == MODE_ROUTER:
|
if self.track_devices:
|
||||||
device_registry = dr.async_get(self.hass)
|
device_registry = dr.async_get(self.hass)
|
||||||
devices = dr.async_entries_for_config_entry(device_registry, self.entry_id)
|
devices = dr.async_entries_for_config_entry(device_registry, self.entry_id)
|
||||||
for device_entry in devices:
|
for device_entry in devices:
|
||||||
|
@ -5,6 +5,7 @@ from collections.abc import Callable
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from datetime import date, datetime
|
from datetime import date, datetime
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
import logging
|
||||||
|
|
||||||
from homeassistant.components.sensor import (
|
from homeassistant.components.sensor import (
|
||||||
RestoreSensor,
|
RestoreSensor,
|
||||||
@ -34,6 +35,8 @@ from .const import (
|
|||||||
)
|
)
|
||||||
from .router import NetgearDeviceEntity, NetgearRouter, NetgearRouterEntity
|
from .router import NetgearDeviceEntity, NetgearRouter, NetgearRouterEntity
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
SENSOR_TYPES = {
|
SENSOR_TYPES = {
|
||||||
"type": SensorEntityDescription(
|
"type": SensorEntityDescription(
|
||||||
key="type",
|
key="type",
|
||||||
@ -114,7 +117,7 @@ SENSOR_TRAFFIC_TYPES = [
|
|||||||
native_unit_of_measurement=DATA_MEGABYTES,
|
native_unit_of_measurement=DATA_MEGABYTES,
|
||||||
icon="mdi:upload",
|
icon="mdi:upload",
|
||||||
index=0,
|
index=0,
|
||||||
value=lambda data: data[0] if data is not None else None,
|
value=lambda data: data[0],
|
||||||
),
|
),
|
||||||
NetgearSensorEntityDescription(
|
NetgearSensorEntityDescription(
|
||||||
key="NewWeekUpload",
|
key="NewWeekUpload",
|
||||||
@ -123,7 +126,7 @@ SENSOR_TRAFFIC_TYPES = [
|
|||||||
native_unit_of_measurement=DATA_MEGABYTES,
|
native_unit_of_measurement=DATA_MEGABYTES,
|
||||||
icon="mdi:upload",
|
icon="mdi:upload",
|
||||||
index=1,
|
index=1,
|
||||||
value=lambda data: data[1] if data is not None else None,
|
value=lambda data: data[1],
|
||||||
),
|
),
|
||||||
NetgearSensorEntityDescription(
|
NetgearSensorEntityDescription(
|
||||||
key="NewWeekDownload",
|
key="NewWeekDownload",
|
||||||
@ -132,7 +135,7 @@ SENSOR_TRAFFIC_TYPES = [
|
|||||||
native_unit_of_measurement=DATA_MEGABYTES,
|
native_unit_of_measurement=DATA_MEGABYTES,
|
||||||
icon="mdi:download",
|
icon="mdi:download",
|
||||||
index=0,
|
index=0,
|
||||||
value=lambda data: data[0] if data is not None else None,
|
value=lambda data: data[0],
|
||||||
),
|
),
|
||||||
NetgearSensorEntityDescription(
|
NetgearSensorEntityDescription(
|
||||||
key="NewWeekDownload",
|
key="NewWeekDownload",
|
||||||
@ -141,7 +144,7 @@ SENSOR_TRAFFIC_TYPES = [
|
|||||||
native_unit_of_measurement=DATA_MEGABYTES,
|
native_unit_of_measurement=DATA_MEGABYTES,
|
||||||
icon="mdi:download",
|
icon="mdi:download",
|
||||||
index=1,
|
index=1,
|
||||||
value=lambda data: data[1] if data is not None else None,
|
value=lambda data: data[1],
|
||||||
),
|
),
|
||||||
NetgearSensorEntityDescription(
|
NetgearSensorEntityDescription(
|
||||||
key="NewMonthUpload",
|
key="NewMonthUpload",
|
||||||
@ -150,7 +153,7 @@ SENSOR_TRAFFIC_TYPES = [
|
|||||||
native_unit_of_measurement=DATA_MEGABYTES,
|
native_unit_of_measurement=DATA_MEGABYTES,
|
||||||
icon="mdi:upload",
|
icon="mdi:upload",
|
||||||
index=0,
|
index=0,
|
||||||
value=lambda data: data[0] if data is not None else None,
|
value=lambda data: data[0],
|
||||||
),
|
),
|
||||||
NetgearSensorEntityDescription(
|
NetgearSensorEntityDescription(
|
||||||
key="NewMonthUpload",
|
key="NewMonthUpload",
|
||||||
@ -159,7 +162,7 @@ SENSOR_TRAFFIC_TYPES = [
|
|||||||
native_unit_of_measurement=DATA_MEGABYTES,
|
native_unit_of_measurement=DATA_MEGABYTES,
|
||||||
icon="mdi:upload",
|
icon="mdi:upload",
|
||||||
index=1,
|
index=1,
|
||||||
value=lambda data: data[1] if data is not None else None,
|
value=lambda data: data[1],
|
||||||
),
|
),
|
||||||
NetgearSensorEntityDescription(
|
NetgearSensorEntityDescription(
|
||||||
key="NewMonthDownload",
|
key="NewMonthDownload",
|
||||||
@ -168,7 +171,7 @@ SENSOR_TRAFFIC_TYPES = [
|
|||||||
native_unit_of_measurement=DATA_MEGABYTES,
|
native_unit_of_measurement=DATA_MEGABYTES,
|
||||||
icon="mdi:download",
|
icon="mdi:download",
|
||||||
index=0,
|
index=0,
|
||||||
value=lambda data: data[0] if data is not None else None,
|
value=lambda data: data[0],
|
||||||
),
|
),
|
||||||
NetgearSensorEntityDescription(
|
NetgearSensorEntityDescription(
|
||||||
key="NewMonthDownload",
|
key="NewMonthDownload",
|
||||||
@ -177,7 +180,7 @@ SENSOR_TRAFFIC_TYPES = [
|
|||||||
native_unit_of_measurement=DATA_MEGABYTES,
|
native_unit_of_measurement=DATA_MEGABYTES,
|
||||||
icon="mdi:download",
|
icon="mdi:download",
|
||||||
index=1,
|
index=1,
|
||||||
value=lambda data: data[1] if data is not None else None,
|
value=lambda data: data[1],
|
||||||
),
|
),
|
||||||
NetgearSensorEntityDescription(
|
NetgearSensorEntityDescription(
|
||||||
key="NewLastMonthUpload",
|
key="NewLastMonthUpload",
|
||||||
@ -186,7 +189,7 @@ SENSOR_TRAFFIC_TYPES = [
|
|||||||
native_unit_of_measurement=DATA_MEGABYTES,
|
native_unit_of_measurement=DATA_MEGABYTES,
|
||||||
icon="mdi:upload",
|
icon="mdi:upload",
|
||||||
index=0,
|
index=0,
|
||||||
value=lambda data: data[0] if data is not None else None,
|
value=lambda data: data[0],
|
||||||
),
|
),
|
||||||
NetgearSensorEntityDescription(
|
NetgearSensorEntityDescription(
|
||||||
key="NewLastMonthUpload",
|
key="NewLastMonthUpload",
|
||||||
@ -195,7 +198,7 @@ SENSOR_TRAFFIC_TYPES = [
|
|||||||
native_unit_of_measurement=DATA_MEGABYTES,
|
native_unit_of_measurement=DATA_MEGABYTES,
|
||||||
icon="mdi:upload",
|
icon="mdi:upload",
|
||||||
index=1,
|
index=1,
|
||||||
value=lambda data: data[1] if data is not None else None,
|
value=lambda data: data[1],
|
||||||
),
|
),
|
||||||
NetgearSensorEntityDescription(
|
NetgearSensorEntityDescription(
|
||||||
key="NewLastMonthDownload",
|
key="NewLastMonthDownload",
|
||||||
@ -204,7 +207,7 @@ SENSOR_TRAFFIC_TYPES = [
|
|||||||
native_unit_of_measurement=DATA_MEGABYTES,
|
native_unit_of_measurement=DATA_MEGABYTES,
|
||||||
icon="mdi:download",
|
icon="mdi:download",
|
||||||
index=0,
|
index=0,
|
||||||
value=lambda data: data[0] if data is not None else None,
|
value=lambda data: data[0],
|
||||||
),
|
),
|
||||||
NetgearSensorEntityDescription(
|
NetgearSensorEntityDescription(
|
||||||
key="NewLastMonthDownload",
|
key="NewLastMonthDownload",
|
||||||
@ -213,7 +216,7 @@ SENSOR_TRAFFIC_TYPES = [
|
|||||||
native_unit_of_measurement=DATA_MEGABYTES,
|
native_unit_of_measurement=DATA_MEGABYTES,
|
||||||
icon="mdi:download",
|
icon="mdi:download",
|
||||||
index=1,
|
index=1,
|
||||||
value=lambda data: data[1] if data is not None else None,
|
value=lambda data: data[1],
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -372,6 +375,17 @@ class NetgearRouterSensorEntity(NetgearRouterEntity, RestoreSensor):
|
|||||||
@callback
|
@callback
|
||||||
def async_update_device(self) -> None:
|
def async_update_device(self) -> None:
|
||||||
"""Update the Netgear device."""
|
"""Update the Netgear device."""
|
||||||
if self.coordinator.data is not None:
|
if self.coordinator.data is None:
|
||||||
|
return
|
||||||
|
|
||||||
data = self.coordinator.data.get(self.entity_description.key)
|
data = self.coordinator.data.get(self.entity_description.key)
|
||||||
|
if data is None:
|
||||||
|
self._value = None
|
||||||
|
_LOGGER.debug(
|
||||||
|
"key '%s' not in Netgear router response '%s'",
|
||||||
|
self.entity_description.key,
|
||||||
|
data,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
self._value = self.entity_description.value(data)
|
self._value = self.entity_description.value(data)
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"name": "RainMachine",
|
"name": "RainMachine",
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/rainmachine",
|
"documentation": "https://www.home-assistant.io/integrations/rainmachine",
|
||||||
"requirements": ["regenmaschine==2022.06.0"],
|
"requirements": ["regenmaschine==2022.06.1"],
|
||||||
"codeowners": ["@bachya"],
|
"codeowners": ["@bachya"],
|
||||||
"iot_class": "local_polling",
|
"iot_class": "local_polling",
|
||||||
"homekit": {
|
"homekit": {
|
||||||
|
@ -5,10 +5,9 @@ from sqlalchemy import text
|
|||||||
from sqlalchemy.orm.session import Session
|
from sqlalchemy.orm.session import Session
|
||||||
|
|
||||||
|
|
||||||
def db_size_bytes(session: Session, database_name: str) -> float:
|
def db_size_bytes(session: Session, database_name: str) -> float | None:
|
||||||
"""Get the mysql database size."""
|
"""Get the mysql database size."""
|
||||||
return float(
|
size = session.execute(
|
||||||
session.execute(
|
|
||||||
text(
|
text(
|
||||||
"SELECT ROUND(SUM(DATA_LENGTH + INDEX_LENGTH), 2) "
|
"SELECT ROUND(SUM(DATA_LENGTH + INDEX_LENGTH), 2) "
|
||||||
"FROM information_schema.TABLES WHERE "
|
"FROM information_schema.TABLES WHERE "
|
||||||
@ -16,4 +15,8 @@ def db_size_bytes(session: Session, database_name: str) -> float:
|
|||||||
),
|
),
|
||||||
{"database_name": database_name},
|
{"database_name": database_name},
|
||||||
).first()[0]
|
).first()[0]
|
||||||
)
|
|
||||||
|
if size is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return float(size)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
"""Support for balance data via the Starling Bank API."""
|
"""Support for balance data via the Starling Bank API."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from datetime import timedelta
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
@ -26,6 +27,7 @@ DEFAULT_SANDBOX = False
|
|||||||
DEFAULT_ACCOUNT_NAME = "Starling"
|
DEFAULT_ACCOUNT_NAME = "Starling"
|
||||||
|
|
||||||
ICON = "mdi:currency-gbp"
|
ICON = "mdi:currency-gbp"
|
||||||
|
SCAN_INTERVAL = timedelta(seconds=180)
|
||||||
|
|
||||||
ACCOUNT_SCHEMA = vol.Schema(
|
ACCOUNT_SCHEMA = vol.Schema(
|
||||||
{
|
{
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from datetime import timedelta
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from synology_dsm import SynologyDSM
|
from synology_dsm import SynologyDSM
|
||||||
@ -98,7 +97,7 @@ class SynoApi:
|
|||||||
self._async_setup_api_requests()
|
self._async_setup_api_requests()
|
||||||
|
|
||||||
await self._hass.async_add_executor_job(self._fetch_device_configuration)
|
await self._hass.async_add_executor_job(self._fetch_device_configuration)
|
||||||
await self.async_update()
|
await self.async_update(first_setup=True)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def subscribe(self, api_key: str, unique_id: str) -> Callable[[], None]:
|
def subscribe(self, api_key: str, unique_id: str) -> Callable[[], None]:
|
||||||
@ -251,7 +250,7 @@ class SynoApi:
|
|||||||
# ignore API errors during logout
|
# ignore API errors during logout
|
||||||
pass
|
pass
|
||||||
|
|
||||||
async def async_update(self, now: timedelta | None = None) -> None:
|
async def async_update(self, first_setup: bool = False) -> None:
|
||||||
"""Update function for updating API information."""
|
"""Update function for updating API information."""
|
||||||
LOGGER.debug("Start data update for '%s'", self._entry.unique_id)
|
LOGGER.debug("Start data update for '%s'", self._entry.unique_id)
|
||||||
self._async_setup_api_requests()
|
self._async_setup_api_requests()
|
||||||
@ -259,14 +258,22 @@ class SynoApi:
|
|||||||
await self._hass.async_add_executor_job(
|
await self._hass.async_add_executor_job(
|
||||||
self.dsm.update, self._with_information
|
self.dsm.update, self._with_information
|
||||||
)
|
)
|
||||||
except (SynologyDSMLoginFailedException, SynologyDSMRequestException) as err:
|
except (
|
||||||
LOGGER.warning(
|
SynologyDSMLoginFailedException,
|
||||||
"Connection error during update, fallback by reloading the entry"
|
SynologyDSMRequestException,
|
||||||
)
|
SynologyDSMAPIErrorException,
|
||||||
|
) as err:
|
||||||
LOGGER.debug(
|
LOGGER.debug(
|
||||||
"Connection error during update of '%s' with exception: %s",
|
"Connection error during update of '%s' with exception: %s",
|
||||||
self._entry.unique_id,
|
self._entry.unique_id,
|
||||||
err,
|
err,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if first_setup:
|
||||||
|
raise err
|
||||||
|
|
||||||
|
LOGGER.warning(
|
||||||
|
"Connection error during update, fallback by reloading the entry"
|
||||||
|
)
|
||||||
await self._hass.config_entries.async_reload(self._entry.entry_id)
|
await self._hass.config_entries.async_reload(self._entry.entry_id)
|
||||||
return
|
return
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"name": "UniFi Protect",
|
"name": "UniFi Protect",
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/unifiprotect",
|
"documentation": "https://www.home-assistant.io/integrations/unifiprotect",
|
||||||
"requirements": ["pyunifiprotect==3.6.0", "unifi-discovery==1.1.3"],
|
"requirements": ["pyunifiprotect==3.9.2", "unifi-discovery==1.1.3"],
|
||||||
"dependencies": ["http"],
|
"dependencies": ["http"],
|
||||||
"codeowners": ["@briis", "@AngellusMortis", "@bdraco"],
|
"codeowners": ["@briis", "@AngellusMortis", "@bdraco"],
|
||||||
"quality_scale": "platinum",
|
"quality_scale": "platinum",
|
||||||
|
@ -4,10 +4,6 @@
|
|||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/wallbox",
|
"documentation": "https://www.home-assistant.io/integrations/wallbox",
|
||||||
"requirements": ["wallbox==0.4.9"],
|
"requirements": ["wallbox==0.4.9"],
|
||||||
"ssdp": [],
|
|
||||||
"zeroconf": [],
|
|
||||||
"homekit": {},
|
|
||||||
"dependencies": [],
|
|
||||||
"codeowners": ["@hesselonline"],
|
"codeowners": ["@hesselonline"],
|
||||||
"iot_class": "cloud_polling",
|
"iot_class": "cloud_polling",
|
||||||
"loggers": ["wallbox"]
|
"loggers": ["wallbox"]
|
||||||
|
@ -167,8 +167,12 @@ class WallboxSensor(WallboxEntity, SensorEntity):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def native_value(self) -> StateType:
|
def native_value(self) -> StateType:
|
||||||
"""Return the state of the sensor."""
|
"""Return the state of the sensor. Round the value when it, and the precision property are not None."""
|
||||||
if (sensor_round := self.entity_description.precision) is not None:
|
if (
|
||||||
|
sensor_round := self.entity_description.precision
|
||||||
|
) is not None and self.coordinator.data[
|
||||||
|
self.entity_description.key
|
||||||
|
] is not None:
|
||||||
return cast(
|
return cast(
|
||||||
StateType,
|
StateType,
|
||||||
round(self.coordinator.data[self.entity_description.key], sensor_round),
|
round(self.coordinator.data[self.entity_description.key], sensor_round),
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"name": "YoLink",
|
"name": "YoLink",
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/yolink",
|
"documentation": "https://www.home-assistant.io/integrations/yolink",
|
||||||
"requirements": ["yolink-api==0.0.6"],
|
"requirements": ["yolink-api==0.0.8"],
|
||||||
"dependencies": ["auth", "application_credentials"],
|
"dependencies": ["auth", "application_credentials"],
|
||||||
"codeowners": ["@matrixd2"],
|
"codeowners": ["@matrixd2"],
|
||||||
"iot_class": "cloud_push"
|
"iot_class": "cloud_push"
|
||||||
|
@ -7,7 +7,7 @@ from .backports.enum import StrEnum
|
|||||||
|
|
||||||
MAJOR_VERSION: Final = 2022
|
MAJOR_VERSION: Final = 2022
|
||||||
MINOR_VERSION: Final = 6
|
MINOR_VERSION: Final = 6
|
||||||
PATCH_VERSION: Final = "4"
|
PATCH_VERSION: Final = "5"
|
||||||
__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, 9, 0)
|
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 9, 0)
|
||||||
|
@ -1538,7 +1538,7 @@ pyheos==0.7.2
|
|||||||
pyhik==0.3.0
|
pyhik==0.3.0
|
||||||
|
|
||||||
# homeassistant.components.hive
|
# homeassistant.components.hive
|
||||||
pyhiveapi==0.5.5
|
pyhiveapi==0.5.9
|
||||||
|
|
||||||
# homeassistant.components.homematic
|
# homeassistant.components.homematic
|
||||||
pyhomematic==0.1.77
|
pyhomematic==0.1.77
|
||||||
@ -1993,7 +1993,7 @@ pytrafikverket==0.2.0.1
|
|||||||
pyudev==0.22.0
|
pyudev==0.22.0
|
||||||
|
|
||||||
# homeassistant.components.unifiprotect
|
# homeassistant.components.unifiprotect
|
||||||
pyunifiprotect==3.6.0
|
pyunifiprotect==3.9.2
|
||||||
|
|
||||||
# homeassistant.components.uptimerobot
|
# homeassistant.components.uptimerobot
|
||||||
pyuptimerobot==22.2.0
|
pyuptimerobot==22.2.0
|
||||||
@ -2065,7 +2065,7 @@ raincloudy==0.0.7
|
|||||||
raspyrfm-client==1.2.8
|
raspyrfm-client==1.2.8
|
||||||
|
|
||||||
# homeassistant.components.rainmachine
|
# homeassistant.components.rainmachine
|
||||||
regenmaschine==2022.06.0
|
regenmaschine==2022.06.1
|
||||||
|
|
||||||
# homeassistant.components.renault
|
# homeassistant.components.renault
|
||||||
renault-api==0.1.11
|
renault-api==0.1.11
|
||||||
@ -2486,7 +2486,7 @@ yeelight==0.7.10
|
|||||||
yeelightsunflower==0.0.10
|
yeelightsunflower==0.0.10
|
||||||
|
|
||||||
# homeassistant.components.yolink
|
# homeassistant.components.yolink
|
||||||
yolink-api==0.0.6
|
yolink-api==0.0.8
|
||||||
|
|
||||||
# homeassistant.components.youless
|
# homeassistant.components.youless
|
||||||
youless-api==0.16
|
youless-api==0.16
|
||||||
|
@ -1029,7 +1029,7 @@ pyhaversion==22.4.1
|
|||||||
pyheos==0.7.2
|
pyheos==0.7.2
|
||||||
|
|
||||||
# homeassistant.components.hive
|
# homeassistant.components.hive
|
||||||
pyhiveapi==0.5.5
|
pyhiveapi==0.5.9
|
||||||
|
|
||||||
# homeassistant.components.homematic
|
# homeassistant.components.homematic
|
||||||
pyhomematic==0.1.77
|
pyhomematic==0.1.77
|
||||||
@ -1322,7 +1322,7 @@ pytrafikverket==0.2.0.1
|
|||||||
pyudev==0.22.0
|
pyudev==0.22.0
|
||||||
|
|
||||||
# homeassistant.components.unifiprotect
|
# homeassistant.components.unifiprotect
|
||||||
pyunifiprotect==3.6.0
|
pyunifiprotect==3.9.2
|
||||||
|
|
||||||
# homeassistant.components.uptimerobot
|
# homeassistant.components.uptimerobot
|
||||||
pyuptimerobot==22.2.0
|
pyuptimerobot==22.2.0
|
||||||
@ -1364,7 +1364,7 @@ rachiopy==1.0.3
|
|||||||
radios==0.1.1
|
radios==0.1.1
|
||||||
|
|
||||||
# homeassistant.components.rainmachine
|
# homeassistant.components.rainmachine
|
||||||
regenmaschine==2022.06.0
|
regenmaschine==2022.06.1
|
||||||
|
|
||||||
# homeassistant.components.renault
|
# homeassistant.components.renault
|
||||||
renault-api==0.1.11
|
renault-api==0.1.11
|
||||||
@ -1638,7 +1638,7 @@ yalexs==1.1.25
|
|||||||
yeelight==0.7.10
|
yeelight==0.7.10
|
||||||
|
|
||||||
# homeassistant.components.yolink
|
# homeassistant.components.yolink
|
||||||
yolink-api==0.0.6
|
yolink-api==0.0.8
|
||||||
|
|
||||||
# homeassistant.components.youless
|
# homeassistant.components.youless
|
||||||
youless-api==0.16
|
youless-api==0.16
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
[metadata]
|
[metadata]
|
||||||
version = 2022.6.4
|
version = 2022.6.5
|
||||||
url = https://www.home-assistant.io/
|
url = https://www.home-assistant.io/
|
||||||
|
|
||||||
[options]
|
[options]
|
||||||
|
@ -23,6 +23,7 @@ VALID_CONFIG_1 = {feedreader.DOMAIN: {CONF_URLS: [URL]}}
|
|||||||
VALID_CONFIG_2 = {feedreader.DOMAIN: {CONF_URLS: [URL], CONF_SCAN_INTERVAL: 60}}
|
VALID_CONFIG_2 = {feedreader.DOMAIN: {CONF_URLS: [URL], CONF_SCAN_INTERVAL: 60}}
|
||||||
VALID_CONFIG_3 = {feedreader.DOMAIN: {CONF_URLS: [URL], CONF_MAX_ENTRIES: 100}}
|
VALID_CONFIG_3 = {feedreader.DOMAIN: {CONF_URLS: [URL], CONF_MAX_ENTRIES: 100}}
|
||||||
VALID_CONFIG_4 = {feedreader.DOMAIN: {CONF_URLS: [URL], CONF_MAX_ENTRIES: 5}}
|
VALID_CONFIG_4 = {feedreader.DOMAIN: {CONF_URLS: [URL], CONF_MAX_ENTRIES: 5}}
|
||||||
|
VALID_CONFIG_5 = {feedreader.DOMAIN: {CONF_URLS: [URL], CONF_MAX_ENTRIES: 1}}
|
||||||
|
|
||||||
|
|
||||||
def load_fixture_bytes(src):
|
def load_fixture_bytes(src):
|
||||||
@ -56,6 +57,12 @@ def fixture_feed_three_events(hass):
|
|||||||
return load_fixture_bytes("feedreader3.xml")
|
return load_fixture_bytes("feedreader3.xml")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="feed_atom_event")
|
||||||
|
def fixture_feed_atom_event(hass):
|
||||||
|
"""Load test feed data for atom event."""
|
||||||
|
return load_fixture_bytes("feedreader5.xml")
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name="events")
|
@pytest.fixture(name="events")
|
||||||
async def fixture_events(hass):
|
async def fixture_events(hass):
|
||||||
"""Fixture that catches alexa events."""
|
"""Fixture that catches alexa events."""
|
||||||
@ -98,7 +105,7 @@ async def test_setup_max_entries(hass):
|
|||||||
|
|
||||||
|
|
||||||
async def test_feed(hass, events, feed_one_event):
|
async def test_feed(hass, events, feed_one_event):
|
||||||
"""Test simple feed with valid data."""
|
"""Test simple rss feed with valid data."""
|
||||||
with patch(
|
with patch(
|
||||||
"feedparser.http.get",
|
"feedparser.http.get",
|
||||||
return_value=feed_one_event,
|
return_value=feed_one_event,
|
||||||
@ -120,6 +127,29 @@ async def test_feed(hass, events, feed_one_event):
|
|||||||
assert events[0].data.published_parsed.tm_min == 10
|
assert events[0].data.published_parsed.tm_min == 10
|
||||||
|
|
||||||
|
|
||||||
|
async def test_atom_feed(hass, events, feed_atom_event):
|
||||||
|
"""Test simple atom feed with valid data."""
|
||||||
|
with patch(
|
||||||
|
"feedparser.http.get",
|
||||||
|
return_value=feed_atom_event,
|
||||||
|
):
|
||||||
|
assert await async_setup_component(hass, feedreader.DOMAIN, VALID_CONFIG_5)
|
||||||
|
|
||||||
|
hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert len(events) == 1
|
||||||
|
assert events[0].data.title == "Atom-Powered Robots Run Amok"
|
||||||
|
assert events[0].data.description == "Some text."
|
||||||
|
assert events[0].data.link == "http://example.org/2003/12/13/atom03"
|
||||||
|
assert events[0].data.id == "urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a"
|
||||||
|
assert events[0].data.updated_parsed.tm_year == 2003
|
||||||
|
assert events[0].data.updated_parsed.tm_mon == 12
|
||||||
|
assert events[0].data.updated_parsed.tm_mday == 13
|
||||||
|
assert events[0].data.updated_parsed.tm_hour == 18
|
||||||
|
assert events[0].data.updated_parsed.tm_min == 30
|
||||||
|
|
||||||
|
|
||||||
async def test_feed_updates(hass, events, feed_one_event, feed_two_event):
|
async def test_feed_updates(hass, events, feed_one_event, feed_two_event):
|
||||||
"""Test feed updates."""
|
"""Test feed updates."""
|
||||||
side_effect = [
|
side_effect = [
|
||||||
|
@ -33,16 +33,6 @@ async def test_import_flow(hass):
|
|||||||
"AccessToken": "mock-access-token",
|
"AccessToken": "mock-access-token",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
), patch(
|
|
||||||
"homeassistant.components.hive.config_flow.Auth.device_registration",
|
|
||||||
return_value=True,
|
|
||||||
), patch(
|
|
||||||
"homeassistant.components.hive.config_flow.Auth.getDeviceData",
|
|
||||||
return_value=[
|
|
||||||
"mock-device-group-key",
|
|
||||||
"mock-device-key",
|
|
||||||
"mock-device-password",
|
|
||||||
],
|
|
||||||
), patch(
|
), patch(
|
||||||
"homeassistant.components.hive.async_setup", return_value=True
|
"homeassistant.components.hive.async_setup", return_value=True
|
||||||
) as mock_setup, patch(
|
) as mock_setup, patch(
|
||||||
@ -67,11 +57,6 @@ async def test_import_flow(hass):
|
|||||||
},
|
},
|
||||||
"ChallengeName": "SUCCESS",
|
"ChallengeName": "SUCCESS",
|
||||||
},
|
},
|
||||||
"device_data": [
|
|
||||||
"mock-device-group-key",
|
|
||||||
"mock-device-key",
|
|
||||||
"mock-device-password",
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
|
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
|
||||||
assert len(mock_setup.mock_calls) == 1
|
assert len(mock_setup.mock_calls) == 1
|
||||||
@ -96,16 +81,6 @@ async def test_user_flow(hass):
|
|||||||
"AccessToken": "mock-access-token",
|
"AccessToken": "mock-access-token",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
), patch(
|
|
||||||
"homeassistant.components.hive.config_flow.Auth.device_registration",
|
|
||||||
return_value=True,
|
|
||||||
), patch(
|
|
||||||
"homeassistant.components.hive.config_flow.Auth.getDeviceData",
|
|
||||||
return_value=[
|
|
||||||
"mock-device-group-key",
|
|
||||||
"mock-device-key",
|
|
||||||
"mock-device-password",
|
|
||||||
],
|
|
||||||
), patch(
|
), patch(
|
||||||
"homeassistant.components.hive.async_setup", return_value=True
|
"homeassistant.components.hive.async_setup", return_value=True
|
||||||
) as mock_setup, patch(
|
) as mock_setup, patch(
|
||||||
@ -130,11 +105,6 @@ async def test_user_flow(hass):
|
|||||||
},
|
},
|
||||||
"ChallengeName": "SUCCESS",
|
"ChallengeName": "SUCCESS",
|
||||||
},
|
},
|
||||||
"device_data": [
|
|
||||||
"mock-device-group-key",
|
|
||||||
"mock-device-key",
|
|
||||||
"mock-device-password",
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
assert len(mock_setup.mock_calls) == 1
|
assert len(mock_setup.mock_calls) == 1
|
||||||
|
@ -1256,12 +1256,8 @@ async def test_tilt_defaults(hass, mqtt_mock_entry_with_yaml_config):
|
|||||||
await mqtt_mock_entry_with_yaml_config()
|
await mqtt_mock_entry_with_yaml_config()
|
||||||
|
|
||||||
state_attributes_dict = hass.states.get("cover.test").attributes
|
state_attributes_dict = hass.states.get("cover.test").attributes
|
||||||
assert ATTR_CURRENT_TILT_POSITION in state_attributes_dict
|
# Tilt position is not yet known
|
||||||
|
assert ATTR_CURRENT_TILT_POSITION not in state_attributes_dict
|
||||||
current_cover_position = hass.states.get("cover.test").attributes[
|
|
||||||
ATTR_CURRENT_TILT_POSITION
|
|
||||||
]
|
|
||||||
assert current_cover_position == STATE_UNKNOWN
|
|
||||||
|
|
||||||
|
|
||||||
async def test_tilt_via_invocation_defaults(hass, mqtt_mock_entry_with_yaml_config):
|
async def test_tilt_via_invocation_defaults(hass, mqtt_mock_entry_with_yaml_config):
|
||||||
|
@ -168,7 +168,7 @@ async def sensor_fixture(
|
|||||||
sensor_obj.motion_detected_at = now - timedelta(hours=1)
|
sensor_obj.motion_detected_at = now - timedelta(hours=1)
|
||||||
sensor_obj.open_status_changed_at = now - timedelta(hours=1)
|
sensor_obj.open_status_changed_at = now - timedelta(hours=1)
|
||||||
sensor_obj.alarm_triggered_at = now - timedelta(hours=1)
|
sensor_obj.alarm_triggered_at = now - timedelta(hours=1)
|
||||||
sensor_obj.tampering_detected_at = now - timedelta(hours=1)
|
sensor_obj.tampering_detected_at = None
|
||||||
|
|
||||||
mock_entry.api.bootstrap.reset_objects()
|
mock_entry.api.bootstrap.reset_objects()
|
||||||
mock_entry.api.bootstrap.nvr.system_info.storage.devices = []
|
mock_entry.api.bootstrap.nvr.system_info.storage.devices = []
|
||||||
@ -204,7 +204,7 @@ async def sensor_none_fixture(
|
|||||||
sensor_obj.mount_type = MountType.LEAK
|
sensor_obj.mount_type = MountType.LEAK
|
||||||
sensor_obj.battery_status.is_low = False
|
sensor_obj.battery_status.is_low = False
|
||||||
sensor_obj.alarm_settings.is_enabled = False
|
sensor_obj.alarm_settings.is_enabled = False
|
||||||
sensor_obj.tampering_detected_at = now - timedelta(hours=1)
|
sensor_obj.tampering_detected_at = None
|
||||||
|
|
||||||
mock_entry.api.bootstrap.reset_objects()
|
mock_entry.api.bootstrap.reset_objects()
|
||||||
mock_entry.api.bootstrap.nvr.system_info.storage.devices = []
|
mock_entry.api.bootstrap.nvr.system_info.storage.devices = []
|
||||||
|
18
tests/fixtures/feedreader5.xml
vendored
Normal file
18
tests/fixtures/feedreader5.xml
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<feed
|
||||||
|
xmlns="http://www.w3.org/2005/Atom">
|
||||||
|
<title>Example Feed</title>
|
||||||
|
<link href="http://example.org/"/>
|
||||||
|
<updated>2003-12-13T18:30:02Z</updated>
|
||||||
|
<author>
|
||||||
|
<name>John Doe</name>
|
||||||
|
</author>
|
||||||
|
<id>urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6</id>
|
||||||
|
<entry>
|
||||||
|
<title>Atom-Powered Robots Run Amok</title>
|
||||||
|
<link href="http://example.org/2003/12/13/atom03"/>
|
||||||
|
<id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id>
|
||||||
|
<updated>2003-12-13T18:30:02Z</updated>
|
||||||
|
<summary>Some text.</summary>
|
||||||
|
</entry>
|
||||||
|
</feed>
|
Loading…
x
Reference in New Issue
Block a user