Mobile app to notify when sensor is disabled (#71561)
* Mobile app to notify when sensor is disabled * Add entity status to get_config * Allow overriding enabled/disabled
This commit is contained in:
parent
e4573273dc
commit
0b09376360
5 changed files with 162 additions and 13 deletions
|
@ -67,7 +67,7 @@ ERR_INVALID_FORMAT = "invalid_format"
|
|||
|
||||
ATTR_SENSOR_ATTRIBUTES = "attributes"
|
||||
ATTR_SENSOR_DEVICE_CLASS = "device_class"
|
||||
ATTR_SENSOR_DEFAULT_DISABLED = "default_disabled"
|
||||
ATTR_SENSOR_DISABLED = "disabled"
|
||||
ATTR_SENSOR_ENTITY_CATEGORY = "entity_category"
|
||||
ATTR_SENSOR_ICON = "icon"
|
||||
ATTR_SENSOR_NAME = "name"
|
||||
|
|
|
@ -9,8 +9,8 @@ from homeassistant.helpers.restore_state import RestoreEntity
|
|||
|
||||
from .const import (
|
||||
ATTR_SENSOR_ATTRIBUTES,
|
||||
ATTR_SENSOR_DEFAULT_DISABLED,
|
||||
ATTR_SENSOR_DEVICE_CLASS,
|
||||
ATTR_SENSOR_DISABLED,
|
||||
ATTR_SENSOR_ENTITY_CATEGORY,
|
||||
ATTR_SENSOR_ICON,
|
||||
ATTR_SENSOR_STATE,
|
||||
|
@ -64,7 +64,7 @@ class MobileAppEntity(RestoreEntity):
|
|||
@property
|
||||
def entity_registry_enabled_default(self) -> bool:
|
||||
"""Return if entity should be enabled by default."""
|
||||
return not self._config.get(ATTR_SENSOR_DEFAULT_DISABLED)
|
||||
return not self._config.get(ATTR_SENSOR_DISABLED)
|
||||
|
||||
@property
|
||||
def device_class(self):
|
||||
|
|
|
@ -64,8 +64,8 @@ from .const import (
|
|||
ATTR_NO_LEGACY_ENCRYPTION,
|
||||
ATTR_OS_VERSION,
|
||||
ATTR_SENSOR_ATTRIBUTES,
|
||||
ATTR_SENSOR_DEFAULT_DISABLED,
|
||||
ATTR_SENSOR_DEVICE_CLASS,
|
||||
ATTR_SENSOR_DISABLED,
|
||||
ATTR_SENSOR_ENTITY_CATEGORY,
|
||||
ATTR_SENSOR_ICON,
|
||||
ATTR_SENSOR_NAME,
|
||||
|
@ -439,6 +439,11 @@ def _gen_unique_id(webhook_id, sensor_unique_id):
|
|||
return f"{webhook_id}_{sensor_unique_id}"
|
||||
|
||||
|
||||
def _extract_sensor_unique_id(webhook_id, unique_id):
|
||||
"""Return a unique sensor ID."""
|
||||
return unique_id[len(webhook_id) + 1 :]
|
||||
|
||||
|
||||
@WEBHOOK_COMMANDS.register("register_sensor")
|
||||
@validate_schema(
|
||||
vol.All(
|
||||
|
@ -457,7 +462,7 @@ def _gen_unique_id(webhook_id, sensor_unique_id):
|
|||
vol.Optional(ATTR_SENSOR_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA,
|
||||
vol.Optional(ATTR_SENSOR_ICON, default="mdi:cellphone"): cv.icon,
|
||||
vol.Optional(ATTR_SENSOR_STATE_CLASS): vol.In(SENSOSR_STATE_CLASSES),
|
||||
vol.Optional(ATTR_SENSOR_DEFAULT_DISABLED): bool,
|
||||
vol.Optional(ATTR_SENSOR_DISABLED): bool,
|
||||
},
|
||||
_validate_state_class_sensor,
|
||||
)
|
||||
|
@ -490,6 +495,15 @@ async def webhook_register_sensor(hass, config_entry, data):
|
|||
) != entry.original_name:
|
||||
changes["original_name"] = new_name
|
||||
|
||||
if (
|
||||
should_be_disabled := data.get(ATTR_SENSOR_DISABLED)
|
||||
) is None or should_be_disabled == entry.disabled:
|
||||
pass
|
||||
elif should_be_disabled:
|
||||
changes["disabled_by"] = er.RegistryEntryDisabler.INTEGRATION
|
||||
else:
|
||||
changes["disabled_by"] = None
|
||||
|
||||
for ent_reg_key, data_key in (
|
||||
("device_class", ATTR_SENSOR_DEVICE_CLASS),
|
||||
("unit_of_measurement", ATTR_SENSOR_UOM),
|
||||
|
@ -551,6 +565,7 @@ async def webhook_update_sensor_states(hass, config_entry, data):
|
|||
|
||||
device_name = config_entry.data[ATTR_DEVICE_NAME]
|
||||
resp = {}
|
||||
entity_registry = er.async_get(hass)
|
||||
|
||||
for sensor in data:
|
||||
entity_type = sensor[ATTR_SENSOR_TYPE]
|
||||
|
@ -559,9 +574,10 @@ async def webhook_update_sensor_states(hass, config_entry, data):
|
|||
|
||||
unique_store_key = _gen_unique_id(config_entry.data[CONF_WEBHOOK_ID], unique_id)
|
||||
|
||||
entity_registry = er.async_get(hass)
|
||||
if not entity_registry.async_get_entity_id(
|
||||
entity_type, DOMAIN, unique_store_key
|
||||
if not (
|
||||
entity_id := entity_registry.async_get_entity_id(
|
||||
entity_type, DOMAIN, unique_store_key
|
||||
)
|
||||
):
|
||||
_LOGGER.debug(
|
||||
"Refusing to update %s non-registered sensor: %s",
|
||||
|
@ -601,6 +617,12 @@ async def webhook_update_sensor_states(hass, config_entry, data):
|
|||
|
||||
resp[unique_id] = {"success": True}
|
||||
|
||||
# Check if disabled
|
||||
entry = entity_registry.async_get(entity_id)
|
||||
|
||||
if entry.disabled_by:
|
||||
resp[unique_id]["is_disabled"] = True
|
||||
|
||||
return webhook_response(resp, registration=config_entry.data)
|
||||
|
||||
|
||||
|
@ -637,6 +659,21 @@ async def webhook_get_config(hass, config_entry, data):
|
|||
with suppress(hass.components.cloud.CloudNotAvailable):
|
||||
resp[CONF_REMOTE_UI_URL] = cloud.async_remote_ui_url(hass)
|
||||
|
||||
webhook_id = config_entry.data[CONF_WEBHOOK_ID]
|
||||
|
||||
entities = {}
|
||||
for entry in er.async_entries_for_config_entry(
|
||||
er.async_get(hass), config_entry.entry_id
|
||||
):
|
||||
if entry.domain in ("binary_sensor", "sensor"):
|
||||
unique_id = _extract_sensor_unique_id(webhook_id, entry.unique_id)
|
||||
else:
|
||||
unique_id = entry.unique_id
|
||||
|
||||
entities[unique_id] = {"disabled": entry.disabled}
|
||||
|
||||
resp["entities"] = entities
|
||||
|
||||
return webhook_response(resp, registration=config_entry.data)
|
||||
|
||||
|
||||
|
|
|
@ -355,7 +355,7 @@ async def test_default_disabling_entity(hass, create_registrations, webhook_clie
|
|||
"name": "Battery State",
|
||||
"type": "sensor",
|
||||
"unique_id": "battery_state",
|
||||
"default_disabled": True,
|
||||
"disabled": True,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
@ -373,3 +373,70 @@ async def test_default_disabling_entity(hass, create_registrations, webhook_clie
|
|||
er.async_get(hass).async_get("sensor.test_1_battery_state").disabled_by
|
||||
== er.RegistryEntryDisabler.INTEGRATION
|
||||
)
|
||||
|
||||
|
||||
async def test_updating_disabled_sensor(hass, create_registrations, webhook_client):
|
||||
"""Test that sensors return error if disabled in instance."""
|
||||
webhook_id = create_registrations[1]["webhook_id"]
|
||||
webhook_url = f"/api/webhook/{webhook_id}"
|
||||
|
||||
reg_resp = await webhook_client.post(
|
||||
webhook_url,
|
||||
json={
|
||||
"type": "register_sensor",
|
||||
"data": {
|
||||
"name": "Battery State",
|
||||
"state": None,
|
||||
"type": "sensor",
|
||||
"unique_id": "battery_state",
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
assert reg_resp.status == HTTPStatus.CREATED
|
||||
|
||||
update_resp = await webhook_client.post(
|
||||
webhook_url,
|
||||
json={
|
||||
"type": "update_sensor_states",
|
||||
"data": [
|
||||
{
|
||||
"icon": "mdi:battery-unknown",
|
||||
"state": 123,
|
||||
"type": "sensor",
|
||||
"unique_id": "battery_state",
|
||||
},
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
assert update_resp.status == HTTPStatus.OK
|
||||
|
||||
json = await update_resp.json()
|
||||
assert json["battery_state"]["success"] is True
|
||||
assert "is_disabled" not in json["battery_state"]
|
||||
|
||||
er.async_get(hass).async_update_entity(
|
||||
"sensor.test_1_battery_state", disabled_by=er.RegistryEntryDisabler.USER
|
||||
)
|
||||
|
||||
update_resp = await webhook_client.post(
|
||||
webhook_url,
|
||||
json={
|
||||
"type": "update_sensor_states",
|
||||
"data": [
|
||||
{
|
||||
"icon": "mdi:battery-unknown",
|
||||
"state": 123,
|
||||
"type": "sensor",
|
||||
"unique_id": "battery_state",
|
||||
},
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
assert update_resp.status == HTTPStatus.OK
|
||||
|
||||
json = await update_resp.json()
|
||||
assert json["battery_state"]["success"] is True
|
||||
assert json["battery_state"]["is_disabled"] is True
|
||||
|
|
|
@ -254,10 +254,30 @@ async def test_webhook_handle_get_zones(hass, create_registrations, webhook_clie
|
|||
|
||||
async def test_webhook_handle_get_config(hass, create_registrations, webhook_client):
|
||||
"""Test that we can get config properly."""
|
||||
resp = await webhook_client.post(
|
||||
"/api/webhook/{}".format(create_registrations[1]["webhook_id"]),
|
||||
json={"type": "get_config"},
|
||||
)
|
||||
webhook_id = create_registrations[1]["webhook_id"]
|
||||
webhook_url = f"/api/webhook/{webhook_id}"
|
||||
|
||||
# Create two entities
|
||||
for sensor in (
|
||||
{
|
||||
"name": "Battery State",
|
||||
"type": "sensor",
|
||||
"unique_id": "battery-state-id",
|
||||
},
|
||||
{
|
||||
"name": "Battery Charging",
|
||||
"type": "sensor",
|
||||
"unique_id": "battery-charging-id",
|
||||
"disabled": True,
|
||||
},
|
||||
):
|
||||
reg_resp = await webhook_client.post(
|
||||
webhook_url,
|
||||
json={"type": "register_sensor", "data": sensor},
|
||||
)
|
||||
assert reg_resp.status == HTTPStatus.CREATED
|
||||
|
||||
resp = await webhook_client.post(webhook_url, json={"type": "get_config"})
|
||||
|
||||
assert resp.status == HTTPStatus.OK
|
||||
|
||||
|
@ -279,6 +299,11 @@ async def test_webhook_handle_get_config(hass, create_registrations, webhook_cli
|
|||
"components": hass_config["components"],
|
||||
"version": hass_config["version"],
|
||||
"theme_color": "#03A9F4", # Default frontend theme color
|
||||
"entities": {
|
||||
"mock-device-id": {"disabled": False},
|
||||
"battery-state-id": {"disabled": False},
|
||||
"battery-charging-id": {"disabled": True},
|
||||
},
|
||||
}
|
||||
|
||||
assert expected_dict == json
|
||||
|
@ -902,6 +927,7 @@ async def test_reregister_sensor(hass, create_registrations, webhook_client):
|
|||
assert entry.unit_of_measurement is None
|
||||
assert entry.entity_category is None
|
||||
assert entry.original_icon == "mdi:cellphone"
|
||||
assert entry.disabled_by is None
|
||||
|
||||
reg_resp = await webhook_client.post(
|
||||
webhook_url,
|
||||
|
@ -917,6 +943,7 @@ async def test_reregister_sensor(hass, create_registrations, webhook_client):
|
|||
"entity_category": "diagnostic",
|
||||
"icon": "mdi:new-icon",
|
||||
"unit_of_measurement": "%",
|
||||
"disabled": True,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
@ -928,3 +955,21 @@ async def test_reregister_sensor(hass, create_registrations, webhook_client):
|
|||
assert entry.unit_of_measurement == "%"
|
||||
assert entry.entity_category == "diagnostic"
|
||||
assert entry.original_icon == "mdi:new-icon"
|
||||
assert entry.disabled_by == er.RegistryEntryDisabler.INTEGRATION
|
||||
|
||||
reg_resp = await webhook_client.post(
|
||||
webhook_url,
|
||||
json={
|
||||
"type": "register_sensor",
|
||||
"data": {
|
||||
"name": "New Name",
|
||||
"type": "sensor",
|
||||
"unique_id": "abcd",
|
||||
"disabled": False,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
assert reg_resp.status == HTTPStatus.CREATED
|
||||
entry = ent_reg.async_get("sensor.test_1_battery_state")
|
||||
assert entry.disabled_by is None
|
||||
|
|
Loading…
Add table
Reference in a new issue