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
parent
c433b251fa
commit
db83dc9acc
|
@ -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):
|
||||||
|
|
|
@ -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}"
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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:
|
||||||
|
|
Loading…
Reference in New Issue