Create an issue if push updates fail for Shelly gen1 devices (#96910)

* Create an issue if push updates fail

* Improve strings

* Delete the issue when reloading configuration entry

* Change MAX_PUSH_UPDATE_FAILURES to 5

* Improve issue strings

* Add test

* Use for

* Update homeassistant/components/shelly/strings.json

Co-authored-by: Charles Garwood <cgarwood@newdealmultimedia.com>

* Simplify deleting the issue

---------

Co-authored-by: Charles Garwood <cgarwood@newdealmultimedia.com>
pull/96945/head
Maciej Bieniek 2023-07-20 11:11:05 +00:00 committed by GitHub
parent c433b251fa
commit db83dc9acc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 72 additions and 0 deletions

View File

@ -14,6 +14,7 @@ from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME, Platform from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME, Platform
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.helpers import issue_registry as ir
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.device_registry import ( from homeassistant.helpers.device_registry import (
@ -30,6 +31,7 @@ from .const import (
DEFAULT_COAP_PORT, DEFAULT_COAP_PORT,
DOMAIN, DOMAIN,
LOGGER, LOGGER,
PUSH_UPDATE_ISSUE_ID,
) )
from .coordinator import ( from .coordinator import (
ShellyBlockCoordinator, ShellyBlockCoordinator,
@ -323,6 +325,14 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return unload_ok return unload_ok
# delete push update issue if it exists
LOGGER.debug(
"Deleting issue %s", PUSH_UPDATE_ISSUE_ID.format(unique=entry.unique_id)
)
ir.async_delete_issue(
hass, DOMAIN, PUSH_UPDATE_ISSUE_ID.format(unique=entry.unique_id)
)
platforms = BLOCK_SLEEPING_PLATFORMS platforms = BLOCK_SLEEPING_PLATFORMS
if not entry.data.get(CONF_SLEEP_PERIOD): if not entry.data.get(CONF_SLEEP_PERIOD):

View File

@ -174,3 +174,7 @@ class BLEScannerMode(StrEnum):
DISABLED = "disabled" DISABLED = "disabled"
ACTIVE = "active" ACTIVE = "active"
PASSIVE = "passive" PASSIVE = "passive"
MAX_PUSH_UPDATE_FAILURES = 5
PUSH_UPDATE_ISSUE_ID = "push_update_{unique}"

View File

@ -17,6 +17,7 @@ from awesomeversion import AwesomeVersion
from homeassistant.config_entries import ConfigEntry, ConfigEntryState from homeassistant.config_entries import ConfigEntry, ConfigEntryState
from homeassistant.const import ATTR_DEVICE_ID, CONF_HOST, EVENT_HOMEASSISTANT_STOP from homeassistant.const import ATTR_DEVICE_ID, CONF_HOST, EVENT_HOMEASSISTANT_STOP
from homeassistant.core import CALLBACK_TYPE, Event, HomeAssistant, callback from homeassistant.core import CALLBACK_TYPE, Event, HomeAssistant, callback
from homeassistant.helpers import issue_registry as ir
from homeassistant.helpers.debounce import Debouncer from homeassistant.helpers.debounce import Debouncer
from homeassistant.helpers.device_registry import ( from homeassistant.helpers.device_registry import (
CONNECTION_NETWORK_MAC, CONNECTION_NETWORK_MAC,
@ -41,7 +42,9 @@ from .const import (
EVENT_SHELLY_CLICK, EVENT_SHELLY_CLICK,
INPUTS_EVENTS_DICT, INPUTS_EVENTS_DICT,
LOGGER, LOGGER,
MAX_PUSH_UPDATE_FAILURES,
MODELS_SUPPORTING_LIGHT_EFFECTS, MODELS_SUPPORTING_LIGHT_EFFECTS,
PUSH_UPDATE_ISSUE_ID,
REST_SENSORS_UPDATE_INTERVAL, REST_SENSORS_UPDATE_INTERVAL,
RPC_INPUTS_EVENTS_TYPES, RPC_INPUTS_EVENTS_TYPES,
RPC_RECONNECT_INTERVAL, RPC_RECONNECT_INTERVAL,
@ -162,6 +165,7 @@ class ShellyBlockCoordinator(ShellyCoordinatorBase[BlockDevice]):
self._last_effect: int | None = None self._last_effect: int | None = None
self._last_input_events_count: dict = {} self._last_input_events_count: dict = {}
self._last_target_temp: float | None = None self._last_target_temp: float | None = None
self._push_update_failures: int = 0
entry.async_on_unload( entry.async_on_unload(
self.async_add_listener(self._async_device_updates_handler) self.async_add_listener(self._async_device_updates_handler)
@ -270,6 +274,25 @@ class ShellyBlockCoordinator(ShellyCoordinatorBase[BlockDevice]):
except InvalidAuthError: except InvalidAuthError:
self.entry.async_start_reauth(self.hass) self.entry.async_start_reauth(self.hass)
else: else:
self._push_update_failures += 1
if self._push_update_failures > MAX_PUSH_UPDATE_FAILURES:
LOGGER.debug(
"Creating issue %s", PUSH_UPDATE_ISSUE_ID.format(unique=self.mac)
)
ir.async_create_issue(
self.hass,
DOMAIN,
PUSH_UPDATE_ISSUE_ID.format(unique=self.mac),
is_fixable=False,
is_persistent=False,
severity=ir.IssueSeverity.ERROR,
learn_more_url="https://www.home-assistant.io/integrations/shelly/#shelly-device-configuration-generation-1",
translation_key="push_update_failure",
translation_placeholders={
"device_name": self.entry.title,
"ip_address": self.device.ip_address,
},
)
device_update_info(self.hass, self.device, self.entry) device_update_info(self.hass, self.device, self.entry)
def async_setup(self) -> None: def async_setup(self) -> None:

View File

@ -118,5 +118,11 @@
} }
} }
} }
},
"issues": {
"push_update_failure": {
"title": "Shelly device {device_name} push update failure",
"description": "Home Assistant is not receiving push updates from the Shelly device {device_name} with IP address {ip_address}. Check the CoIoT configuration in the web panel of the device and your network configuration."
}
} }
} }

View File

@ -13,6 +13,7 @@ from homeassistant.components.shelly.const import (
ATTR_GENERATION, ATTR_GENERATION,
DOMAIN, DOMAIN,
ENTRY_RELOAD_COOLDOWN, ENTRY_RELOAD_COOLDOWN,
MAX_PUSH_UPDATE_FAILURES,
RPC_RECONNECT_INTERVAL, RPC_RECONNECT_INTERVAL,
SLEEP_PERIOD_MULTIPLIER, SLEEP_PERIOD_MULTIPLIER,
UPDATE_PERIOD_MULTIPLIER, UPDATE_PERIOD_MULTIPLIER,
@ -24,15 +25,18 @@ from homeassistant.helpers.device_registry import (
async_entries_for_config_entry, async_entries_for_config_entry,
async_get as async_get_dev_reg, async_get as async_get_dev_reg,
) )
import homeassistant.helpers.issue_registry as ir
from homeassistant.util import dt as dt_util from homeassistant.util import dt as dt_util
from . import ( from . import (
MOCK_MAC,
init_integration, init_integration,
inject_rpc_device_event, inject_rpc_device_event,
mock_polling_rpc_update, mock_polling_rpc_update,
mock_rest_update, mock_rest_update,
register_entity, register_entity,
) )
from .conftest import MOCK_BLOCKS
from tests.common import async_fire_time_changed from tests.common import async_fire_time_changed
@ -249,6 +253,31 @@ async def test_block_sleeping_device_no_periodic_updates(
assert hass.states.get(entity_id).state == STATE_UNAVAILABLE assert hass.states.get(entity_id).state == STATE_UNAVAILABLE
async def test_block_device_push_updates_failure(
hass: HomeAssistant, mock_block_device, monkeypatch
) -> None:
"""Test block device with push updates failure."""
issue_registry: ir.IssueRegistry = ir.async_get(hass)
monkeypatch.setattr(
mock_block_device,
"update",
AsyncMock(return_value=MOCK_BLOCKS),
)
await init_integration(hass, 1)
# Move time to force polling
for _ in range(MAX_PUSH_UPDATE_FAILURES + 1):
async_fire_time_changed(
hass, dt_util.utcnow() + timedelta(seconds=UPDATE_PERIOD_MULTIPLIER * 15)
)
await hass.async_block_till_done()
assert issue_registry.async_get_issue(
domain=DOMAIN, issue_id=f"push_update_{MOCK_MAC}"
)
async def test_block_button_click_event( async def test_block_button_click_event(
hass: HomeAssistant, mock_block_device, events, monkeypatch hass: HomeAssistant, mock_block_device, events, monkeypatch
) -> None: ) -> None: