From f1ee920463d044ae45f5a1b547497e6f678d8a29 Mon Sep 17 00:00:00 2001 From: Joe Rogers <1337joe@users.noreply.github.com> Date: Sun, 4 Dec 2022 15:52:48 -0500 Subject: [PATCH] Handle numeric versions in mqtt update (#83218) * Handle numeric versions in mqtt update * Remove need for type:ignore Co-authored-by: Jan Bouwhuis Co-authored-by: Jan Bouwhuis --- homeassistant/components/mqtt/update.py | 20 ++++-- tests/components/mqtt/test_update.py | 94 +++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/mqtt/update.py b/homeassistant/components/mqtt/update.py index 0875778059f..874f6024a37 100644 --- a/homeassistant/components/mqtt/update.py +++ b/homeassistant/components/mqtt/update.py @@ -172,14 +172,22 @@ class MqttUpdate(MqttEntity, UpdateEntity, RestoreEntity): ) return - json_payload = {} + json_payload: Any | dict = {} try: json_payload = json_loads(payload) - _LOGGER.debug( - "JSON payload detected after processing payload '%s' on topic %s", - json_payload, - msg.topic, - ) + if isinstance(json_payload, dict): + _LOGGER.debug( + "JSON payload detected after processing payload '%s' on topic %s", + json_payload, + msg.topic, + ) + else: + _LOGGER.debug( + "Non-dictionary JSON payload detected after processing payload '%s' on topic %s", + payload, + msg.topic, + ) + json_payload = {"installed_version": payload} except JSON_DECODE_EXCEPTIONS: _LOGGER.debug( "No valid (JSON) payload detected after processing payload '%s' on topic %s", diff --git a/tests/components/mqtt/test_update.py b/tests/components/mqtt/test_update.py index 5746f2ac471..a633b99fb1a 100644 --- a/tests/components/mqtt/test_update.py +++ b/tests/components/mqtt/test_update.py @@ -110,6 +110,54 @@ async def test_run_update_setup(hass, mqtt_mock_entry_with_yaml_config): assert state.attributes.get("latest_version") == "2.0.0" +async def test_run_update_setup_float(hass, mqtt_mock_entry_with_yaml_config): + """Test that it fetches the given payload when the version is parsable as a number.""" + installed_version_topic = "test/installed-version" + latest_version_topic = "test/latest-version" + await async_setup_component( + hass, + mqtt.DOMAIN, + { + mqtt.DOMAIN: { + update.DOMAIN: { + "state_topic": installed_version_topic, + "latest_version_topic": latest_version_topic, + "name": "Test Update", + "release_summary": "Test release summary", + "release_url": "https://example.com/release", + "title": "Test Update Title", + "entity_picture": "https://example.com/icon.png", + } + } + }, + ) + await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() + + async_fire_mqtt_message(hass, installed_version_topic, "1.9") + async_fire_mqtt_message(hass, latest_version_topic, "1.9") + + await hass.async_block_till_done() + + state = hass.states.get("update.test_update") + assert state.state == STATE_OFF + assert state.attributes.get("installed_version") == "1.9" + assert state.attributes.get("latest_version") == "1.9" + assert state.attributes.get("release_summary") == "Test release summary" + assert state.attributes.get("release_url") == "https://example.com/release" + assert state.attributes.get("title") == "Test Update Title" + assert state.attributes.get("entity_picture") == "https://example.com/icon.png" + + async_fire_mqtt_message(hass, latest_version_topic, "2.0") + + await hass.async_block_till_done() + + state = hass.states.get("update.test_update") + assert state.state == STATE_ON + assert state.attributes.get("installed_version") == "1.9" + assert state.attributes.get("latest_version") == "2.0" + + async def test_value_template(hass, mqtt_mock_entry_with_yaml_config): """Test that it fetches the given payload with a template.""" installed_version_topic = "test/installed-version" @@ -156,6 +204,52 @@ async def test_value_template(hass, mqtt_mock_entry_with_yaml_config): assert state.attributes.get("latest_version") == "2.0.0" +async def test_value_template_float(hass, mqtt_mock_entry_with_yaml_config): + """Test that it fetches the given payload with a template when the version is parsable as a number.""" + installed_version_topic = "test/installed-version" + latest_version_topic = "test/latest-version" + await async_setup_component( + hass, + mqtt.DOMAIN, + { + mqtt.DOMAIN: { + update.DOMAIN: { + "state_topic": installed_version_topic, + "value_template": "{{ value_json.installed }}", + "latest_version_topic": latest_version_topic, + "latest_version_template": "{{ value_json.latest }}", + "name": "Test Update", + } + } + }, + ) + await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() + + async_fire_mqtt_message(hass, installed_version_topic, '{"installed":"1.9"}') + async_fire_mqtt_message(hass, latest_version_topic, '{"latest":"1.9"}') + + await hass.async_block_till_done() + + state = hass.states.get("update.test_update") + assert state.state == STATE_OFF + assert state.attributes.get("installed_version") == "1.9" + assert state.attributes.get("latest_version") == "1.9" + assert ( + state.attributes.get("entity_picture") + == "https://brands.home-assistant.io/_/mqtt/icon.png" + ) + + async_fire_mqtt_message(hass, latest_version_topic, '{"latest":"2.0"}') + + await hass.async_block_till_done() + + state = hass.states.get("update.test_update") + assert state.state == STATE_ON + assert state.attributes.get("installed_version") == "1.9" + assert state.attributes.get("latest_version") == "2.0" + + async def test_empty_json_state_message(hass, mqtt_mock_entry_with_yaml_config): """Test an empty JSON payload.""" state_topic = "test/state-topic"