diff --git a/homeassistant/components/mobile_app/const.py b/homeassistant/components/mobile_app/const.py index a9cdc676932..0fc4a5ee407 100644 --- a/homeassistant/components/mobile_app/const.py +++ b/homeassistant/components/mobile_app/const.py @@ -57,6 +57,7 @@ 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" ATTR_SENSOR_ATTRIBUTES = "attributes" diff --git a/homeassistant/components/mobile_app/webhook.py b/homeassistant/components/mobile_app/webhook.py index adc90c15e98..09618390e18 100644 --- a/homeassistant/components/mobile_app/webhook.py +++ b/homeassistant/components/mobile_app/webhook.py @@ -76,6 +76,7 @@ from .const import ( ERR_ENCRYPTION_ALREADY_ENABLED, ERR_ENCRYPTION_NOT_AVAILABLE, ERR_ENCRYPTION_REQUIRED, + ERR_INVALID_FORMAT, ERR_SENSOR_DUPLICATE_UNIQUE_ID, ERR_SENSOR_NOT_REGISTERED, SIGNAL_LOCATION_UPDATE, @@ -394,20 +395,31 @@ async def webhook_register_sensor(hass, config_entry, data): vol.All( cv.ensure_list, [ + # Partial schema, enough to identify schema. + # We don't validate everything because otherwise 1 invalid sensor + # will invalidate all sensors. vol.Schema( { - vol.Optional(ATTR_SENSOR_ATTRIBUTES, default={}): dict, - vol.Optional(ATTR_SENSOR_ICON, default="mdi:cellphone"): cv.icon, - vol.Required(ATTR_SENSOR_STATE): vol.Any(bool, str, int, float), vol.Required(ATTR_SENSOR_TYPE): vol.In(SENSOR_TYPES), vol.Required(ATTR_SENSOR_UNIQUE_ID): cv.string, - } + }, + extra=vol.ALLOW_EXTRA, ) ], ) ) async def webhook_update_sensor_states(hass, config_entry, data): """Handle an update sensor states webhook.""" + sensor_schema_full = vol.Schema( + { + vol.Optional(ATTR_SENSOR_ATTRIBUTES, default={}): dict, + vol.Optional(ATTR_SENSOR_ICON, default="mdi:cellphone"): cv.icon, + vol.Required(ATTR_SENSOR_STATE): vol.Any(bool, str, int, float), + vol.Required(ATTR_SENSOR_TYPE): vol.In(SENSOR_TYPES), + vol.Required(ATTR_SENSOR_UNIQUE_ID): cv.string, + } + ) + resp = {} for sensor in data: entity_type = sensor[ATTR_SENSOR_TYPE] @@ -429,6 +441,19 @@ async def webhook_update_sensor_states(hass, config_entry, data): entry = hass.data[DOMAIN][entity_type][unique_store_key] + try: + sensor = sensor_schema_full(sensor) + except vol.Invalid as err: + err_msg = vol.humanize.humanize_error(sensor, err) + _LOGGER.error( + "Received invalid sensor payload for %s: %s", unique_id, err_msg + ) + resp[unique_id] = { + "success": False, + "error": {"code": ERR_INVALID_FORMAT, "message": err_msg}, + } + continue + new_state = {**entry, **sensor} hass.data[DOMAIN][entity_type][unique_store_key] = new_state diff --git a/homeassistant/components/myq/manifest.json b/homeassistant/components/myq/manifest.json index 953f7a31097..8a68eae03ea 100644 --- a/homeassistant/components/myq/manifest.json +++ b/homeassistant/components/myq/manifest.json @@ -2,7 +2,7 @@ "domain": "myq", "name": "MyQ", "documentation": "https://www.home-assistant.io/integrations/myq", - "requirements": ["pymyq==2.0.2"], + "requirements": ["pymyq==2.0.3"], "codeowners": ["@bdraco"], "config_flow": true, "homekit": { diff --git a/homeassistant/components/zeroconf/__init__.py b/homeassistant/components/zeroconf/__init__.py index c2fcb88c305..2c6ae4f4225 100644 --- a/homeassistant/components/zeroconf/__init__.py +++ b/homeassistant/components/zeroconf/__init__.py @@ -120,10 +120,10 @@ def setup(hass, config): params = { "version": __version__, - "external_url": None, - "internal_url": None, + "external_url": "", + "internal_url": "", # Old base URL, for backward compatibility - "base_url": None, + "base_url": "", # Always needs authentication "requires_api_password": True, } diff --git a/homeassistant/const.py b/homeassistant/const.py index 1c49e3447e6..eb2bdb91252 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 110 -PATCH_VERSION = "4" +PATCH_VERSION = "5" __short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__ = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER = (3, 7, 0) diff --git a/requirements_all.txt b/requirements_all.txt index 01d99251e22..15d8f257edd 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1468,7 +1468,7 @@ pymsteams==0.1.12 pymusiccast==0.1.6 # homeassistant.components.myq -pymyq==2.0.2 +pymyq==2.0.3 # homeassistant.components.mysensors pymysensors==0.18.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 79af61a03e2..2ed01366cde 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -627,7 +627,7 @@ pymodbus==2.3.0 pymonoprice==0.3 # homeassistant.components.myq -pymyq==2.0.2 +pymyq==2.0.3 # homeassistant.components.nut pynut2==2.1.2 diff --git a/tests/components/mobile_app/test_entity.py b/tests/components/mobile_app/test_entity.py index 78259cd1145..16b21e7b264 100644 --- a/tests/components/mobile_app/test_entity.py +++ b/tests/components/mobile_app/test_entity.py @@ -57,13 +57,18 @@ async def test_sensor(hass, create_registrations, webhook_client): "state": 123, "type": "sensor", "unique_id": "battery_state", - } + }, + # This invalid data should not invalidate whole request + {"type": "sensor", "unique_id": "invalid_state", "invalid": "data"}, ], }, ) assert update_resp.status == 200 + json = await update_resp.json() + assert json["invalid_state"]["success"] is False + updated_entity = hass.states.get("sensor.test_1_battery_state") assert updated_entity.state == "123"