diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index b99036efbe3..59f64972f6c 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -57,6 +57,7 @@ from .const import ( CONF_BIRTH_MESSAGE, CONF_BROKER, CONF_COMMAND_TOPIC, + CONF_ENCODING, CONF_QOS, CONF_RETAIN, CONF_STATE_TOPIC, @@ -65,6 +66,7 @@ from .const import ( DATA_MQTT_CONFIG, DEFAULT_BIRTH, DEFAULT_DISCOVERY, + DEFAULT_ENCODING, DEFAULT_PREFIX, DEFAULT_QOS, DEFAULT_RETAIN, @@ -200,7 +202,10 @@ CONFIG_SCHEMA = vol.Schema( extra=vol.ALLOW_EXTRA, ) -SCHEMA_BASE = {vol.Optional(CONF_QOS, default=DEFAULT_QOS): _VALID_QOS_SCHEMA} +SCHEMA_BASE = { + vol.Optional(CONF_QOS, default=DEFAULT_QOS): _VALID_QOS_SCHEMA, + vol.Optional(CONF_ENCODING, default=DEFAULT_ENCODING): cv.string, +} MQTT_BASE_PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend(SCHEMA_BASE) diff --git a/homeassistant/components/mqtt/abbreviations.py b/homeassistant/components/mqtt/abbreviations.py index 9ad5ca4ce1c..c47b512b727 100644 --- a/homeassistant/components/mqtt/abbreviations.py +++ b/homeassistant/components/mqtt/abbreviations.py @@ -46,6 +46,7 @@ ABBREVIATIONS = { "dev_cla": "device_class", "dock_t": "docked_topic", "dock_tpl": "docked_template", + "e": "encoding", "en": "enabled_by_default", "err_t": "error_topic", "err_tpl": "error_template", diff --git a/homeassistant/components/mqtt/binary_sensor.py b/homeassistant/components/mqtt/binary_sensor.py index 1928f79032d..3d988079c6f 100644 --- a/homeassistant/components/mqtt/binary_sensor.py +++ b/homeassistant/components/mqtt/binary_sensor.py @@ -28,7 +28,7 @@ from homeassistant.util import dt as dt_util from . import PLATFORMS, subscription from .. import mqtt -from .const import CONF_QOS, CONF_STATE_TOPIC, DOMAIN +from .const import CONF_ENCODING, CONF_QOS, CONF_STATE_TOPIC, DOMAIN from .debug_info import log_messages from .mixins import ( MQTT_ENTITY_COMMON_SCHEMA, @@ -200,6 +200,7 @@ class MqttBinarySensor(MqttEntity, BinarySensorEntity): "topic": self._config[CONF_STATE_TOPIC], "msg_callback": state_message_received, "qos": self._config[CONF_QOS], + "encoding": self._config[CONF_ENCODING] or None, } }, ) diff --git a/homeassistant/components/mqtt/const.py b/homeassistant/components/mqtt/const.py index c626592b0a3..9c788490ea3 100644 --- a/homeassistant/components/mqtt/const.py +++ b/homeassistant/components/mqtt/const.py @@ -13,6 +13,7 @@ CONF_AVAILABILITY = "availability" CONF_BROKER = "broker" CONF_BIRTH_MESSAGE = "birth_message" CONF_COMMAND_TOPIC = "command_topic" +CONF_ENCODING = "encoding" CONF_QOS = ATTR_QOS CONF_RETAIN = ATTR_RETAIN CONF_STATE_TOPIC = "state_topic" @@ -24,6 +25,7 @@ DATA_MQTT_CONFIG = "mqtt_config" DEFAULT_PREFIX = "homeassistant" DEFAULT_BIRTH_WILL_TOPIC = DEFAULT_PREFIX + "/status" DEFAULT_DISCOVERY = True +DEFAULT_ENCODING = "utf-8" DEFAULT_QOS = 0 DEFAULT_PAYLOAD_AVAILABLE = "online" DEFAULT_PAYLOAD_NOT_AVAILABLE = "offline" diff --git a/homeassistant/components/mqtt/sensor.py b/homeassistant/components/mqtt/sensor.py index 7ddc8ec467a..72f0b339fe2 100644 --- a/homeassistant/components/mqtt/sensor.py +++ b/homeassistant/components/mqtt/sensor.py @@ -33,7 +33,7 @@ from homeassistant.util import dt as dt_util from . import PLATFORMS, subscription from .. import mqtt -from .const import CONF_QOS, CONF_STATE_TOPIC, DOMAIN +from .const import CONF_ENCODING, CONF_QOS, CONF_STATE_TOPIC, DOMAIN from .debug_info import log_messages from .mixins import ( MQTT_ENTITY_COMMON_SCHEMA, @@ -252,6 +252,7 @@ class MqttSensor(MqttEntity, SensorEntity): "topic": self._config[CONF_STATE_TOPIC], "msg_callback": message_received, "qos": self._config[CONF_QOS], + "encoding": self._config[CONF_ENCODING] or None, } @callback diff --git a/homeassistant/components/mqtt/trigger.py b/homeassistant/components/mqtt/trigger.py index f9c035dea85..db366010bb2 100644 --- a/homeassistant/components/mqtt/trigger.py +++ b/homeassistant/components/mqtt/trigger.py @@ -10,13 +10,10 @@ from homeassistant.core import HassJob, callback from homeassistant.helpers import config_validation as cv, template from .. import mqtt -from .const import CONF_QOS, CONF_TOPIC +from .const import CONF_ENCODING, CONF_QOS, CONF_TOPIC, DEFAULT_ENCODING, DEFAULT_QOS # mypy: allow-untyped-defs -CONF_ENCODING = "encoding" -DEFAULT_ENCODING = "utf-8" -DEFAULT_QOS = 0 TRIGGER_SCHEMA = cv.TRIGGER_BASE_SCHEMA.extend( { diff --git a/tests/components/mqtt/test_binary_sensor.py b/tests/components/mqtt/test_binary_sensor.py index ba601fd094d..6e1bdd60972 100644 --- a/tests/components/mqtt/test_binary_sensor.py +++ b/tests/components/mqtt/test_binary_sensor.py @@ -373,6 +373,39 @@ async def test_setting_sensor_value_via_mqtt_message_and_template2( assert "template output: 'ILLEGAL'" in caplog.text +async def test_setting_sensor_value_via_mqtt_message_and_template_and_raw_state_encoding( + hass, mqtt_mock, caplog +): + """Test processing a raw value via MQTT.""" + assert await async_setup_component( + hass, + binary_sensor.DOMAIN, + { + binary_sensor.DOMAIN: { + "platform": "mqtt", + "name": "test", + "encoding": "", + "state_topic": "test-topic", + "payload_on": "ON", + "payload_off": "OFF", + "value_template": "{%if value|bitwise_and(1)-%}ON{%else%}OFF{%-endif-%}", + } + }, + ) + await hass.async_block_till_done() + + state = hass.states.get("binary_sensor.test") + assert state.state == STATE_OFF + + async_fire_mqtt_message(hass, "test-topic", b"\x01") + state = hass.states.get("binary_sensor.test") + assert state.state == STATE_ON + + async_fire_mqtt_message(hass, "test-topic", b"\x00") + state = hass.states.get("binary_sensor.test") + assert state.state == STATE_OFF + + async def test_setting_sensor_value_via_mqtt_message_empty_template( hass, mqtt_mock, caplog ): diff --git a/tests/components/mqtt/test_sensor.py b/tests/components/mqtt/test_sensor.py index 680bafb3b2b..2ebfdde4a52 100644 --- a/tests/components/mqtt/test_sensor.py +++ b/tests/components/mqtt/test_sensor.py @@ -894,6 +894,35 @@ async def test_entity_category(hass, mqtt_mock): async def test_value_template_with_entity_id(hass, mqtt_mock): + """Test processing a raw value via MQTT.""" + assert await async_setup_component( + hass, + sensor.DOMAIN, + { + sensor.DOMAIN: { + "platform": "mqtt", + "name": "test", + "encoding": "", + "state_topic": "test-topic", + "unit_of_measurement": "fav unit", + "value_template": "{{ value | bitwise_and(255) }}", + } + }, + ) + await hass.async_block_till_done() + + async_fire_mqtt_message(hass, "test-topic", b"\xff") + state = hass.states.get("sensor.test") + + assert state.state == "255" + + async_fire_mqtt_message(hass, "test-topic", b"\x01\x10") + state = hass.states.get("sensor.test") + + assert state.state == "16" + + +async def test_value_template_with_raw_data(hass, mqtt_mock): """Test the access to attributes in value_template via the entity_id.""" assert await async_setup_component( hass,