diff --git a/homeassistant/components/mobile_app/const.py b/homeassistant/components/mobile_app/const.py index 0fc4a5ee4070..6e83a08c508b 100644 --- a/homeassistant/components/mobile_app/const.py +++ b/homeassistant/components/mobile_app/const.py @@ -56,7 +56,6 @@ ERR_ENCRYPTION_ALREADY_ENABLED = "encryption_already_enabled" ERR_ENCRYPTION_NOT_AVAILABLE = "encryption_not_available" ERR_ENCRYPTION_REQUIRED = "encryption_required" ERR_SENSOR_NOT_REGISTERED = "not_registered" -ERR_SENSOR_DUPLICATE_UNIQUE_ID = "duplicate_unique_id" ERR_INVALID_FORMAT = "invalid_format" diff --git a/homeassistant/components/mobile_app/webhook.py b/homeassistant/components/mobile_app/webhook.py index c155e722976e..d0ab79ab7e24 100644 --- a/homeassistant/components/mobile_app/webhook.py +++ b/homeassistant/components/mobile_app/webhook.py @@ -78,7 +78,6 @@ from .const import ( ERR_ENCRYPTION_NOT_AVAILABLE, ERR_ENCRYPTION_REQUIRED, ERR_INVALID_FORMAT, - ERR_SENSOR_DUPLICATE_UNIQUE_ID, ERR_SENSOR_NOT_REGISTERED, SIGNAL_LOCATION_UPDATE, SIGNAL_SENSOR_UPDATE, @@ -364,29 +363,30 @@ async def webhook_enable_encryption(hass, config_entry, data): async def webhook_register_sensor(hass, config_entry, data): """Handle a register sensor webhook.""" entity_type = data[ATTR_SENSOR_TYPE] - unique_id = data[ATTR_SENSOR_UNIQUE_ID] unique_store_key = f"{config_entry.data[CONF_WEBHOOK_ID]}_{unique_id}" - - if unique_store_key in hass.data[DOMAIN][entity_type]: - _LOGGER.error("Refusing to re-register existing sensor %s!", unique_id) - return error_response( - ERR_SENSOR_DUPLICATE_UNIQUE_ID, - f"{entity_type} {unique_id} already exists!", - status=409, - ) + existing_sensor = unique_store_key in hass.data[DOMAIN][entity_type] data[CONF_WEBHOOK_ID] = config_entry.data[CONF_WEBHOOK_ID] + # If sensor already is registered, update current state instead + if existing_sensor: + _LOGGER.debug("Re-register existing sensor %s", unique_id) + entry = hass.data[DOMAIN][entity_type][unique_store_key] + data = {**entry, **data} + hass.data[DOMAIN][entity_type][unique_store_key] = data hass.data[DOMAIN][DATA_STORE].async_delay_save( lambda: savable_state(hass), DELAY_SAVE ) - register_signal = f"{DOMAIN}_{data[ATTR_SENSOR_TYPE]}_register" - async_dispatcher_send(hass, register_signal, data) + if existing_sensor: + async_dispatcher_send(hass, SIGNAL_SENSOR_UPDATE, data) + else: + register_signal = f"{DOMAIN}_{data[ATTR_SENSOR_TYPE]}_register" + async_dispatcher_send(hass, register_signal, data) return webhook_response( {"success": True}, registration=config_entry.data, status=HTTP_CREATED, diff --git a/tests/components/mobile_app/test_entity.py b/tests/components/mobile_app/test_entity.py index 9f91e659fb02..d0d2a4f841af 100644 --- a/tests/components/mobile_app/test_entity.py +++ b/tests/components/mobile_app/test_entity.py @@ -95,8 +95,8 @@ async def test_sensor_must_register(hass, create_registrations, webhook_client): assert json["battery_state"]["error"]["code"] == "not_registered" -async def test_sensor_id_no_dupes(hass, create_registrations, webhook_client): - """Test that sensors must have a unique ID.""" +async def test_sensor_id_no_dupes(hass, create_registrations, webhook_client, caplog): + """Test that a duplicate unique ID in registration updates the sensor.""" webhook_id = create_registrations[1]["webhook_id"] webhook_url = f"/api/webhook/{webhook_id}" @@ -120,14 +120,41 @@ async def test_sensor_id_no_dupes(hass, create_registrations, webhook_client): reg_json = await reg_resp.json() assert reg_json == {"success": True} + await hass.async_block_till_done() + assert "Re-register existing sensor" not in caplog.text + + entity = hass.states.get("sensor.test_1_battery_state") + assert entity is not None + + assert entity.attributes["device_class"] == "battery" + assert entity.attributes["icon"] == "mdi:battery" + assert entity.attributes["unit_of_measurement"] == UNIT_PERCENTAGE + assert entity.attributes["foo"] == "bar" + assert entity.domain == "sensor" + assert entity.name == "Test 1 Battery State" + assert entity.state == "100" + + payload["data"]["state"] = 99 dupe_resp = await webhook_client.post(webhook_url, json=payload) - assert dupe_resp.status == 409 + assert dupe_resp.status == 201 + dupe_reg_json = await dupe_resp.json() + assert dupe_reg_json == {"success": True} + await hass.async_block_till_done() - dupe_json = await dupe_resp.json() - assert dupe_json["success"] is False - assert dupe_json["error"]["code"] == "duplicate_unique_id" + assert "Re-register existing sensor" in caplog.text + + entity = hass.states.get("sensor.test_1_battery_state") + assert entity is not None + + assert entity.attributes["device_class"] == "battery" + assert entity.attributes["icon"] == "mdi:battery" + assert entity.attributes["unit_of_measurement"] == UNIT_PERCENTAGE + assert entity.attributes["foo"] == "bar" + assert entity.domain == "sensor" + assert entity.name == "Test 1 Battery State" + assert entity.state == "99" async def test_register_sensor_no_state(hass, create_registrations, webhook_client):