diff --git a/homeassistant/components/mqtt/abbreviations.py b/homeassistant/components/mqtt/abbreviations.py index ddbced5286da..32b67874a45f 100644 --- a/homeassistant/components/mqtt/abbreviations.py +++ b/homeassistant/components/mqtt/abbreviations.py @@ -80,6 +80,7 @@ ABBREVIATIONS = { "hs_stat_t": "hs_state_topic", "hs_val_tpl": "hs_value_template", "ic": "icon", + "img_e": "image_encoding", "init": "initial", "hum_cmd_t": "target_humidity_command_topic", "hum_cmd_tpl": "target_humidity_command_template", diff --git a/homeassistant/components/mqtt/camera.py b/homeassistant/components/mqtt/camera.py index f213bec9bb67..61c87e868885 100644 --- a/homeassistant/components/mqtt/camera.py +++ b/homeassistant/components/mqtt/camera.py @@ -3,6 +3,7 @@ from __future__ import annotations from base64 import b64decode import functools +import logging import voluptuous as vol @@ -17,7 +18,7 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from . import subscription from .config import MQTT_BASE_SCHEMA -from .const import CONF_ENCODING, CONF_QOS, CONF_TOPIC +from .const import CONF_ENCODING, CONF_QOS, CONF_TOPIC, DEFAULT_ENCODING from .debug_info import log_messages from .mixins import ( MQTT_ENTITY_COMMON_SCHEMA, @@ -29,6 +30,10 @@ from .mixins import ( ) from .util import valid_subscribe_topic +_LOGGER = logging.getLogger(__name__) + +CONF_IMAGE_ENCODING = "image_encoding" + DEFAULT_NAME = "MQTT Camera" MQTT_CAMERA_ATTRIBUTES_BLOCKED = frozenset( @@ -40,20 +45,41 @@ MQTT_CAMERA_ATTRIBUTES_BLOCKED = frozenset( } ) -PLATFORM_SCHEMA_MODERN = MQTT_BASE_SCHEMA.extend( + +# Using CONF_ENCODING to set b64 encoding for images is deprecated as of Home Assistant 2022.9 +# use CONF_IMAGE_ENCODING instead, support for the work-a-round will be removed with Home Assistant 2022.11 +def repair_legacy_encoding(config: ConfigType) -> ConfigType: + """Check incorrect deprecated config of image encoding.""" + if config[CONF_ENCODING] == "b64": + config[CONF_IMAGE_ENCODING] = "b64" + config[CONF_ENCODING] = DEFAULT_ENCODING + _LOGGER.warning( + "Using the `encoding` parameter to set image encoding has been deprecated, use `image_encoding` instead" + ) + return config + + +PLATFORM_SCHEMA_BASE = MQTT_BASE_SCHEMA.extend( { vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Required(CONF_TOPIC): valid_subscribe_topic, + vol.Optional(CONF_IMAGE_ENCODING): "b64", } ).extend(MQTT_ENTITY_COMMON_SCHEMA.schema) -# Configuring MQTT Camera under the camera platform key is deprecated in HA Core 2022.6 -PLATFORM_SCHEMA = vol.All( - cv.PLATFORM_SCHEMA.extend(PLATFORM_SCHEMA_MODERN.schema), - warn_for_legacy_schema(camera.DOMAIN), +PLATFORM_SCHEMA_MODERN = vol.All( + PLATFORM_SCHEMA_BASE.schema, + repair_legacy_encoding, ) -DISCOVERY_SCHEMA = PLATFORM_SCHEMA_MODERN.extend({}, extra=vol.REMOVE_EXTRA) +# Configuring MQTT Camera under the camera platform key is deprecated in HA Core 2022.6 +PLATFORM_SCHEMA = vol.All( + cv.PLATFORM_SCHEMA.extend(PLATFORM_SCHEMA_BASE.schema), + warn_for_legacy_schema(camera.DOMAIN), + repair_legacy_encoding, +) + +DISCOVERY_SCHEMA = PLATFORM_SCHEMA_BASE.extend({}, extra=vol.REMOVE_EXTRA) async def async_setup_platform( @@ -124,7 +150,7 @@ class MqttCamera(MqttEntity, Camera): @log_messages(self.hass, self.entity_id) def message_received(msg): """Handle new MQTT messages.""" - if self._config[CONF_ENCODING] == "b64": + if CONF_IMAGE_ENCODING in self._config: self._last_image = b64decode(msg.payload) else: self._last_image = msg.payload diff --git a/tests/components/mqtt/test_camera.py b/tests/components/mqtt/test_camera.py index c6d116b6a744..a76025a608a5 100644 --- a/tests/components/mqtt/test_camera.py +++ b/tests/components/mqtt/test_camera.py @@ -110,6 +110,80 @@ async def test_run_camera_b64_encoded( assert body == "grass" +# Using CONF_ENCODING to set b64 encoding for images is deprecated Home Assistant 2022.9, use CONF_IMAGE_ENCODING instead +async def test_legacy_camera_b64_encoded_with_availability( + hass, hass_client_no_auth, mqtt_mock_entry_with_yaml_config +): + """Test availability works if b64 encoding (legacy mode) is turned on.""" + topic = "test/camera" + topic_availability = "test/camera_availability" + await async_setup_component( + hass, + "camera", + { + "camera": { + "platform": "mqtt", + "topic": topic, + "name": "Test Camera", + "encoding": "b64", + "availability": {"topic": topic_availability}, + } + }, + ) + await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() + + # Make sure we are available + async_fire_mqtt_message(hass, topic_availability, "online") + + url = hass.states.get("camera.test_camera").attributes["entity_picture"] + + async_fire_mqtt_message(hass, topic, b64encode(b"grass")) + + client = await hass_client_no_auth() + resp = await client.get(url) + assert resp.status == HTTPStatus.OK + body = await resp.text() + assert body == "grass" + + +async def test_camera_b64_encoded_with_availability( + hass, hass_client_no_auth, mqtt_mock_entry_with_yaml_config +): + """Test availability works if b64 encoding is turned on.""" + topic = "test/camera" + topic_availability = "test/camera_availability" + await async_setup_component( + hass, + "camera", + { + "camera": { + "platform": "mqtt", + "topic": topic, + "name": "Test Camera", + "encoding": "utf-8", + "image_encoding": "b64", + "availability": {"topic": topic_availability}, + } + }, + ) + await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() + + # Make sure we are available + async_fire_mqtt_message(hass, topic_availability, "online") + + url = hass.states.get("camera.test_camera").attributes["entity_picture"] + + async_fire_mqtt_message(hass, topic, b64encode(b"grass")) + + client = await hass_client_no_auth() + resp = await client.get(url) + assert resp.status == HTTPStatus.OK + body = await resp.text() + assert body == "grass" + + async def test_availability_when_connection_lost( hass, mqtt_mock_entry_with_yaml_config ):