diff --git a/homeassistant/components/mobile_app/binary_sensor.py b/homeassistant/components/mobile_app/binary_sensor.py index 4d40e42a47e..fd8545b1f98 100644 --- a/homeassistant/components/mobile_app/binary_sensor.py +++ b/homeassistant/components/mobile_app/binary_sensor.py @@ -3,14 +3,13 @@ from typing import Any from homeassistant.components.binary_sensor import BinarySensorEntity from homeassistant.config_entries import ConfigEntry -from homeassistant.const import CONF_NAME, CONF_UNIQUE_ID, CONF_WEBHOOK_ID, STATE_ON +from homeassistant.const import CONF_WEBHOOK_ID, STATE_ON from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import entity_registry as er from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import ( - ATTR_DEVICE_NAME, ATTR_SENSOR_ATTRIBUTES, ATTR_SENSOR_DEVICE_CLASS, ATTR_SENSOR_ENTITY_CATEGORY, @@ -22,7 +21,7 @@ from .const import ( ATTR_SENSOR_UNIQUE_ID, DOMAIN, ) -from .entity import MobileAppEntity, unique_id +from .entity import MobileAppEntity async def async_setup_entry( @@ -59,13 +58,6 @@ async def async_setup_entry( if data[CONF_WEBHOOK_ID] != webhook_id: return - data[CONF_UNIQUE_ID] = unique_id( - data[CONF_WEBHOOK_ID], data[ATTR_SENSOR_UNIQUE_ID] - ) - data[ - CONF_NAME - ] = f"{config_entry.data[ATTR_DEVICE_NAME]} {data[ATTR_SENSOR_NAME]}" - async_add_entities([MobileAppBinarySensor(data, config_entry)]) async_dispatcher_connect( diff --git a/homeassistant/components/mobile_app/const.py b/homeassistant/components/mobile_app/const.py index ba81a0484cf..e6a26430f11 100644 --- a/homeassistant/components/mobile_app/const.py +++ b/homeassistant/components/mobile_app/const.py @@ -67,6 +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_ENTITY_CATEGORY = "entity_category" ATTR_SENSOR_ICON = "icon" ATTR_SENSOR_NAME = "name" diff --git a/homeassistant/components/mobile_app/entity.py b/homeassistant/components/mobile_app/entity.py index 0cb6cfc6fcc..b38774d56d6 100644 --- a/homeassistant/components/mobile_app/entity.py +++ b/homeassistant/components/mobile_app/entity.py @@ -2,46 +2,35 @@ from __future__ import annotations from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ( - ATTR_ICON, - CONF_NAME, - CONF_UNIQUE_ID, - CONF_WEBHOOK_ID, - STATE_UNAVAILABLE, -) +from homeassistant.const import ATTR_ICON, CONF_NAME, CONF_UNIQUE_ID, STATE_UNAVAILABLE from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.restore_state import RestoreEntity from .const import ( ATTR_SENSOR_ATTRIBUTES, + ATTR_SENSOR_DEFAULT_DISABLED, ATTR_SENSOR_DEVICE_CLASS, ATTR_SENSOR_ENTITY_CATEGORY, ATTR_SENSOR_ICON, ATTR_SENSOR_STATE, - ATTR_SENSOR_TYPE, - ATTR_SENSOR_UNIQUE_ID, SIGNAL_SENSOR_UPDATE, ) from .helpers import device_info -def unique_id(webhook_id, sensor_unique_id): - """Return a unique sensor ID.""" - return f"{webhook_id}_{sensor_unique_id}" - - class MobileAppEntity(RestoreEntity): """Representation of an mobile app entity.""" + _attr_should_poll = False + def __init__(self, config: dict, entry: ConfigEntry) -> None: """Initialize the entity.""" self._config = config self._entry = entry self._registration = entry.data - self._unique_id = config[CONF_UNIQUE_ID] - self._entity_type = config[ATTR_SENSOR_TYPE] - self._name = config[CONF_NAME] + self._attr_unique_id = config[CONF_UNIQUE_ID] + self._name = self._config[CONF_NAME] async def async_added_to_hass(self): """Register callbacks.""" @@ -67,16 +56,16 @@ class MobileAppEntity(RestoreEntity): if ATTR_ICON in last_state.attributes: self._config[ATTR_SENSOR_ICON] = last_state.attributes[ATTR_ICON] - @property - def should_poll(self) -> bool: - """Declare that this entity pushes its state to HA.""" - return False - @property def name(self): """Return the name of the mobile app sensor.""" return self._name + @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) + @property def device_class(self): """Return the device class.""" @@ -97,11 +86,6 @@ class MobileAppEntity(RestoreEntity): """Return the entity category, if any.""" return self._config.get(ATTR_SENSOR_ENTITY_CATEGORY) - @property - def unique_id(self): - """Return the unique ID of this sensor.""" - return self._unique_id - @property def device_info(self): """Return device registry information for this entity.""" @@ -113,10 +97,9 @@ class MobileAppEntity(RestoreEntity): return self._config.get(ATTR_SENSOR_STATE) != STATE_UNAVAILABLE @callback - def _handle_update(self, data): + def _handle_update(self, incoming_id, data): """Handle async event updates.""" - incoming_id = unique_id(data[CONF_WEBHOOK_ID], data[ATTR_SENSOR_UNIQUE_ID]) - if incoming_id != self._unique_id: + if incoming_id != self._attr_unique_id: return self._config = {**self._config, **data} diff --git a/homeassistant/components/mobile_app/sensor.py b/homeassistant/components/mobile_app/sensor.py index 45bc4acd6a2..d7cfc9545f6 100644 --- a/homeassistant/components/mobile_app/sensor.py +++ b/homeassistant/components/mobile_app/sensor.py @@ -5,12 +5,7 @@ from typing import Any from homeassistant.components.sensor import SensorDeviceClass, SensorEntity from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ( - CONF_NAME, - CONF_UNIQUE_ID, - CONF_WEBHOOK_ID, - STATE_UNKNOWN, -) +from homeassistant.const import CONF_WEBHOOK_ID, STATE_UNKNOWN from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import entity_registry as er from homeassistant.helpers.dispatcher import async_dispatcher_connect @@ -18,7 +13,6 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.util import dt as dt_util from .const import ( - ATTR_DEVICE_NAME, ATTR_SENSOR_ATTRIBUTES, ATTR_SENSOR_DEVICE_CLASS, ATTR_SENSOR_ENTITY_CATEGORY, @@ -32,7 +26,7 @@ from .const import ( ATTR_SENSOR_UOM, DOMAIN, ) -from .entity import MobileAppEntity, unique_id +from .entity import MobileAppEntity async def async_setup_entry( @@ -70,13 +64,6 @@ async def async_setup_entry( if data[CONF_WEBHOOK_ID] != webhook_id: return - data[CONF_UNIQUE_ID] = unique_id( - data[CONF_WEBHOOK_ID], data[ATTR_SENSOR_UNIQUE_ID] - ) - data[ - CONF_NAME - ] = f"{config_entry.data[ATTR_DEVICE_NAME]} {data[ATTR_SENSOR_NAME]}" - async_add_entities([MobileAppSensor(data, config_entry)]) async_dispatcher_connect( diff --git a/homeassistant/components/mobile_app/webhook.py b/homeassistant/components/mobile_app/webhook.py index f0c7c628976..bc67076b005 100644 --- a/homeassistant/components/mobile_app/webhook.py +++ b/homeassistant/components/mobile_app/webhook.py @@ -34,6 +34,8 @@ from homeassistant.const import ( ATTR_SERVICE, ATTR_SERVICE_DATA, ATTR_SUPPORTED_FEATURES, + CONF_NAME, + CONF_UNIQUE_ID, CONF_WEBHOOK_ID, ) from homeassistant.core import EventOrigin, HomeAssistant @@ -62,6 +64,7 @@ from .const import ( ATTR_NO_LEGACY_ENCRYPTION, ATTR_OS_VERSION, ATTR_SENSOR_ATTRIBUTES, + ATTR_SENSOR_DEFAULT_DISABLED, ATTR_SENSOR_DEVICE_CLASS, ATTR_SENSOR_ENTITY_CATEGORY, ATTR_SENSOR_ICON, @@ -431,6 +434,11 @@ def _validate_state_class_sensor(value: dict): return value +def _gen_unique_id(webhook_id, sensor_unique_id): + """Return a unique sensor ID.""" + return f"{webhook_id}_{sensor_unique_id}" + + @WEBHOOK_COMMANDS.register("register_sensor") @validate_schema( vol.All( @@ -449,6 +457,7 @@ def _validate_state_class_sensor(value: dict): 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, }, _validate_state_class_sensor, ) @@ -459,7 +468,7 @@ async def webhook_register_sensor(hass, config_entry, data): unique_id = data[ATTR_SENSOR_UNIQUE_ID] device_name = config_entry.data[ATTR_DEVICE_NAME] - unique_store_key = f"{config_entry.data[CONF_WEBHOOK_ID]}_{unique_id}" + unique_store_key = _gen_unique_id(config_entry.data[CONF_WEBHOOK_ID], unique_id) entity_registry = er.async_get(hass) existing_sensor = entity_registry.async_get_entity_id( entity_type, DOMAIN, unique_store_key @@ -493,8 +502,13 @@ async def webhook_register_sensor(hass, config_entry, data): if changes: entity_registry.async_update_entity(existing_sensor, **changes) - async_dispatcher_send(hass, SIGNAL_SENSOR_UPDATE, data) + async_dispatcher_send(hass, SIGNAL_SENSOR_UPDATE, unique_store_key, data) else: + data[CONF_UNIQUE_ID] = unique_store_key + data[ + CONF_NAME + ] = f"{config_entry.data[ATTR_DEVICE_NAME]} {data[ATTR_SENSOR_NAME]}" + register_signal = f"{DOMAIN}_{data[ATTR_SENSOR_TYPE]}_register" async_dispatcher_send(hass, register_signal, data) @@ -543,7 +557,7 @@ async def webhook_update_sensor_states(hass, config_entry, data): unique_id = sensor[ATTR_SENSOR_UNIQUE_ID] - unique_store_key = f"{config_entry.data[CONF_WEBHOOK_ID]}_{unique_id}" + 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( @@ -578,7 +592,12 @@ async def webhook_update_sensor_states(hass, config_entry, data): continue sensor[CONF_WEBHOOK_ID] = config_entry.data[CONF_WEBHOOK_ID] - async_dispatcher_send(hass, SIGNAL_SENSOR_UPDATE, sensor) + async_dispatcher_send( + hass, + SIGNAL_SENSOR_UPDATE, + unique_store_key, + sensor, + ) resp[unique_id] = {"success": True} diff --git a/tests/components/mobile_app/test_sensor.py b/tests/components/mobile_app/test_sensor.py index 7eb99df8d8f..301c49381f7 100644 --- a/tests/components/mobile_app/test_sensor.py +++ b/tests/components/mobile_app/test_sensor.py @@ -340,3 +340,36 @@ async def test_sensor_datetime( assert entity.attributes["device_class"] == device_class assert entity.domain == "sensor" assert entity.state == state_value + + +async def test_default_disabling_entity(hass, create_registrations, webhook_client): + """Test that sensors can be disabled by default upon registration.""" + 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", + "type": "sensor", + "unique_id": "battery_state", + "default_disabled": True, + }, + }, + ) + + assert reg_resp.status == HTTPStatus.CREATED + + json = await reg_resp.json() + assert json == {"success": True} + await hass.async_block_till_done() + + entity = hass.states.get("sensor.test_1_battery_state") + assert entity is None + + assert ( + er.async_get(hass).async_get("sensor.test_1_battery_state").disabled_by + == er.RegistryEntryDisabler.INTEGRATION + )