Add mqtt encoding support for publishing (#62739)

* encoding support for mqtt publishing - todo tests

* signature allows None values for qos and retain

* common test for mqtt publishing encoding

* better test with command templates

* more tests

* fix tests alarm control panel+tests light basic

* tests light json and template

* add tests vacuum and fix tests light_template
This commit is contained in:
Jan Bouwhuis 2022-01-03 09:03:47 +01:00 committed by GitHub
parent 2cc4d9846b
commit d0c4f0fec4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
33 changed files with 1283 additions and 27 deletions

View file

@ -338,18 +338,53 @@ def _build_publish_data(topic: Any, qos: int, retain: bool) -> ServiceDataType:
return data
def publish(hass: HomeAssistant, topic, payload, qos=0, retain=False) -> None:
"""Publish message to an MQTT topic."""
hass.add_job(async_publish, hass, topic, payload, qos, retain)
def publish(
hass: HomeAssistant,
topic: str,
payload: PublishPayloadType,
qos: int | None = 0,
retain: bool | None = False,
encoding: str | None = DEFAULT_ENCODING,
) -> None:
"""Publish message to a MQTT topic."""
hass.add_job(async_publish, hass, topic, payload, qos, retain, encoding)
async def async_publish(
hass: HomeAssistant, topic: Any, payload, qos=0, retain=False
hass: HomeAssistant,
topic: str,
payload: PublishPayloadType,
qos: int | None = 0,
retain: bool | None = False,
encoding: str | None = DEFAULT_ENCODING,
) -> None:
"""Publish message to an MQTT topic."""
await hass.data[DATA_MQTT].async_publish(
topic, str(payload) if not isinstance(payload, bytes) else payload, qos, retain
"""Publish message to a MQTT topic."""
outgoing_payload = payload
if not isinstance(payload, bytes):
if not encoding:
_LOGGER.error(
"Can't pass-through payload for publishing %s on %s with no encoding set, need 'bytes' got %s",
payload,
topic,
type(payload),
)
return
outgoing_payload = str(payload)
if encoding != DEFAULT_ENCODING:
# a string is encoded as utf-8 by default, other encoding requires bytes as payload
try:
outgoing_payload = outgoing_payload.encode(encoding)
except (AttributeError, LookupError, UnicodeEncodeError):
_LOGGER.error(
"Can't encode payload for publishing %s on %s with encoding %s",
payload,
topic,
encoding,
)
return
await hass.data[DATA_MQTT].async_publish(topic, outgoing_payload, qos, retain)
AsyncDeprecatedMessageCallbackType = Callable[

View file

@ -40,7 +40,14 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from . import PLATFORMS, MqttCommandTemplate, subscription
from .. import mqtt
from .const import CONF_COMMAND_TOPIC, CONF_QOS, CONF_RETAIN, CONF_STATE_TOPIC, DOMAIN
from .const import (
CONF_COMMAND_TOPIC,
CONF_ENCODING,
CONF_QOS,
CONF_RETAIN,
CONF_STATE_TOPIC,
DOMAIN,
)
from .debug_info import log_messages
from .mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, async_setup_entry_helper
@ -326,6 +333,7 @@ class MqttAlarm(MqttEntity, alarm.AlarmControlPanelEntity):
payload,
self._config[CONF_QOS],
self._config[CONF_RETAIN],
self._config[CONF_ENCODING],
)
def _validate_code(self, code, state):

View file

@ -17,7 +17,7 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from . import PLATFORMS
from .. import mqtt
from .const import CONF_COMMAND_TOPIC, CONF_QOS, CONF_RETAIN, DOMAIN
from .const import CONF_COMMAND_TOPIC, CONF_ENCODING, CONF_QOS, CONF_RETAIN, DOMAIN
from .mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, async_setup_entry_helper
CONF_PAYLOAD_PRESS = "payload_press"
@ -100,4 +100,5 @@ class MqttButton(MqttEntity, ButtonEntity):
self._config[CONF_PAYLOAD_PRESS],
self._config[CONF_QOS],
self._config[CONF_RETAIN],
self._config[CONF_ENCODING],
)

View file

@ -58,7 +58,7 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from . import MQTT_BASE_PLATFORM_SCHEMA, PLATFORMS, MqttCommandTemplate, subscription
from .. import mqtt
from .const import CONF_QOS, CONF_RETAIN, DOMAIN
from .const import CONF_ENCODING, CONF_QOS, CONF_RETAIN, DOMAIN
from .debug_info import log_messages
from .mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, async_setup_entry_helper
@ -685,6 +685,7 @@ class MqttClimate(MqttEntity, ClimateEntity):
payload,
self._config[CONF_QOS],
self._config[CONF_RETAIN],
self._config[CONF_ENCODING],
)
async def _set_temperature(

View file

@ -42,7 +42,14 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from . import PLATFORMS, MqttCommandTemplate, subscription
from .. import mqtt
from .const import CONF_COMMAND_TOPIC, CONF_QOS, CONF_RETAIN, CONF_STATE_TOPIC, DOMAIN
from .const import (
CONF_COMMAND_TOPIC,
CONF_ENCODING,
CONF_QOS,
CONF_RETAIN,
CONF_STATE_TOPIC,
DOMAIN,
)
from .debug_info import log_messages
from .mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, async_setup_entry_helper
@ -547,6 +554,7 @@ class MqttCover(MqttEntity, CoverEntity):
self._config[CONF_PAYLOAD_OPEN],
self._config[CONF_QOS],
self._config[CONF_RETAIN],
self._config[CONF_ENCODING],
)
if self._optimistic:
# Optimistically assume that cover has changed state.
@ -568,6 +576,7 @@ class MqttCover(MqttEntity, CoverEntity):
self._config[CONF_PAYLOAD_CLOSE],
self._config[CONF_QOS],
self._config[CONF_RETAIN],
self._config[CONF_ENCODING],
)
if self._optimistic:
# Optimistically assume that cover has changed state.
@ -589,6 +598,7 @@ class MqttCover(MqttEntity, CoverEntity):
self._config[CONF_PAYLOAD_STOP],
self._config[CONF_QOS],
self._config[CONF_RETAIN],
self._config[CONF_ENCODING],
)
async def async_open_cover_tilt(self, **kwargs):
@ -599,6 +609,7 @@ class MqttCover(MqttEntity, CoverEntity):
self._config[CONF_TILT_OPEN_POSITION],
self._config[CONF_QOS],
self._config[CONF_RETAIN],
self._config[CONF_ENCODING],
)
if self._tilt_optimistic:
self._tilt_value = self.find_percentage_in_range(
@ -614,6 +625,7 @@ class MqttCover(MqttEntity, CoverEntity):
self._config[CONF_TILT_CLOSED_POSITION],
self._config[CONF_QOS],
self._config[CONF_RETAIN],
self._config[CONF_ENCODING],
)
if self._tilt_optimistic:
self._tilt_value = self.find_percentage_in_range(
@ -643,6 +655,7 @@ class MqttCover(MqttEntity, CoverEntity):
tilt,
self._config[CONF_QOS],
self._config[CONF_RETAIN],
self._config[CONF_ENCODING],
)
if self._tilt_optimistic:
_LOGGER.debug("Set tilt value optimistic")
@ -670,6 +683,7 @@ class MqttCover(MqttEntity, CoverEntity):
position,
self._config[CONF_QOS],
self._config[CONF_RETAIN],
self._config[CONF_ENCODING],
)
if self._optimistic:
self._state = (

View file

@ -42,7 +42,14 @@ from homeassistant.util.percentage import (
from . import PLATFORMS, MqttCommandTemplate, subscription
from .. import mqtt
from .const import CONF_COMMAND_TOPIC, CONF_QOS, CONF_RETAIN, CONF_STATE_TOPIC, DOMAIN
from .const import (
CONF_COMMAND_TOPIC,
CONF_ENCODING,
CONF_QOS,
CONF_RETAIN,
CONF_STATE_TOPIC,
DOMAIN,
)
from .debug_info import log_messages
from .mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, async_setup_entry_helper
@ -542,6 +549,7 @@ class MqttFan(MqttEntity, FanEntity):
mqtt_payload,
self._config[CONF_QOS],
self._config[CONF_RETAIN],
self._config[CONF_ENCODING],
)
if percentage:
await self.async_set_percentage(percentage)
@ -563,6 +571,7 @@ class MqttFan(MqttEntity, FanEntity):
mqtt_payload,
self._config[CONF_QOS],
self._config[CONF_RETAIN],
self._config[CONF_ENCODING],
)
if self._optimistic:
self._state = False
@ -583,6 +592,7 @@ class MqttFan(MqttEntity, FanEntity):
mqtt_payload,
self._config[CONF_QOS],
self._config[CONF_RETAIN],
self._config[CONF_ENCODING],
)
if self._optimistic_percentage:
@ -606,6 +616,7 @@ class MqttFan(MqttEntity, FanEntity):
mqtt_payload,
self._config[CONF_QOS],
self._config[CONF_RETAIN],
self._config[CONF_ENCODING],
)
if self._optimistic_preset_mode:
@ -632,6 +643,7 @@ class MqttFan(MqttEntity, FanEntity):
mqtt_payload,
self._config[CONF_QOS],
self._config[CONF_RETAIN],
self._config[CONF_ENCODING],
)
if self._optimistic_oscillation:

View file

@ -32,7 +32,14 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from . import PLATFORMS, MqttCommandTemplate, subscription
from .. import mqtt
from .const import CONF_COMMAND_TOPIC, CONF_QOS, CONF_RETAIN, CONF_STATE_TOPIC, DOMAIN
from .const import (
CONF_COMMAND_TOPIC,
CONF_ENCODING,
CONF_QOS,
CONF_RETAIN,
CONF_STATE_TOPIC,
DOMAIN,
)
from .debug_info import log_messages
from .mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, async_setup_entry_helper
@ -408,6 +415,7 @@ class MqttHumidifier(MqttEntity, HumidifierEntity):
mqtt_payload,
self._config[CONF_QOS],
self._config[CONF_RETAIN],
self._config[CONF_ENCODING],
)
if self._optimistic:
self._state = True
@ -425,6 +433,7 @@ class MqttHumidifier(MqttEntity, HumidifierEntity):
mqtt_payload,
self._config[CONF_QOS],
self._config[CONF_RETAIN],
self._config[CONF_ENCODING],
)
if self._optimistic:
self._state = False
@ -442,6 +451,7 @@ class MqttHumidifier(MqttEntity, HumidifierEntity):
mqtt_payload,
self._config[CONF_QOS],
self._config[CONF_RETAIN],
self._config[CONF_ENCODING],
)
if self._optimistic_target_humidity:
@ -465,6 +475,7 @@ class MqttHumidifier(MqttEntity, HumidifierEntity):
mqtt_payload,
self._config[CONF_QOS],
self._config[CONF_RETAIN],
self._config[CONF_ENCODING],
)
if self._optimistic_mode:

View file

@ -53,7 +53,13 @@ import homeassistant.util.color as color_util
from .. import subscription
from ... import mqtt
from ..const import CONF_COMMAND_TOPIC, CONF_QOS, CONF_RETAIN, CONF_STATE_TOPIC
from ..const import (
CONF_COMMAND_TOPIC,
CONF_ENCODING,
CONF_QOS,
CONF_RETAIN,
CONF_STATE_TOPIC,
)
from ..debug_info import log_messages
from ..mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity
from .schema import MQTT_LIGHT_SCHEMA_SCHEMA
@ -821,6 +827,7 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity):
payload,
self._config[CONF_QOS],
self._config[CONF_RETAIN],
self._config[CONF_ENCODING],
)
def scale_rgbx(color, brightness=None):
@ -1069,6 +1076,7 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity):
self._payload["off"],
self._config[CONF_QOS],
self._config[CONF_RETAIN],
self._config[CONF_ENCODING],
)
if self._optimistic:

View file

@ -59,7 +59,13 @@ import homeassistant.util.color as color_util
from .. import subscription
from ... import mqtt
from ..const import CONF_COMMAND_TOPIC, CONF_QOS, CONF_RETAIN, CONF_STATE_TOPIC
from ..const import (
CONF_COMMAND_TOPIC,
CONF_ENCODING,
CONF_QOS,
CONF_RETAIN,
CONF_STATE_TOPIC,
)
from ..debug_info import log_messages
from ..mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity
from .schema import MQTT_LIGHT_SCHEMA_SCHEMA
@ -629,6 +635,7 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity):
json.dumps(message),
self._config[CONF_QOS],
self._config[CONF_RETAIN],
self._config[CONF_ENCODING],
)
if self._optimistic:
@ -654,6 +661,7 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity):
json.dumps(message),
self._config[CONF_QOS],
self._config[CONF_RETAIN],
self._config[CONF_ENCODING],
)
if self._optimistic:

View file

@ -35,7 +35,13 @@ import homeassistant.util.color as color_util
from .. import subscription
from ... import mqtt
from ..const import CONF_COMMAND_TOPIC, CONF_QOS, CONF_RETAIN, CONF_STATE_TOPIC
from ..const import (
CONF_COMMAND_TOPIC,
CONF_ENCODING,
CONF_QOS,
CONF_RETAIN,
CONF_STATE_TOPIC,
)
from ..debug_info import log_messages
from ..mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity
from .schema import MQTT_LIGHT_SCHEMA_SCHEMA
@ -384,6 +390,7 @@ class MqttLightTemplate(MqttEntity, LightEntity, RestoreEntity):
),
self._config[CONF_QOS],
self._config[CONF_RETAIN],
self._config[CONF_ENCODING],
)
if self._optimistic:
@ -409,6 +416,7 @@ class MqttLightTemplate(MqttEntity, LightEntity, RestoreEntity):
),
self._config[CONF_QOS],
self._config[CONF_RETAIN],
self._config[CONF_ENCODING],
)
if self._optimistic:

View file

@ -17,7 +17,14 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from . import PLATFORMS, subscription
from .. import mqtt
from .const import CONF_COMMAND_TOPIC, CONF_QOS, CONF_RETAIN, CONF_STATE_TOPIC, DOMAIN
from .const import (
CONF_COMMAND_TOPIC,
CONF_ENCODING,
CONF_QOS,
CONF_RETAIN,
CONF_STATE_TOPIC,
DOMAIN,
)
from .debug_info import log_messages
from .mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, async_setup_entry_helper
@ -175,6 +182,7 @@ class MqttLock(MqttEntity, LockEntity):
self._config[CONF_PAYLOAD_LOCK],
self._config[CONF_QOS],
self._config[CONF_RETAIN],
self._config[CONF_ENCODING],
)
if self._optimistic:
# Optimistically assume that the lock has changed state.
@ -192,6 +200,7 @@ class MqttLock(MqttEntity, LockEntity):
self._config[CONF_PAYLOAD_UNLOCK],
self._config[CONF_QOS],
self._config[CONF_RETAIN],
self._config[CONF_ENCODING],
)
if self._optimistic:
# Optimistically assume that the lock has changed state.
@ -209,6 +218,7 @@ class MqttLock(MqttEntity, LockEntity):
self._config[CONF_PAYLOAD_OPEN],
self._config[CONF_QOS],
self._config[CONF_RETAIN],
self._config[CONF_ENCODING],
)
if self._optimistic:
# Optimistically assume that the lock unlocks when opened.

View file

@ -29,7 +29,14 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from . import PLATFORMS, MqttCommandTemplate, subscription
from .. import mqtt
from .const import CONF_COMMAND_TOPIC, CONF_QOS, CONF_RETAIN, CONF_STATE_TOPIC, DOMAIN
from .const import (
CONF_COMMAND_TOPIC,
CONF_ENCODING,
CONF_QOS,
CONF_RETAIN,
CONF_STATE_TOPIC,
DOMAIN,
)
from .debug_info import log_messages
from .mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, async_setup_entry_helper
@ -258,6 +265,7 @@ class MqttNumber(MqttEntity, NumberEntity, RestoreEntity):
payload,
self._config[CONF_QOS],
self._config[CONF_RETAIN],
self._config[CONF_ENCODING],
)
@property

View file

@ -17,7 +17,7 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from . import PLATFORMS
from .. import mqtt
from .const import CONF_COMMAND_TOPIC, CONF_QOS, CONF_RETAIN, DOMAIN
from .const import CONF_COMMAND_TOPIC, CONF_ENCODING, CONF_QOS, CONF_RETAIN, DOMAIN
from .mixins import (
CONF_OBJECT_ID,
MQTT_AVAILABILITY_SCHEMA,
@ -153,4 +153,5 @@ class MqttScene(
self._config[CONF_PAYLOAD_ON],
self._config[CONF_QOS],
self._config[CONF_RETAIN],
self._config[CONF_ENCODING],
)

View file

@ -19,7 +19,14 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from . import PLATFORMS, MqttCommandTemplate, subscription
from .. import mqtt
from .const import CONF_COMMAND_TOPIC, CONF_QOS, CONF_RETAIN, CONF_STATE_TOPIC, DOMAIN
from .const import (
CONF_COMMAND_TOPIC,
CONF_ENCODING,
CONF_QOS,
CONF_RETAIN,
CONF_STATE_TOPIC,
DOMAIN,
)
from .debug_info import log_messages
from .mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, async_setup_entry_helper
@ -184,6 +191,7 @@ class MqttSelect(MqttEntity, SelectEntity, RestoreEntity):
payload,
self._config[CONF_QOS],
self._config[CONF_RETAIN],
self._config[CONF_ENCODING],
)
@property

View file

@ -26,7 +26,14 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from . import PLATFORMS, subscription
from .. import mqtt
from .const import CONF_COMMAND_TOPIC, CONF_QOS, CONF_RETAIN, CONF_STATE_TOPIC, DOMAIN
from .const import (
CONF_COMMAND_TOPIC,
CONF_ENCODING,
CONF_QOS,
CONF_RETAIN,
CONF_STATE_TOPIC,
DOMAIN,
)
from .debug_info import log_messages
from .mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, async_setup_entry_helper
@ -188,6 +195,7 @@ class MqttSwitch(MqttEntity, SwitchEntity, RestoreEntity):
self._config[CONF_PAYLOAD_ON],
self._config[CONF_QOS],
self._config[CONF_RETAIN],
self._config[CONF_ENCODING],
)
if self._optimistic:
# Optimistically assume that switch has changed state.
@ -205,6 +213,7 @@ class MqttSwitch(MqttEntity, SwitchEntity, RestoreEntity):
self._config[CONF_PAYLOAD_OFF],
self._config[CONF_QOS],
self._config[CONF_RETAIN],
self._config[CONF_ENCODING],
)
if self._optimistic:
# Optimistically assume that switch has changed state.

View file

@ -26,7 +26,7 @@ from homeassistant.helpers.icon import icon_for_battery_level
from .. import subscription
from ... import mqtt
from ..const import CONF_COMMAND_TOPIC, CONF_QOS, CONF_RETAIN
from ..const import CONF_COMMAND_TOPIC, CONF_ENCODING, CONF_QOS, CONF_RETAIN
from ..debug_info import log_messages
from ..mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity
from .const import MQTT_VACUUM_ATTRIBUTES_BLOCKED
@ -199,6 +199,7 @@ class MqttVacuum(MqttEntity, VacuumEntity):
self._fan_speed_list = config[CONF_FAN_SPEED_LIST]
self._qos = config[CONF_QOS]
self._retain = config[CONF_RETAIN]
self._encoding = config[CONF_ENCODING]
self._command_topic = config.get(CONF_COMMAND_TOPIC)
self._set_fan_speed_topic = config.get(CONF_SET_FAN_SPEED_TOPIC)
@ -388,6 +389,7 @@ class MqttVacuum(MqttEntity, VacuumEntity):
self._payloads[CONF_PAYLOAD_TURN_ON],
self._qos,
self._retain,
self._encoding,
)
self._status = "Cleaning"
self.async_write_ha_state()
@ -403,6 +405,7 @@ class MqttVacuum(MqttEntity, VacuumEntity):
self._payloads[CONF_PAYLOAD_TURN_OFF],
self._qos,
self._retain,
self._encoding,
)
self._status = "Turning Off"
self.async_write_ha_state()
@ -418,6 +421,7 @@ class MqttVacuum(MqttEntity, VacuumEntity):
self._payloads[CONF_PAYLOAD_STOP],
self._qos,
self._retain,
self._encoding,
)
self._status = "Stopping the current task"
self.async_write_ha_state()
@ -433,6 +437,7 @@ class MqttVacuum(MqttEntity, VacuumEntity):
self._payloads[CONF_PAYLOAD_CLEAN_SPOT],
self._qos,
self._retain,
self._encoding,
)
self._status = "Cleaning spot"
self.async_write_ha_state()
@ -448,6 +453,7 @@ class MqttVacuum(MqttEntity, VacuumEntity):
self._payloads[CONF_PAYLOAD_LOCATE],
self._qos,
self._retain,
self._encoding,
)
self._status = "Hi, I'm over here!"
self.async_write_ha_state()
@ -463,6 +469,7 @@ class MqttVacuum(MqttEntity, VacuumEntity):
self._payloads[CONF_PAYLOAD_START_PAUSE],
self._qos,
self._retain,
self._encoding,
)
self._status = "Pausing/Resuming cleaning..."
self.async_write_ha_state()
@ -478,6 +485,7 @@ class MqttVacuum(MqttEntity, VacuumEntity):
self._payloads[CONF_PAYLOAD_RETURN_TO_BASE],
self._qos,
self._retain,
self._encoding,
)
self._status = "Returning home..."
self.async_write_ha_state()
@ -490,7 +498,12 @@ class MqttVacuum(MqttEntity, VacuumEntity):
return None
await mqtt.async_publish(
self.hass, self._set_fan_speed_topic, fan_speed, self._qos, self._retain
self.hass,
self._set_fan_speed_topic,
fan_speed,
self._qos,
self._retain,
self._encoding,
)
self._status = f"Setting fan to {fan_speed}..."
self.async_write_ha_state()
@ -506,7 +519,12 @@ class MqttVacuum(MqttEntity, VacuumEntity):
else:
message = command
await mqtt.async_publish(
self.hass, self._send_command_topic, message, self._qos, self._retain
self.hass,
self._send_command_topic,
message,
self._qos,
self._retain,
self._encoding,
)
self._status = f"Sending command {message}..."
self.async_write_ha_state()

View file

@ -29,7 +29,13 @@ import homeassistant.helpers.config_validation as cv
from .. import subscription
from ... import mqtt
from ..const import CONF_COMMAND_TOPIC, CONF_QOS, CONF_RETAIN, CONF_STATE_TOPIC
from ..const import (
CONF_COMMAND_TOPIC,
CONF_ENCODING,
CONF_QOS,
CONF_RETAIN,
CONF_STATE_TOPIC,
)
from ..debug_info import log_messages
from ..mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity
from .const import MQTT_VACUUM_ATTRIBUTES_BLOCKED
@ -248,6 +254,7 @@ class MqttStateVacuum(MqttEntity, StateVacuumEntity):
self._config[CONF_PAYLOAD_START],
self._config[CONF_QOS],
self._config[CONF_RETAIN],
self._config[CONF_ENCODING],
)
async def async_pause(self):
@ -260,6 +267,7 @@ class MqttStateVacuum(MqttEntity, StateVacuumEntity):
self._config[CONF_PAYLOAD_PAUSE],
self._config[CONF_QOS],
self._config[CONF_RETAIN],
self._config[CONF_ENCODING],
)
async def async_stop(self, **kwargs):
@ -272,6 +280,7 @@ class MqttStateVacuum(MqttEntity, StateVacuumEntity):
self._config[CONF_PAYLOAD_STOP],
self._config[CONF_QOS],
self._config[CONF_RETAIN],
self._config[CONF_ENCODING],
)
async def async_set_fan_speed(self, fan_speed, **kwargs):
@ -286,6 +295,7 @@ class MqttStateVacuum(MqttEntity, StateVacuumEntity):
fan_speed,
self._config[CONF_QOS],
self._config[CONF_RETAIN],
self._config[CONF_ENCODING],
)
async def async_return_to_base(self, **kwargs):
@ -298,6 +308,7 @@ class MqttStateVacuum(MqttEntity, StateVacuumEntity):
self._config[CONF_PAYLOAD_RETURN_TO_BASE],
self._config[CONF_QOS],
self._config[CONF_RETAIN],
self._config[CONF_ENCODING],
)
async def async_clean_spot(self, **kwargs):
@ -310,6 +321,7 @@ class MqttStateVacuum(MqttEntity, StateVacuumEntity):
self._config[CONF_PAYLOAD_CLEAN_SPOT],
self._config[CONF_QOS],
self._config[CONF_RETAIN],
self._config[CONF_ENCODING],
)
async def async_locate(self, **kwargs):
@ -322,6 +334,7 @@ class MqttStateVacuum(MqttEntity, StateVacuumEntity):
self._config[CONF_PAYLOAD_LOCATE],
self._config[CONF_QOS],
self._config[CONF_RETAIN],
self._config[CONF_ENCODING],
)
async def async_send_command(self, command, params=None, **kwargs):
@ -340,4 +353,5 @@ class MqttStateVacuum(MqttEntity, StateVacuumEntity):
message,
self._config[CONF_QOS],
self._config[CONF_RETAIN],
self._config[CONF_ENCODING],
)

View file

@ -50,6 +50,7 @@ from .test_common import (
help_test_entity_device_info_with_identifier,
help_test_entity_id_update_discovery_update,
help_test_entity_id_update_subscriptions,
help_test_publishing_with_custom_encoding,
help_test_setting_attribute_via_mqtt_json_message,
help_test_setting_attribute_with_template,
help_test_setting_blocked_attribute_via_mqtt_json_message,
@ -750,3 +751,58 @@ async def test_entity_debug_info_message(hass, mqtt_mock):
await help_test_entity_debug_info_message(
hass, mqtt_mock, alarm_control_panel.DOMAIN, DEFAULT_CONFIG
)
@pytest.mark.parametrize(
"service,topic,parameters,payload,template,tpl_par,tpl_output",
[
(
alarm_control_panel.SERVICE_ALARM_ARM_AWAY,
"command_topic",
{"code": "secret"},
"ARM_AWAY",
"command_template",
"code",
b"s",
),
(
alarm_control_panel.SERVICE_ALARM_DISARM,
"command_topic",
{"code": "secret"},
"DISARM",
"command_template",
"code",
b"s",
),
],
)
async def test_publishing_with_custom_encoding(
hass,
mqtt_mock,
caplog,
service,
topic,
parameters,
payload,
template,
tpl_par,
tpl_output,
):
"""Test publishing MQTT payload with different encoding."""
domain = alarm_control_panel.DOMAIN
config = DEFAULT_CONFIG[domain]
await help_test_publishing_with_custom_encoding(
hass,
mqtt_mock,
caplog,
domain,
config,
service,
topic,
parameters,
payload,
template,
tpl_par=tpl_par,
tpl_output=tpl_output,
)

View file

@ -23,6 +23,7 @@ from .test_common import (
help_test_entity_device_info_with_connection,
help_test_entity_device_info_with_identifier,
help_test_entity_id_update_discovery_update,
help_test_publishing_with_custom_encoding,
help_test_setting_attribute_via_mqtt_json_message,
help_test_setting_attribute_with_template,
help_test_setting_blocked_attribute_via_mqtt_json_message,
@ -321,3 +322,30 @@ async def test_valid_device_class(hass, mqtt_mock):
assert state.attributes["device_class"] == button.ButtonDeviceClass.RESTART
state = hass.states.get("button.test_3")
assert "device_class" not in state.attributes
@pytest.mark.parametrize(
"service,topic,parameters,payload,template",
[
(button.SERVICE_PRESS, "command_topic", None, "PRESS", None),
],
)
async def test_publishing_with_custom_encoding(
hass, mqtt_mock, caplog, service, topic, parameters, payload, template
):
"""Test publishing MQTT payload with different encoding."""
domain = button.DOMAIN
config = DEFAULT_CONFIG[domain]
await help_test_publishing_with_custom_encoding(
hass,
mqtt_mock,
caplog,
domain,
config,
service,
topic,
parameters,
payload,
template,
)

View file

@ -6,6 +6,7 @@ from unittest.mock import call, patch
import pytest
import voluptuous as vol
from homeassistant.components import climate
from homeassistant.components.climate import DEFAULT_MAX_TEMP, DEFAULT_MIN_TEMP
from homeassistant.components.climate.const import (
ATTR_HVAC_ACTION,
@ -46,6 +47,7 @@ from .test_common import (
help_test_entity_device_info_with_identifier,
help_test_entity_id_update_discovery_update,
help_test_entity_id_update_subscriptions,
help_test_publishing_with_custom_encoding,
help_test_setting_attribute_via_mqtt_json_message,
help_test_setting_attribute_with_template,
help_test_setting_blocked_attribute_via_mqtt_json_message,
@ -1133,3 +1135,121 @@ async def test_precision_whole(hass, mqtt_mock):
state = hass.states.get(ENTITY_CLIMATE)
assert state.attributes.get("temperature") == 24.0
mqtt_mock.async_publish.reset_mock()
@pytest.mark.parametrize(
"service,topic,parameters,payload,template",
[
(
climate.SERVICE_TURN_ON,
"power_command_topic",
None,
"ON",
None,
),
(
climate.SERVICE_SET_HVAC_MODE,
"mode_command_topic",
{"hvac_mode": "cool"},
"cool",
"mode_command_template",
),
(
climate.SERVICE_SET_PRESET_MODE,
"away_mode_command_topic",
{"preset_mode": "away"},
"ON",
None,
),
(
climate.SERVICE_SET_PRESET_MODE,
"hold_command_topic",
{"preset_mode": "eco"},
"eco",
"hold_command_template",
),
(
climate.SERVICE_SET_PRESET_MODE,
"hold_command_topic",
{"preset_mode": "some_hold_mode"},
"some_hold_mode",
"hold_command_template",
),
(
climate.SERVICE_SET_FAN_MODE,
"fan_mode_command_topic",
{"fan_mode": "medium"},
"medium",
"fan_mode_command_template",
),
(
climate.SERVICE_SET_SWING_MODE,
"swing_mode_command_topic",
{"swing_mode": "on"},
"on",
"swing_mode_command_template",
),
(
climate.SERVICE_SET_AUX_HEAT,
"aux_command_topic",
{"aux_heat": "on"},
"ON",
None,
),
(
climate.SERVICE_SET_TEMPERATURE,
"temperature_command_topic",
{"temperature": "20.1"},
20.1,
"temperature_command_template",
),
(
climate.SERVICE_SET_TEMPERATURE,
"temperature_low_command_topic",
{
"temperature": "20.1",
"target_temp_low": "15.1",
"target_temp_high": "29.8",
},
15.1,
"temperature_low_command_template",
),
(
climate.SERVICE_SET_TEMPERATURE,
"temperature_high_command_topic",
{
"temperature": "20.1",
"target_temp_low": "15.1",
"target_temp_high": "29.8",
},
29.8,
"temperature_high_command_template",
),
],
)
async def test_publishing_with_custom_encoding(
hass,
mqtt_mock,
caplog,
service,
topic,
parameters,
payload,
template,
):
"""Test publishing MQTT payload with different encoding."""
domain = climate.DOMAIN
config = DEFAULT_CONFIG[domain]
await help_test_publishing_with_custom_encoding(
hass,
mqtt_mock,
caplog,
domain,
config,
service,
topic,
parameters,
payload,
template,
)

View file

@ -8,7 +8,7 @@ from homeassistant.components import mqtt
from homeassistant.components.mqtt import debug_info
from homeassistant.components.mqtt.const import MQTT_DISCONNECTED
from homeassistant.components.mqtt.mixins import MQTT_ATTRIBUTES_BLOCKED
from homeassistant.const import ATTR_ASSUMED_STATE, STATE_UNAVAILABLE
from homeassistant.const import ATTR_ASSUMED_STATE, ATTR_ENTITY_ID, STATE_UNAVAILABLE
from homeassistant.helpers import device_registry as dr, entity_registry as er
from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.setup import async_setup_component
@ -1266,3 +1266,120 @@ async def help_test_entity_category(hass, mqtt_mock, domain, config):
async_fire_mqtt_message(hass, f"homeassistant/{domain}/{unique_id}/config", data)
await hass.async_block_till_done()
assert not ent_registry.async_get_entity_id(domain, mqtt.DOMAIN, unique_id)
async def help_test_publishing_with_custom_encoding(
hass,
mqtt_mock,
caplog,
domain,
config,
service,
topic,
parameters,
payload,
template,
tpl_par="value",
tpl_output=None,
):
"""Test a service with publishing MQTT payload with different encoding."""
# prepare config for tests
test_config = {
"test1": {"encoding": None, "cmd_tpl": False},
"test2": {"encoding": "utf-16", "cmd_tpl": False},
"test3": {"encoding": "", "cmd_tpl": False},
"test4": {"encoding": "invalid", "cmd_tpl": False},
"test5": {"encoding": "", "cmd_tpl": True},
}
setup_config = []
service_data = {}
for test_id, test_data in test_config.items():
test_config_setup = copy.deepcopy(config)
test_config_setup.update(
{
topic: f"cmd/{test_id}",
"name": f"{test_id}",
}
)
if test_data["encoding"] is not None:
test_config_setup["encoding"] = test_data["encoding"]
if test_data["cmd_tpl"]:
test_config_setup[
template
] = f"{{{{ (('%.1f'|format({tpl_par}))[0] if is_number({tpl_par}) else {tpl_par}[0]) | ord | pack('b') }}}}"
setup_config.append(test_config_setup)
# setup service data
service_data[test_id] = {ATTR_ENTITY_ID: f"{domain}.{test_id}"}
if parameters:
service_data[test_id].update(parameters)
# setup test entities
assert await async_setup_component(
hass,
domain,
{domain: setup_config},
)
await hass.async_block_till_done()
# 1) test with default encoding
await hass.services.async_call(
domain,
service,
service_data["test1"],
blocking=True,
)
mqtt_mock.async_publish.assert_any_call("cmd/test1", str(payload), 0, False)
mqtt_mock.async_publish.reset_mock()
# 2) test with utf-16 encoding
await hass.services.async_call(
domain,
service,
service_data["test2"],
blocking=True,
)
mqtt_mock.async_publish.assert_any_call(
"cmd/test2", str(payload).encode("utf-16"), 0, False
)
mqtt_mock.async_publish.reset_mock()
# 3) test with no encoding set should fail if payload is a string
await hass.services.async_call(
domain,
service,
service_data["test3"],
blocking=True,
)
assert (
f"Can't pass-through payload for publishing {payload} on cmd/test3 with no encoding set, need 'bytes'"
in caplog.text
)
# 4) test with invalid encoding set should fail
await hass.services.async_call(
domain,
service,
service_data["test4"],
blocking=True,
)
assert (
f"Can't encode payload for publishing {payload} on cmd/test4 with encoding invalid"
in caplog.text
)
# 5) test with command template and raw encoding if specified
if not template:
return
await hass.services.async_call(
domain,
service,
service_data["test5"],
blocking=True,
)
mqtt_mock.async_publish.assert_any_call(
"cmd/test5", tpl_output or str(payload)[0].encode("utf-8"), 0, False
)
mqtt_mock.async_publish.reset_mock()

View file

@ -61,6 +61,7 @@ from .test_common import (
help_test_entity_device_info_with_identifier,
help_test_entity_id_update_discovery_update,
help_test_entity_id_update_subscriptions,
help_test_publishing_with_custom_encoding,
help_test_setting_attribute_via_mqtt_json_message,
help_test_setting_attribute_with_template,
help_test_setting_blocked_attribute_via_mqtt_json_message,
@ -3057,3 +3058,58 @@ async def test_tilt_status_template_without_tilt_status_topic_topic(
f"'{CONF_TILT_STATUS_TEMPLATE}' must be set together with '{CONF_TILT_STATUS_TOPIC}'."
in caplog.text
)
@pytest.mark.parametrize(
"service,topic,parameters,payload,template",
[
(
SERVICE_OPEN_COVER,
"command_topic",
None,
"OPEN",
None,
),
(
SERVICE_SET_COVER_POSITION,
"set_position_topic",
{ATTR_POSITION: "50"},
50,
"set_position_template",
),
(
SERVICE_SET_COVER_TILT_POSITION,
"tilt_command_topic",
{ATTR_TILT_POSITION: "45"},
45,
"tilt_command_template",
),
],
)
async def test_publishing_with_custom_encoding(
hass,
mqtt_mock,
caplog,
service,
topic,
parameters,
payload,
template,
):
"""Test publishing MQTT payload with different encoding."""
domain = cover.DOMAIN
config = DEFAULT_CONFIG[domain]
config["position_topic"] = "some-position-topic"
await help_test_publishing_with_custom_encoding(
hass,
mqtt_mock,
caplog,
domain,
config,
service,
topic,
parameters,
payload,
template,
)

View file

@ -1,4 +1,5 @@
"""Test MQTT fans."""
import copy
from unittest.mock import patch
import pytest
@ -32,6 +33,7 @@ from .test_common import (
help_test_entity_device_info_with_identifier,
help_test_entity_id_update_discovery_update,
help_test_entity_id_update_subscriptions,
help_test_publishing_with_custom_encoding,
help_test_setting_attribute_via_mqtt_json_message,
help_test_setting_attribute_with_template,
help_test_setting_blocked_attribute_via_mqtt_json_message,
@ -1663,3 +1665,73 @@ async def test_entity_debug_info_message(hass, mqtt_mock):
await help_test_entity_debug_info_message(
hass, mqtt_mock, fan.DOMAIN, DEFAULT_CONFIG
)
@pytest.mark.parametrize(
"service,topic,parameters,payload,template",
[
(
fan.SERVICE_TURN_ON,
"command_topic",
None,
"ON",
None,
),
(
fan.SERVICE_TURN_OFF,
"command_topic",
None,
"OFF",
None,
),
(
fan.SERVICE_SET_PRESET_MODE,
"preset_mode_command_topic",
{fan.ATTR_PRESET_MODE: "eco"},
"eco",
"preset_mode_command_template",
),
(
fan.SERVICE_SET_PERCENTAGE,
"percentage_command_topic",
{fan.ATTR_PERCENTAGE: "45"},
45,
"percentage_command_template",
),
(
fan.SERVICE_OSCILLATE,
"oscillation_command_topic",
{fan.ATTR_OSCILLATING: "on"},
"oscillate_on",
"oscillation_command_template",
),
],
)
async def test_publishing_with_custom_encoding(
hass,
mqtt_mock,
caplog,
service,
topic,
parameters,
payload,
template,
):
"""Test publishing MQTT payload with different encoding."""
domain = fan.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[domain])
if topic == "preset_mode_command_topic":
config["preset_modes"] = ["auto", "eco"]
await help_test_publishing_with_custom_encoding(
hass,
mqtt_mock,
caplog,
domain,
config,
service,
topic,
parameters,
payload,
template,
)

View file

@ -1,4 +1,5 @@
"""Test MQTT humidifiers."""
import copy
from unittest.mock import patch
import pytest
@ -42,6 +43,7 @@ from .test_common import (
help_test_entity_device_info_with_identifier,
help_test_entity_id_update_discovery_update,
help_test_entity_id_update_subscriptions,
help_test_publishing_with_custom_encoding,
help_test_setting_attribute_via_mqtt_json_message,
help_test_setting_attribute_with_template,
help_test_setting_blocked_attribute_via_mqtt_json_message,
@ -1058,3 +1060,66 @@ async def test_entity_debug_info_message(hass, mqtt_mock):
await help_test_entity_debug_info_message(
hass, mqtt_mock, humidifier.DOMAIN, DEFAULT_CONFIG
)
@pytest.mark.parametrize(
"service,topic,parameters,payload,template",
[
(
humidifier.SERVICE_TURN_ON,
"command_topic",
None,
"ON",
None,
),
(
humidifier.SERVICE_TURN_OFF,
"command_topic",
None,
"OFF",
None,
),
(
humidifier.SERVICE_SET_MODE,
"mode_command_topic",
{humidifier.ATTR_MODE: "eco"},
"eco",
"mode_command_template",
),
(
humidifier.SERVICE_SET_HUMIDITY,
"target_humidity_command_topic",
{humidifier.ATTR_HUMIDITY: "45"},
45,
"target_humidity_command_template",
),
],
)
async def test_publishing_with_custom_encoding(
hass,
mqtt_mock,
caplog,
service,
topic,
parameters,
payload,
template,
):
"""Test publishing MQTT payload with different encoding."""
domain = humidifier.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[domain])
if topic == "mode_command_topic":
config["modes"] = ["auto", "eco"]
await help_test_publishing_with_custom_encoding(
hass,
mqtt_mock,
caplog,
domain,
config,
service,
topic,
parameters,
payload,
template,
)

View file

@ -41,6 +41,7 @@ from .test_common import (
help_test_entity_device_info_with_identifier,
help_test_entity_id_update_discovery_update,
help_test_entity_id_update_subscriptions,
help_test_publishing_with_custom_encoding,
help_test_setting_attribute_via_mqtt_json_message,
help_test_setting_attribute_with_template,
help_test_setting_blocked_attribute_via_mqtt_json_message,
@ -746,3 +747,78 @@ async def test_entity_debug_info_message(hass, mqtt_mock):
await help_test_entity_debug_info_message(
hass, mqtt_mock, vacuum.DOMAIN, config, "test-topic"
)
@pytest.mark.parametrize(
"service,topic,parameters,payload,template",
[
(
vacuum.SERVICE_TURN_ON,
"command_topic",
None,
"turn_on",
None,
),
(
vacuum.SERVICE_CLEAN_SPOT,
"command_topic",
None,
"clean_spot",
None,
),
(
vacuum.SERVICE_SET_FAN_SPEED,
"set_fan_speed_topic",
{"fan_speed": "medium"},
"medium",
None,
),
(
vacuum.SERVICE_SEND_COMMAND,
"send_command_topic",
{"command": "custom command"},
"custom command",
None,
),
(
vacuum.SERVICE_TURN_OFF,
"command_topic",
None,
"turn_off",
None,
),
],
)
async def test_publishing_with_custom_encoding(
hass,
mqtt_mock,
caplog,
service,
topic,
parameters,
payload,
template,
):
"""Test publishing MQTT payload with different encoding."""
domain = vacuum.DOMAIN
config = deepcopy(DEFAULT_CONFIG)
config["supported_features"] = [
"turn_on",
"turn_off",
"clean_spot",
"fan_speed",
"send_command",
]
await help_test_publishing_with_custom_encoding(
hass,
mqtt_mock,
caplog,
domain,
config,
service,
topic,
parameters,
payload,
template,
)

View file

@ -153,6 +153,7 @@ light:
payload_off: "off"
"""
import copy
from unittest.mock import call, patch
import pytest
@ -189,6 +190,7 @@ from .test_common import (
help_test_entity_device_info_with_identifier,
help_test_entity_id_update_discovery_update,
help_test_entity_id_update_subscriptions,
help_test_publishing_with_custom_encoding,
help_test_setting_attribute_via_mqtt_json_message,
help_test_setting_attribute_with_template,
help_test_setting_blocked_attribute_via_mqtt_json_message,
@ -3407,3 +3409,125 @@ async def test_reloadable(hass, mqtt_mock):
assert hass.states.get("light.test") is None
assert hass.states.get("light.reload")
@pytest.mark.parametrize(
"service,topic,parameters,payload,template,tpl_par,tpl_output",
[
(
light.SERVICE_TURN_ON,
"command_topic",
None,
"ON",
None,
None,
None,
),
(
light.SERVICE_TURN_ON,
"white_command_topic",
{"white": "255"},
255,
None,
None,
None,
),
(
light.SERVICE_TURN_ON,
"brightness_command_topic",
{"color_temp": "200", "brightness": "50"},
50,
None,
None,
None,
),
(
light.SERVICE_TURN_ON,
"effect_command_topic",
{"rgb_color": [255, 128, 0], "effect": "color_loop"},
"color_loop",
None,
None,
None,
),
(
light.SERVICE_TURN_ON,
"color_temp_command_topic",
{"color_temp": "200"},
200,
"color_temp_command_template",
"value",
b"2",
),
(
light.SERVICE_TURN_ON,
"rgb_command_topic",
{"rgb_color": [255, 128, 0]},
"255,128,0",
"rgb_command_template",
"red",
b"2",
),
(
light.SERVICE_TURN_ON,
"hs_command_topic",
{"rgb_color": [255, 128, 0]},
"30.118,100.0",
None,
None,
None,
),
(
light.SERVICE_TURN_ON,
"xy_command_topic",
{"hs_color": [30.118, 100.0]},
"0.611,0.375",
None,
None,
None,
),
(
light.SERVICE_TURN_OFF,
"command_topic",
None,
"OFF",
None,
None,
None,
),
],
)
async def test_publishing_with_custom_encoding(
hass,
mqtt_mock,
caplog,
service,
topic,
parameters,
payload,
template,
tpl_par,
tpl_output,
):
"""Test publishing MQTT payload with different encoding."""
domain = light.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[domain])
if topic == "effect_command_topic":
config["effect_list"] = ["random", "color_loop"]
elif topic == "white_command_topic":
config["rgb_command_topic"] = "some-cmd-topic"
await help_test_publishing_with_custom_encoding(
hass,
mqtt_mock,
caplog,
domain,
config,
service,
topic,
parameters,
payload,
template,
tpl_par=tpl_par,
tpl_output=tpl_output,
)

View file

@ -87,6 +87,7 @@ light:
brightness: true
brightness_scale: 99
"""
import copy
import json
from unittest.mock import call, patch
@ -122,6 +123,7 @@ from .test_common import (
help_test_entity_device_info_with_identifier,
help_test_entity_id_update_discovery_update,
help_test_entity_id_update_subscriptions,
help_test_publishing_with_custom_encoding,
help_test_setting_attribute_via_mqtt_json_message,
help_test_setting_attribute_with_template,
help_test_setting_blocked_attribute_via_mqtt_json_message,
@ -1902,3 +1904,62 @@ async def test_max_mireds(hass, mqtt_mock):
state = hass.states.get("light.test")
assert state.attributes.get("min_mireds") == 153
assert state.attributes.get("max_mireds") == 370
@pytest.mark.parametrize(
"service,topic,parameters,payload,template,tpl_par,tpl_output",
[
(
light.SERVICE_TURN_ON,
"command_topic",
None,
'{"state": "ON"}',
None,
None,
None,
),
(
light.SERVICE_TURN_OFF,
"command_topic",
None,
'{"state": "OFF"}',
None,
None,
None,
),
],
)
async def test_publishing_with_custom_encoding(
hass,
mqtt_mock,
caplog,
service,
topic,
parameters,
payload,
template,
tpl_par,
tpl_output,
):
"""Test publishing MQTT payload with different encoding."""
domain = light.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[domain])
if topic == "effect_command_topic":
config["effect_list"] = ["random", "color_loop"]
elif topic == "white_command_topic":
config["rgb_command_topic"] = "some-cmd-topic"
await help_test_publishing_with_custom_encoding(
hass,
mqtt_mock,
caplog,
domain,
config,
service,
topic,
parameters,
payload,
template,
tpl_par=tpl_par,
tpl_output=tpl_output,
)

View file

@ -26,6 +26,7 @@ If your light doesn't support white value feature, omit `white_value_template`.
If your light doesn't support RGB feature, omit `(red|green|blue)_template`.
"""
import copy
from unittest.mock import patch
import pytest
@ -60,6 +61,7 @@ from .test_common import (
help_test_entity_device_info_with_identifier,
help_test_entity_id_update_discovery_update,
help_test_entity_id_update_subscriptions,
help_test_publishing_with_custom_encoding,
help_test_setting_attribute_via_mqtt_json_message,
help_test_setting_attribute_with_template,
help_test_setting_blocked_attribute_via_mqtt_json_message,
@ -1085,3 +1087,62 @@ async def test_max_mireds(hass, mqtt_mock):
state = hass.states.get("light.test")
assert state.attributes.get("min_mireds") == 153
assert state.attributes.get("max_mireds") == 370
@pytest.mark.parametrize(
"service,topic,parameters,payload,template,tpl_par,tpl_output",
[
(
light.SERVICE_TURN_ON,
"command_topic",
None,
"on,",
None,
None,
None,
),
(
light.SERVICE_TURN_OFF,
"command_topic",
None,
"off,",
None,
None,
None,
),
],
)
async def test_publishing_with_custom_encoding(
hass,
mqtt_mock,
caplog,
service,
topic,
parameters,
payload,
template,
tpl_par,
tpl_output,
):
"""Test publishing MQTT payload with different encoding."""
domain = light.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[domain])
if topic == "effect_command_topic":
config["effect_list"] = ["random", "color_loop"]
elif topic == "white_command_topic":
config["rgb_command_topic"] = "some-cmd-topic"
await help_test_publishing_with_custom_encoding(
hass,
mqtt_mock,
caplog,
domain,
config,
service,
topic,
parameters,
payload,
template,
tpl_par=tpl_par,
tpl_output=tpl_output,
)

View file

@ -37,6 +37,7 @@ from .test_common import (
help_test_entity_device_info_with_identifier,
help_test_entity_id_update_discovery_update,
help_test_entity_id_update_subscriptions,
help_test_publishing_with_custom_encoding,
help_test_setting_attribute_via_mqtt_json_message,
help_test_setting_attribute_with_template,
help_test_setting_blocked_attribute_via_mqtt_json_message,
@ -589,3 +590,43 @@ async def test_entity_debug_info_message(hass, mqtt_mock):
await help_test_entity_debug_info_message(
hass, mqtt_mock, LOCK_DOMAIN, DEFAULT_CONFIG
)
@pytest.mark.parametrize(
"service,topic,parameters,payload,template",
[
(
SERVICE_LOCK,
"command_topic",
None,
"LOCK",
None,
),
],
)
async def test_publishing_with_custom_encoding(
hass,
mqtt_mock,
caplog,
service,
topic,
parameters,
payload,
template,
):
"""Test publishing MQTT payload with different encoding."""
domain = LOCK_DOMAIN
config = DEFAULT_CONFIG[domain]
await help_test_publishing_with_custom_encoding(
hass,
mqtt_mock,
caplog,
domain,
config,
service,
topic,
parameters,
payload,
template,
)

View file

@ -43,6 +43,7 @@ from .test_common import (
help_test_entity_device_info_with_identifier,
help_test_entity_id_update_discovery_update,
help_test_entity_id_update_subscriptions,
help_test_publishing_with_custom_encoding,
help_test_setting_attribute_via_mqtt_json_message,
help_test_setting_attribute_with_template,
help_test_setting_blocked_attribute_via_mqtt_json_message,
@ -640,3 +641,43 @@ async def test_mqtt_payload_out_of_range_error(hass, caplog, mqtt_mock):
assert (
"Invalid value for number.test_number: 115.5 (range 5.0 - 110.0)" in caplog.text
)
@pytest.mark.parametrize(
"service,topic,parameters,payload,template",
[
(
SERVICE_SET_VALUE,
"command_topic",
{ATTR_VALUE: "45"},
45,
"command_template",
),
],
)
async def test_publishing_with_custom_encoding(
hass,
mqtt_mock,
caplog,
service,
topic,
parameters,
payload,
template,
):
"""Test publishing MQTT payload with different encoding."""
domain = NUMBER_DOMAIN
config = DEFAULT_CONFIG[domain]
await help_test_publishing_with_custom_encoding(
hass,
mqtt_mock,
caplog,
domain,
config,
service,
topic,
parameters,
payload,
template,
)

View file

@ -33,6 +33,7 @@ from .test_common import (
help_test_entity_device_info_with_identifier,
help_test_entity_id_update_discovery_update,
help_test_entity_id_update_subscriptions,
help_test_publishing_with_custom_encoding,
help_test_setting_attribute_via_mqtt_json_message,
help_test_setting_attribute_with_template,
help_test_setting_blocked_attribute_via_mqtt_json_message,
@ -524,3 +525,37 @@ async def test_mqtt_payload_not_an_option_warning(hass, caplog, mqtt_mock):
"Invalid option for select.test_select: 'öl' (valid options: ['milk', 'beer'])"
in caplog.text
)
@pytest.mark.parametrize(
"service,topic,parameters,payload,template",
[
(
select.SERVICE_SELECT_OPTION,
"command_topic",
{"option": "beer"},
"beer",
"command_template",
),
],
)
async def test_publishing_with_custom_encoding(
hass, mqtt_mock, caplog, service, topic, parameters, payload, template
):
"""Test publishing MQTT payload with different encoding."""
domain = select.DOMAIN
config = DEFAULT_CONFIG[domain]
config["options"] = ["milk", "beer"]
await help_test_publishing_with_custom_encoding(
hass,
mqtt_mock,
caplog,
domain,
config,
service,
topic,
parameters,
payload,
template,
)

View file

@ -51,6 +51,7 @@ from .test_common import (
help_test_entity_device_info_with_identifier,
help_test_entity_id_update_discovery_update,
help_test_entity_id_update_subscriptions,
help_test_publishing_with_custom_encoding,
help_test_setting_attribute_via_mqtt_json_message,
help_test_setting_attribute_with_template,
help_test_setting_blocked_attribute_via_mqtt_json_message,
@ -502,3 +503,83 @@ async def test_entity_debug_info_message(hass, mqtt_mock):
await help_test_entity_debug_info_message(
hass, mqtt_mock, vacuum.DOMAIN, DEFAULT_CONFIG_2, payload="{}"
)
@pytest.mark.parametrize(
"service,topic,parameters,payload,template",
[
(
vacuum.SERVICE_START,
"command_topic",
None,
"start",
None,
),
(
vacuum.SERVICE_CLEAN_SPOT,
"command_topic",
None,
"clean_spot",
None,
),
(
vacuum.SERVICE_SET_FAN_SPEED,
"set_fan_speed_topic",
{"fan_speed": "medium"},
"medium",
None,
),
(
vacuum.SERVICE_SEND_COMMAND,
"send_command_topic",
{"command": "custom command"},
"custom command",
None,
),
(
vacuum.SERVICE_STOP,
"command_topic",
None,
"stop",
None,
),
],
)
async def test_publishing_with_custom_encoding(
hass,
mqtt_mock,
caplog,
service,
topic,
parameters,
payload,
template,
):
"""Test publishing MQTT payload with different encoding."""
domain = vacuum.DOMAIN
config = deepcopy(DEFAULT_CONFIG)
config["supported_features"] = [
"battery",
"clean_spot",
"fan_speed",
"locate",
"pause",
"return_home",
"send_command",
"start",
"status",
"stop",
]
await help_test_publishing_with_custom_encoding(
hass,
mqtt_mock,
caplog,
domain,
config,
service,
topic,
parameters,
payload,
template,
)

View file

@ -32,6 +32,7 @@ from .test_common import (
help_test_entity_device_info_with_identifier,
help_test_entity_id_update_discovery_update,
help_test_entity_id_update_subscriptions,
help_test_publishing_with_custom_encoding,
help_test_setting_attribute_via_mqtt_json_message,
help_test_setting_attribute_with_template,
help_test_setting_blocked_attribute_via_mqtt_json_message,
@ -467,3 +468,50 @@ async def test_entity_debug_info_message(hass, mqtt_mock):
await help_test_entity_debug_info_message(
hass, mqtt_mock, switch.DOMAIN, DEFAULT_CONFIG
)
@pytest.mark.parametrize(
"service,topic,parameters,payload,template",
[
(
switch.SERVICE_TURN_ON,
"command_topic",
None,
"ON",
None,
),
(
switch.SERVICE_TURN_OFF,
"command_topic",
None,
"OFF",
None,
),
],
)
async def test_publishing_with_custom_encoding(
hass,
mqtt_mock,
caplog,
service,
topic,
parameters,
payload,
template,
):
"""Test publishing MQTT payload with different encoding."""
domain = switch.DOMAIN
config = DEFAULT_CONFIG[domain]
await help_test_publishing_with_custom_encoding(
hass,
mqtt_mock,
caplog,
domain,
config,
service,
topic,
parameters,
payload,
template,
)