Remove MQTT legacy vacuum support (#107274)
parent
3eb81bc461
commit
9ad3c8dbc9
|
@ -22,10 +22,6 @@ ABBREVIATIONS = {
|
|||
"bri_tpl": "brightness_template",
|
||||
"bri_val_tpl": "brightness_value_template",
|
||||
"clr_temp_cmd_tpl": "color_temp_command_template",
|
||||
"bat_lev_t": "battery_level_topic",
|
||||
"bat_lev_tpl": "battery_level_template",
|
||||
"chrg_t": "charging_topic",
|
||||
"chrg_tpl": "charging_template",
|
||||
"clrm": "color_mode",
|
||||
"clrm_stat_t": "color_mode_state_topic",
|
||||
"clrm_val_tpl": "color_mode_value_template",
|
||||
|
@ -33,8 +29,6 @@ ABBREVIATIONS = {
|
|||
"clr_temp_stat_t": "color_temp_state_topic",
|
||||
"clr_temp_tpl": "color_temp_template",
|
||||
"clr_temp_val_tpl": "color_temp_value_template",
|
||||
"cln_t": "cleaning_topic",
|
||||
"cln_tpl": "cleaning_template",
|
||||
"cmd_off_tpl": "command_off_template",
|
||||
"cmd_on_tpl": "command_on_template",
|
||||
"cmd_t": "command_topic",
|
||||
|
@ -54,19 +48,13 @@ ABBREVIATIONS = {
|
|||
"dir_cmd_tpl": "direction_command_template",
|
||||
"dir_stat_t": "direction_state_topic",
|
||||
"dir_val_tpl": "direction_value_template",
|
||||
"dock_t": "docked_topic",
|
||||
"dock_tpl": "docked_template",
|
||||
"dock_cmd_t": "dock_command_topic",
|
||||
"dock_cmd_tpl": "dock_command_template",
|
||||
"e": "encoding",
|
||||
"en": "enabled_by_default",
|
||||
"ent_cat": "entity_category",
|
||||
"ent_pic": "entity_picture",
|
||||
"err_t": "error_topic",
|
||||
"err_tpl": "error_template",
|
||||
"evt_typ": "event_types",
|
||||
"fanspd_t": "fan_speed_topic",
|
||||
"fanspd_tpl": "fan_speed_template",
|
||||
"fanspd_lst": "fan_speed_list",
|
||||
"flsh_tlng": "flash_time_long",
|
||||
"flsh_tsht": "flash_time_short",
|
||||
|
@ -160,7 +148,6 @@ ABBREVIATIONS = {
|
|||
"pl_rst_pr_mode": "payload_reset_preset_mode",
|
||||
"pl_stop": "payload_stop",
|
||||
"pl_strt": "payload_start",
|
||||
"pl_stpa": "payload_start_pause",
|
||||
"pl_ret": "payload_return_to_base",
|
||||
"pl_toff": "payload_turn_off",
|
||||
"pl_ton": "payload_turn_on",
|
||||
|
|
|
@ -1,12 +1,8 @@
|
|||
{
|
||||
"issues": {
|
||||
"deprecation_mqtt_legacy_vacuum_yaml": {
|
||||
"title": "MQTT vacuum entities with legacy schema found in your configuration.yaml",
|
||||
"description": "MQTT vacuum entities that use the legacy schema are deprecated, please adjust your configuration.yaml and restart Home Assistant to fix this issue."
|
||||
},
|
||||
"deprecation_mqtt_legacy_vacuum_discovery": {
|
||||
"title": "MQTT vacuum entities with legacy schema added through MQTT discovery",
|
||||
"description": "MQTT vacuum entities that use the legacy schema are deprecated, please adjust your devices to use the correct schema and restart Home Assistant to fix this issue."
|
||||
"deprecation_mqtt_schema_vacuum_yaml": {
|
||||
"title": "MQTT vacuum entities with deprecated `schema` config settings found in your configuration.yaml",
|
||||
"description": "The `schema` setting for MQTT vacuum entities is deprecated and should be removed. Please adjust your configuration.yaml and restart Home Assistant to fix this issue."
|
||||
},
|
||||
"deprecated_climate_aux_property": {
|
||||
"title": "MQTT entities with auxiliary heat support found",
|
||||
|
|
|
@ -1,10 +1,19 @@
|
|||
"""Support for a State MQTT vacuum."""
|
||||
"""Support for MQTT vacuums."""
|
||||
|
||||
# The legacy schema for MQTT vacuum was deprecated with HA Core 2023.8.0
|
||||
# and was removed with HA Core 2024.2.0
|
||||
# The use of the schema attribute with MQTT vacuum was deprecated with HA Core 2024.2
|
||||
# the attribute will be remove with HA Core 2024.8
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
import logging
|
||||
from typing import Any, cast
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components import vacuum
|
||||
from homeassistant.components.vacuum import (
|
||||
ENTITY_ID_FORMAT,
|
||||
STATE_CLEANING,
|
||||
|
@ -21,58 +30,37 @@ from homeassistant.const import (
|
|||
STATE_IDLE,
|
||||
STATE_PAUSED,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.core import HomeAssistant, async_get_hass, callback
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
|
||||
from homeassistant.helpers.json import json_dumps
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
from homeassistant.util.json import json_loads_object
|
||||
|
||||
from .. import subscription
|
||||
from ..config import MQTT_BASE_SCHEMA
|
||||
from ..const import (
|
||||
from . import subscription
|
||||
from .config import MQTT_BASE_SCHEMA
|
||||
from .const import (
|
||||
CONF_COMMAND_TOPIC,
|
||||
CONF_ENCODING,
|
||||
CONF_QOS,
|
||||
CONF_RETAIN,
|
||||
CONF_SCHEMA,
|
||||
CONF_STATE_TOPIC,
|
||||
DOMAIN,
|
||||
)
|
||||
from ..debug_info import log_messages
|
||||
from ..mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, write_state_on_attr_change
|
||||
from ..models import ReceiveMessage
|
||||
from ..util import valid_publish_topic
|
||||
from .const import MQTT_VACUUM_ATTRIBUTES_BLOCKED
|
||||
from .schema import MQTT_VACUUM_SCHEMA, services_to_strings, strings_to_services
|
||||
|
||||
SERVICE_TO_STRING: dict[VacuumEntityFeature, str] = {
|
||||
VacuumEntityFeature.START: "start",
|
||||
VacuumEntityFeature.PAUSE: "pause",
|
||||
VacuumEntityFeature.STOP: "stop",
|
||||
VacuumEntityFeature.RETURN_HOME: "return_home",
|
||||
VacuumEntityFeature.FAN_SPEED: "fan_speed",
|
||||
VacuumEntityFeature.BATTERY: "battery",
|
||||
VacuumEntityFeature.STATUS: "status",
|
||||
VacuumEntityFeature.SEND_COMMAND: "send_command",
|
||||
VacuumEntityFeature.LOCATE: "locate",
|
||||
VacuumEntityFeature.CLEAN_SPOT: "clean_spot",
|
||||
}
|
||||
|
||||
STRING_TO_SERVICE = {v: k for k, v in SERVICE_TO_STRING.items()}
|
||||
|
||||
|
||||
DEFAULT_SERVICES = (
|
||||
VacuumEntityFeature.START
|
||||
| VacuumEntityFeature.STOP
|
||||
| VacuumEntityFeature.RETURN_HOME
|
||||
| VacuumEntityFeature.BATTERY
|
||||
| VacuumEntityFeature.CLEAN_SPOT
|
||||
)
|
||||
ALL_SERVICES = (
|
||||
DEFAULT_SERVICES
|
||||
| VacuumEntityFeature.PAUSE
|
||||
| VacuumEntityFeature.LOCATE
|
||||
| VacuumEntityFeature.FAN_SPEED
|
||||
| VacuumEntityFeature.SEND_COMMAND
|
||||
from .debug_info import log_messages
|
||||
from .mixins import (
|
||||
MQTT_ENTITY_COMMON_SCHEMA,
|
||||
MqttEntity,
|
||||
async_setup_entity_entry_helper,
|
||||
write_state_on_attr_change,
|
||||
)
|
||||
from .models import ReceiveMessage
|
||||
from .util import valid_publish_topic
|
||||
|
||||
LEGACY = "legacy"
|
||||
STATE = "state"
|
||||
|
||||
BATTERY = "battery_level"
|
||||
FAN_SPEED = "fan_speed"
|
||||
|
@ -102,7 +90,7 @@ CONF_SEND_COMMAND_TOPIC = "send_command_topic"
|
|||
|
||||
DEFAULT_NAME = "MQTT State Vacuum"
|
||||
DEFAULT_RETAIN = False
|
||||
DEFAULT_SERVICE_STRINGS = services_to_strings(DEFAULT_SERVICES, SERVICE_TO_STRING)
|
||||
|
||||
DEFAULT_PAYLOAD_RETURN_TO_BASE = "return_to_base"
|
||||
DEFAULT_PAYLOAD_STOP = "stop"
|
||||
DEFAULT_PAYLOAD_CLEAN_SPOT = "clean_spot"
|
||||
|
@ -110,6 +98,52 @@ DEFAULT_PAYLOAD_LOCATE = "locate"
|
|||
DEFAULT_PAYLOAD_START = "start"
|
||||
DEFAULT_PAYLOAD_PAUSE = "pause"
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
SERVICE_TO_STRING: dict[VacuumEntityFeature, str] = {
|
||||
VacuumEntityFeature.START: "start",
|
||||
VacuumEntityFeature.PAUSE: "pause",
|
||||
VacuumEntityFeature.STOP: "stop",
|
||||
VacuumEntityFeature.RETURN_HOME: "return_home",
|
||||
VacuumEntityFeature.FAN_SPEED: "fan_speed",
|
||||
VacuumEntityFeature.BATTERY: "battery",
|
||||
VacuumEntityFeature.STATUS: "status",
|
||||
VacuumEntityFeature.SEND_COMMAND: "send_command",
|
||||
VacuumEntityFeature.LOCATE: "locate",
|
||||
VacuumEntityFeature.CLEAN_SPOT: "clean_spot",
|
||||
}
|
||||
|
||||
STRING_TO_SERVICE = {v: k for k, v in SERVICE_TO_STRING.items()}
|
||||
DEFAULT_SERVICES = (
|
||||
VacuumEntityFeature.START
|
||||
| VacuumEntityFeature.STOP
|
||||
| VacuumEntityFeature.RETURN_HOME
|
||||
| VacuumEntityFeature.BATTERY
|
||||
| VacuumEntityFeature.CLEAN_SPOT
|
||||
)
|
||||
ALL_SERVICES = (
|
||||
DEFAULT_SERVICES
|
||||
| VacuumEntityFeature.PAUSE
|
||||
| VacuumEntityFeature.LOCATE
|
||||
| VacuumEntityFeature.FAN_SPEED
|
||||
| VacuumEntityFeature.SEND_COMMAND
|
||||
)
|
||||
|
||||
|
||||
def services_to_strings(
|
||||
services: VacuumEntityFeature,
|
||||
service_to_string: dict[VacuumEntityFeature, str],
|
||||
) -> list[str]:
|
||||
"""Convert SUPPORT_* service bitmask to list of service strings."""
|
||||
return [
|
||||
service_to_string[service]
|
||||
for service in service_to_string
|
||||
if service & services
|
||||
]
|
||||
|
||||
|
||||
DEFAULT_SERVICE_STRINGS = services_to_strings(DEFAULT_SERVICES, SERVICE_TO_STRING)
|
||||
|
||||
_FEATURE_PAYLOADS = {
|
||||
VacuumEntityFeature.START: CONF_PAYLOAD_START,
|
||||
VacuumEntityFeature.STOP: CONF_PAYLOAD_STOP,
|
||||
|
@ -119,40 +153,105 @@ _FEATURE_PAYLOADS = {
|
|||
VacuumEntityFeature.RETURN_HOME: CONF_PAYLOAD_RETURN_TO_BASE,
|
||||
}
|
||||
|
||||
PLATFORM_SCHEMA_STATE_MODERN = (
|
||||
MQTT_BASE_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional(CONF_FAN_SPEED_LIST, default=[]): vol.All(
|
||||
cv.ensure_list, [cv.string]
|
||||
),
|
||||
vol.Optional(CONF_NAME): vol.Any(cv.string, None),
|
||||
vol.Optional(
|
||||
CONF_PAYLOAD_CLEAN_SPOT, default=DEFAULT_PAYLOAD_CLEAN_SPOT
|
||||
): cv.string,
|
||||
vol.Optional(
|
||||
CONF_PAYLOAD_LOCATE, default=DEFAULT_PAYLOAD_LOCATE
|
||||
): cv.string,
|
||||
vol.Optional(
|
||||
CONF_PAYLOAD_RETURN_TO_BASE, default=DEFAULT_PAYLOAD_RETURN_TO_BASE
|
||||
): cv.string,
|
||||
vol.Optional(CONF_PAYLOAD_START, default=DEFAULT_PAYLOAD_START): cv.string,
|
||||
vol.Optional(CONF_PAYLOAD_PAUSE, default=DEFAULT_PAYLOAD_PAUSE): cv.string,
|
||||
vol.Optional(CONF_PAYLOAD_STOP, default=DEFAULT_PAYLOAD_STOP): cv.string,
|
||||
vol.Optional(CONF_SEND_COMMAND_TOPIC): valid_publish_topic,
|
||||
vol.Optional(CONF_SET_FAN_SPEED_TOPIC): valid_publish_topic,
|
||||
vol.Optional(CONF_STATE_TOPIC): valid_publish_topic,
|
||||
vol.Optional(
|
||||
CONF_SUPPORTED_FEATURES, default=DEFAULT_SERVICE_STRINGS
|
||||
): vol.All(cv.ensure_list, [vol.In(STRING_TO_SERVICE.keys())]),
|
||||
vol.Optional(CONF_COMMAND_TOPIC): valid_publish_topic,
|
||||
vol.Optional(CONF_RETAIN, default=DEFAULT_RETAIN): cv.boolean,
|
||||
}
|
||||
)
|
||||
.extend(MQTT_ENTITY_COMMON_SCHEMA.schema)
|
||||
.extend(MQTT_VACUUM_SCHEMA.schema)
|
||||
MQTT_VACUUM_ATTRIBUTES_BLOCKED = frozenset(
|
||||
{
|
||||
vacuum.ATTR_BATTERY_ICON,
|
||||
vacuum.ATTR_BATTERY_LEVEL,
|
||||
vacuum.ATTR_FAN_SPEED,
|
||||
}
|
||||
)
|
||||
|
||||
DISCOVERY_SCHEMA_STATE = PLATFORM_SCHEMA_STATE_MODERN.extend({}, extra=vol.REMOVE_EXTRA)
|
||||
MQTT_VACUUM_DOCS_URL = "https://www.home-assistant.io/integrations/vacuum.mqtt/"
|
||||
|
||||
|
||||
def _fail_legacy_config(discovery: bool) -> Callable[[ConfigType], ConfigType]:
|
||||
@callback
|
||||
def _fail_legacy_config_callback(config: ConfigType) -> ConfigType:
|
||||
"""Fail the legacy schema."""
|
||||
if CONF_SCHEMA not in config:
|
||||
return config
|
||||
|
||||
if config[CONF_SCHEMA] == "legacy":
|
||||
raise vol.Invalid(
|
||||
"The support for the `legacy` MQTT vacuum schema has been removed"
|
||||
)
|
||||
|
||||
if discovery:
|
||||
return config
|
||||
|
||||
translation_key = "deprecation_mqtt_schema_vacuum_yaml"
|
||||
hass = async_get_hass()
|
||||
async_create_issue(
|
||||
hass,
|
||||
DOMAIN,
|
||||
translation_key,
|
||||
breaks_in_ha_version="2024.8.0",
|
||||
is_fixable=False,
|
||||
translation_key=translation_key,
|
||||
learn_more_url=MQTT_VACUUM_DOCS_URL,
|
||||
severity=IssueSeverity.WARNING,
|
||||
)
|
||||
return config
|
||||
|
||||
return _fail_legacy_config_callback
|
||||
|
||||
|
||||
VACUUM_BASE_SCHEMA = MQTT_BASE_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional(CONF_FAN_SPEED_LIST, default=[]): vol.All(
|
||||
cv.ensure_list, [cv.string]
|
||||
),
|
||||
vol.Optional(CONF_NAME): vol.Any(cv.string, None),
|
||||
vol.Optional(
|
||||
CONF_PAYLOAD_CLEAN_SPOT, default=DEFAULT_PAYLOAD_CLEAN_SPOT
|
||||
): cv.string,
|
||||
vol.Optional(CONF_PAYLOAD_LOCATE, default=DEFAULT_PAYLOAD_LOCATE): cv.string,
|
||||
vol.Optional(
|
||||
CONF_PAYLOAD_RETURN_TO_BASE, default=DEFAULT_PAYLOAD_RETURN_TO_BASE
|
||||
): cv.string,
|
||||
vol.Optional(CONF_PAYLOAD_START, default=DEFAULT_PAYLOAD_START): cv.string,
|
||||
vol.Optional(CONF_PAYLOAD_PAUSE, default=DEFAULT_PAYLOAD_PAUSE): cv.string,
|
||||
vol.Optional(CONF_PAYLOAD_STOP, default=DEFAULT_PAYLOAD_STOP): cv.string,
|
||||
vol.Optional(CONF_SEND_COMMAND_TOPIC): valid_publish_topic,
|
||||
vol.Optional(CONF_SET_FAN_SPEED_TOPIC): valid_publish_topic,
|
||||
vol.Optional(CONF_STATE_TOPIC): valid_publish_topic,
|
||||
vol.Optional(CONF_SUPPORTED_FEATURES, default=DEFAULT_SERVICE_STRINGS): vol.All(
|
||||
cv.ensure_list, [vol.In(STRING_TO_SERVICE.keys())]
|
||||
),
|
||||
vol.Optional(CONF_COMMAND_TOPIC): valid_publish_topic,
|
||||
vol.Optional(CONF_RETAIN, default=DEFAULT_RETAIN): cv.boolean,
|
||||
vol.Optional(CONF_SCHEMA): vol.All(vol.Lower, vol.Any(LEGACY, STATE)),
|
||||
}
|
||||
).extend(MQTT_ENTITY_COMMON_SCHEMA.schema)
|
||||
|
||||
DISCOVERY_SCHEMA = vol.All(
|
||||
_fail_legacy_config(discovery=True),
|
||||
VACUUM_BASE_SCHEMA.extend({}, extra=vol.ALLOW_EXTRA),
|
||||
cv.deprecated(CONF_SCHEMA),
|
||||
)
|
||||
|
||||
PLATFORM_SCHEMA_MODERN = vol.All(
|
||||
_fail_legacy_config(discovery=False),
|
||||
VACUUM_BASE_SCHEMA,
|
||||
cv.deprecated(CONF_SCHEMA),
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up MQTT vacuum through YAML and through MQTT discovery."""
|
||||
await async_setup_entity_entry_helper(
|
||||
hass,
|
||||
config_entry,
|
||||
MqttStateVacuum,
|
||||
vacuum.DOMAIN,
|
||||
async_add_entities,
|
||||
DISCOVERY_SCHEMA,
|
||||
PLATFORM_SCHEMA_MODERN,
|
||||
)
|
||||
|
||||
|
||||
class MqttStateVacuum(MqttEntity, StateVacuumEntity):
|
||||
|
@ -182,12 +281,22 @@ class MqttStateVacuum(MqttEntity, StateVacuumEntity):
|
|||
@staticmethod
|
||||
def config_schema() -> vol.Schema:
|
||||
"""Return the config schema."""
|
||||
return DISCOVERY_SCHEMA_STATE
|
||||
return DISCOVERY_SCHEMA
|
||||
|
||||
def _setup_from_config(self, config: ConfigType) -> None:
|
||||
"""(Re)Setup the entity."""
|
||||
|
||||
def _strings_to_services(
|
||||
strings: list[str], string_to_service: dict[str, VacuumEntityFeature]
|
||||
) -> VacuumEntityFeature:
|
||||
"""Convert service strings to SUPPORT_* service bitmask."""
|
||||
services = VacuumEntityFeature.STATE
|
||||
for string in strings:
|
||||
services |= string_to_service[string]
|
||||
return services
|
||||
|
||||
supported_feature_strings: list[str] = config[CONF_SUPPORTED_FEATURES]
|
||||
self._attr_supported_features = VacuumEntityFeature.STATE | strings_to_services(
|
||||
self._attr_supported_features = _strings_to_services(
|
||||
supported_feature_strings, STRING_TO_SERVICE
|
||||
)
|
||||
self._attr_fan_speed_list = config[CONF_FAN_SPEED_LIST]
|
|
@ -1,122 +0,0 @@
|
|||
"""Support for MQTT vacuums."""
|
||||
|
||||
# The legacy schema for MQTT vacuum was deprecated with HA Core 2023.8.0
|
||||
# and will be removed with HA Core 2024.2.0
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components import vacuum
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, async_get_hass, callback
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
from ..const import DOMAIN
|
||||
from ..mixins import async_setup_entity_entry_helper
|
||||
from .schema import CONF_SCHEMA, LEGACY, MQTT_VACUUM_SCHEMA, STATE
|
||||
from .schema_legacy import (
|
||||
DISCOVERY_SCHEMA_LEGACY,
|
||||
PLATFORM_SCHEMA_LEGACY_MODERN,
|
||||
MqttVacuum,
|
||||
)
|
||||
from .schema_state import (
|
||||
DISCOVERY_SCHEMA_STATE,
|
||||
PLATFORM_SCHEMA_STATE_MODERN,
|
||||
MqttStateVacuum,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
MQTT_VACUUM_DOCS_URL = "https://www.home-assistant.io/integrations/vacuum.mqtt/"
|
||||
|
||||
|
||||
# The legacy schema for MQTT vacuum was deprecated with HA Core 2023.8.0
|
||||
# and will be removed with HA Core 2024.2.0
|
||||
def warn_for_deprecation_legacy_schema(
|
||||
hass: HomeAssistant, config: ConfigType, discovery: bool
|
||||
) -> None:
|
||||
"""Warn for deprecation of legacy schema."""
|
||||
if config[CONF_SCHEMA] == STATE:
|
||||
return
|
||||
|
||||
key_suffix = "discovery" if discovery else "yaml"
|
||||
translation_key = f"deprecation_mqtt_legacy_vacuum_{key_suffix}"
|
||||
async_create_issue(
|
||||
hass,
|
||||
DOMAIN,
|
||||
translation_key,
|
||||
breaks_in_ha_version="2024.2.0",
|
||||
is_fixable=False,
|
||||
translation_key=translation_key,
|
||||
learn_more_url=MQTT_VACUUM_DOCS_URL,
|
||||
severity=IssueSeverity.WARNING,
|
||||
)
|
||||
_LOGGER.warning(
|
||||
"Deprecated `legacy` schema detected for MQTT vacuum, expected `state` schema, config found: %s",
|
||||
config,
|
||||
)
|
||||
|
||||
|
||||
@callback
|
||||
def validate_mqtt_vacuum_discovery(config_value: ConfigType) -> ConfigType:
|
||||
"""Validate MQTT vacuum schema."""
|
||||
|
||||
# The legacy schema for MQTT vacuum was deprecated with HA Core 2023.8.0
|
||||
# and will be removed with HA Core 2024.2.0
|
||||
|
||||
schemas = {LEGACY: DISCOVERY_SCHEMA_LEGACY, STATE: DISCOVERY_SCHEMA_STATE}
|
||||
config: ConfigType = schemas[config_value[CONF_SCHEMA]](config_value)
|
||||
hass = async_get_hass()
|
||||
warn_for_deprecation_legacy_schema(hass, config, True)
|
||||
return config
|
||||
|
||||
|
||||
@callback
|
||||
def validate_mqtt_vacuum_modern(config_value: ConfigType) -> ConfigType:
|
||||
"""Validate MQTT vacuum modern schema."""
|
||||
|
||||
# The legacy schema for MQTT vacuum was deprecated with HA Core 2023.8.0
|
||||
# and will be removed with HA Core 2024.2.0
|
||||
|
||||
schemas = {
|
||||
LEGACY: PLATFORM_SCHEMA_LEGACY_MODERN,
|
||||
STATE: PLATFORM_SCHEMA_STATE_MODERN,
|
||||
}
|
||||
config: ConfigType = schemas[config_value[CONF_SCHEMA]](config_value)
|
||||
# The legacy schema for MQTT vacuum was deprecated with HA Core 2023.8.0
|
||||
# and will be removed with HA Core 2024.2.0
|
||||
hass = async_get_hass()
|
||||
warn_for_deprecation_legacy_schema(hass, config, False)
|
||||
return config
|
||||
|
||||
|
||||
DISCOVERY_SCHEMA = vol.All(
|
||||
MQTT_VACUUM_SCHEMA.extend({}, extra=vol.ALLOW_EXTRA), validate_mqtt_vacuum_discovery
|
||||
)
|
||||
|
||||
PLATFORM_SCHEMA_MODERN = vol.All(
|
||||
MQTT_VACUUM_SCHEMA.extend({}, extra=vol.ALLOW_EXTRA), validate_mqtt_vacuum_modern
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up MQTT vacuum through YAML and through MQTT discovery."""
|
||||
await async_setup_entity_entry_helper(
|
||||
hass,
|
||||
config_entry,
|
||||
None,
|
||||
vacuum.DOMAIN,
|
||||
async_add_entities,
|
||||
DISCOVERY_SCHEMA,
|
||||
PLATFORM_SCHEMA_MODERN,
|
||||
{"legacy": MqttVacuum, "state": MqttStateVacuum},
|
||||
)
|
|
@ -1,10 +0,0 @@
|
|||
"""Shared constants."""
|
||||
from homeassistant.components import vacuum
|
||||
|
||||
MQTT_VACUUM_ATTRIBUTES_BLOCKED = frozenset(
|
||||
{
|
||||
vacuum.ATTR_BATTERY_ICON,
|
||||
vacuum.ATTR_BATTERY_LEVEL,
|
||||
vacuum.ATTR_FAN_SPEED,
|
||||
}
|
||||
)
|
|
@ -1,41 +0,0 @@
|
|||
"""Shared schema code."""
|
||||
from __future__ import annotations
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.vacuum import VacuumEntityFeature
|
||||
|
||||
from ..const import CONF_SCHEMA
|
||||
|
||||
LEGACY = "legacy"
|
||||
STATE = "state"
|
||||
|
||||
MQTT_VACUUM_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Optional(CONF_SCHEMA, default=LEGACY): vol.All(
|
||||
vol.Lower, vol.Any(LEGACY, STATE)
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def services_to_strings(
|
||||
services: VacuumEntityFeature,
|
||||
service_to_string: dict[VacuumEntityFeature, str],
|
||||
) -> list[str]:
|
||||
"""Convert SUPPORT_* service bitmask to list of service strings."""
|
||||
return [
|
||||
service_to_string[service]
|
||||
for service in service_to_string
|
||||
if service & services
|
||||
]
|
||||
|
||||
|
||||
def strings_to_services(
|
||||
strings: list[str], string_to_service: dict[str, VacuumEntityFeature]
|
||||
) -> VacuumEntityFeature:
|
||||
"""Convert service strings to SUPPORT_* service bitmask."""
|
||||
services = VacuumEntityFeature(0)
|
||||
for string in strings:
|
||||
services |= string_to_service[string]
|
||||
return services
|
|
@ -1,496 +0,0 @@
|
|||
"""Support for Legacy MQTT vacuum.
|
||||
|
||||
The legacy schema for MQTT vacuum was deprecated with HA Core 2023.8.0
|
||||
and is will be removed with HA Core 2024.2.0
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from typing import Any
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.vacuum import (
|
||||
ATTR_STATUS,
|
||||
ENTITY_ID_FORMAT,
|
||||
VacuumEntity,
|
||||
VacuumEntityFeature,
|
||||
)
|
||||
from homeassistant.const import ATTR_SUPPORTED_FEATURES, CONF_NAME
|
||||
from homeassistant.core import callback
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.icon import icon_for_battery_level
|
||||
from homeassistant.helpers.json import json_dumps
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
from .. import subscription
|
||||
from ..config import MQTT_BASE_SCHEMA
|
||||
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, write_state_on_attr_change
|
||||
from ..models import (
|
||||
MqttValueTemplate,
|
||||
PayloadSentinel,
|
||||
ReceiveMessage,
|
||||
ReceivePayloadType,
|
||||
)
|
||||
from ..util import valid_publish_topic
|
||||
from .const import MQTT_VACUUM_ATTRIBUTES_BLOCKED
|
||||
from .schema import MQTT_VACUUM_SCHEMA, services_to_strings, strings_to_services
|
||||
|
||||
SERVICE_TO_STRING = {
|
||||
VacuumEntityFeature.TURN_ON: "turn_on",
|
||||
VacuumEntityFeature.TURN_OFF: "turn_off",
|
||||
VacuumEntityFeature.PAUSE: "pause",
|
||||
VacuumEntityFeature.STOP: "stop",
|
||||
VacuumEntityFeature.RETURN_HOME: "return_home",
|
||||
VacuumEntityFeature.FAN_SPEED: "fan_speed",
|
||||
VacuumEntityFeature.BATTERY: "battery",
|
||||
VacuumEntityFeature.STATUS: "status",
|
||||
VacuumEntityFeature.SEND_COMMAND: "send_command",
|
||||
VacuumEntityFeature.LOCATE: "locate",
|
||||
VacuumEntityFeature.CLEAN_SPOT: "clean_spot",
|
||||
}
|
||||
|
||||
STRING_TO_SERVICE = {v: k for k, v in SERVICE_TO_STRING.items()}
|
||||
|
||||
DEFAULT_SERVICES = (
|
||||
VacuumEntityFeature.TURN_ON
|
||||
| VacuumEntityFeature.TURN_OFF
|
||||
| VacuumEntityFeature.STOP
|
||||
| VacuumEntityFeature.RETURN_HOME
|
||||
| VacuumEntityFeature.STATUS
|
||||
| VacuumEntityFeature.BATTERY
|
||||
| VacuumEntityFeature.CLEAN_SPOT
|
||||
)
|
||||
ALL_SERVICES = (
|
||||
DEFAULT_SERVICES
|
||||
| VacuumEntityFeature.PAUSE
|
||||
| VacuumEntityFeature.LOCATE
|
||||
| VacuumEntityFeature.FAN_SPEED
|
||||
| VacuumEntityFeature.SEND_COMMAND
|
||||
)
|
||||
|
||||
CONF_SUPPORTED_FEATURES = ATTR_SUPPORTED_FEATURES
|
||||
CONF_BATTERY_LEVEL_TEMPLATE = "battery_level_template"
|
||||
CONF_BATTERY_LEVEL_TOPIC = "battery_level_topic"
|
||||
CONF_CHARGING_TEMPLATE = "charging_template"
|
||||
CONF_CHARGING_TOPIC = "charging_topic"
|
||||
CONF_CLEANING_TEMPLATE = "cleaning_template"
|
||||
CONF_CLEANING_TOPIC = "cleaning_topic"
|
||||
CONF_DOCKED_TEMPLATE = "docked_template"
|
||||
CONF_DOCKED_TOPIC = "docked_topic"
|
||||
CONF_ERROR_TEMPLATE = "error_template"
|
||||
CONF_ERROR_TOPIC = "error_topic"
|
||||
CONF_FAN_SPEED_LIST = "fan_speed_list"
|
||||
CONF_FAN_SPEED_TEMPLATE = "fan_speed_template"
|
||||
CONF_FAN_SPEED_TOPIC = "fan_speed_topic"
|
||||
CONF_PAYLOAD_CLEAN_SPOT = "payload_clean_spot"
|
||||
CONF_PAYLOAD_LOCATE = "payload_locate"
|
||||
CONF_PAYLOAD_RETURN_TO_BASE = "payload_return_to_base"
|
||||
CONF_PAYLOAD_START_PAUSE = "payload_start_pause"
|
||||
CONF_PAYLOAD_STOP = "payload_stop"
|
||||
CONF_PAYLOAD_TURN_OFF = "payload_turn_off"
|
||||
CONF_PAYLOAD_TURN_ON = "payload_turn_on"
|
||||
CONF_SEND_COMMAND_TOPIC = "send_command_topic"
|
||||
CONF_SET_FAN_SPEED_TOPIC = "set_fan_speed_topic"
|
||||
|
||||
DEFAULT_NAME = "MQTT Vacuum"
|
||||
DEFAULT_PAYLOAD_CLEAN_SPOT = "clean_spot"
|
||||
DEFAULT_PAYLOAD_LOCATE = "locate"
|
||||
DEFAULT_PAYLOAD_RETURN_TO_BASE = "return_to_base"
|
||||
DEFAULT_PAYLOAD_START_PAUSE = "start_pause"
|
||||
DEFAULT_PAYLOAD_STOP = "stop"
|
||||
DEFAULT_PAYLOAD_TURN_OFF = "turn_off"
|
||||
DEFAULT_PAYLOAD_TURN_ON = "turn_on"
|
||||
DEFAULT_RETAIN = False
|
||||
DEFAULT_SERVICE_STRINGS = services_to_strings(DEFAULT_SERVICES, SERVICE_TO_STRING)
|
||||
|
||||
MQTT_LEGACY_VACUUM_ATTRIBUTES_BLOCKED = MQTT_VACUUM_ATTRIBUTES_BLOCKED | frozenset(
|
||||
{ATTR_STATUS}
|
||||
)
|
||||
|
||||
PLATFORM_SCHEMA_LEGACY_MODERN = (
|
||||
MQTT_BASE_SCHEMA.extend(
|
||||
{
|
||||
vol.Inclusive(CONF_BATTERY_LEVEL_TEMPLATE, "battery"): cv.template,
|
||||
vol.Inclusive(CONF_BATTERY_LEVEL_TOPIC, "battery"): valid_publish_topic,
|
||||
vol.Inclusive(CONF_CHARGING_TEMPLATE, "charging"): cv.template,
|
||||
vol.Inclusive(CONF_CHARGING_TOPIC, "charging"): valid_publish_topic,
|
||||
vol.Inclusive(CONF_CLEANING_TEMPLATE, "cleaning"): cv.template,
|
||||
vol.Inclusive(CONF_CLEANING_TOPIC, "cleaning"): valid_publish_topic,
|
||||
vol.Inclusive(CONF_DOCKED_TEMPLATE, "docked"): cv.template,
|
||||
vol.Inclusive(CONF_DOCKED_TOPIC, "docked"): valid_publish_topic,
|
||||
vol.Inclusive(CONF_ERROR_TEMPLATE, "error"): cv.template,
|
||||
vol.Inclusive(CONF_ERROR_TOPIC, "error"): valid_publish_topic,
|
||||
vol.Optional(CONF_FAN_SPEED_LIST, default=[]): vol.All(
|
||||
cv.ensure_list, [cv.string]
|
||||
),
|
||||
vol.Inclusive(CONF_FAN_SPEED_TEMPLATE, "fan_speed"): cv.template,
|
||||
vol.Inclusive(CONF_FAN_SPEED_TOPIC, "fan_speed"): valid_publish_topic,
|
||||
vol.Optional(CONF_NAME): vol.Any(cv.string, None),
|
||||
vol.Optional(
|
||||
CONF_PAYLOAD_CLEAN_SPOT, default=DEFAULT_PAYLOAD_CLEAN_SPOT
|
||||
): cv.string,
|
||||
vol.Optional(
|
||||
CONF_PAYLOAD_LOCATE, default=DEFAULT_PAYLOAD_LOCATE
|
||||
): cv.string,
|
||||
vol.Optional(
|
||||
CONF_PAYLOAD_RETURN_TO_BASE, default=DEFAULT_PAYLOAD_RETURN_TO_BASE
|
||||
): cv.string,
|
||||
vol.Optional(
|
||||
CONF_PAYLOAD_START_PAUSE, default=DEFAULT_PAYLOAD_START_PAUSE
|
||||
): cv.string,
|
||||
vol.Optional(CONF_PAYLOAD_STOP, default=DEFAULT_PAYLOAD_STOP): cv.string,
|
||||
vol.Optional(
|
||||
CONF_PAYLOAD_TURN_OFF, default=DEFAULT_PAYLOAD_TURN_OFF
|
||||
): cv.string,
|
||||
vol.Optional(
|
||||
CONF_PAYLOAD_TURN_ON, default=DEFAULT_PAYLOAD_TURN_ON
|
||||
): cv.string,
|
||||
vol.Optional(CONF_SEND_COMMAND_TOPIC): valid_publish_topic,
|
||||
vol.Optional(CONF_SET_FAN_SPEED_TOPIC): valid_publish_topic,
|
||||
vol.Optional(
|
||||
CONF_SUPPORTED_FEATURES, default=DEFAULT_SERVICE_STRINGS
|
||||
): vol.All(cv.ensure_list, [vol.In(STRING_TO_SERVICE.keys())]),
|
||||
vol.Optional(CONF_COMMAND_TOPIC): valid_publish_topic,
|
||||
vol.Optional(CONF_RETAIN, default=DEFAULT_RETAIN): cv.boolean,
|
||||
}
|
||||
)
|
||||
.extend(MQTT_ENTITY_COMMON_SCHEMA.schema)
|
||||
.extend(MQTT_VACUUM_SCHEMA.schema)
|
||||
)
|
||||
|
||||
DISCOVERY_SCHEMA_LEGACY = PLATFORM_SCHEMA_LEGACY_MODERN.extend(
|
||||
{}, extra=vol.REMOVE_EXTRA
|
||||
)
|
||||
|
||||
|
||||
_COMMANDS = {
|
||||
VacuumEntityFeature.TURN_ON: {
|
||||
"payload": CONF_PAYLOAD_TURN_ON,
|
||||
"status": "Cleaning",
|
||||
},
|
||||
VacuumEntityFeature.TURN_OFF: {
|
||||
"payload": CONF_PAYLOAD_TURN_OFF,
|
||||
"status": "Turning Off",
|
||||
},
|
||||
VacuumEntityFeature.STOP: {
|
||||
"payload": CONF_PAYLOAD_STOP,
|
||||
"status": "Stopping the current task",
|
||||
},
|
||||
VacuumEntityFeature.CLEAN_SPOT: {
|
||||
"payload": CONF_PAYLOAD_CLEAN_SPOT,
|
||||
"status": "Cleaning spot",
|
||||
},
|
||||
VacuumEntityFeature.LOCATE: {
|
||||
"payload": CONF_PAYLOAD_LOCATE,
|
||||
"status": "Hi, I'm over here!",
|
||||
},
|
||||
VacuumEntityFeature.PAUSE: {
|
||||
"payload": CONF_PAYLOAD_START_PAUSE,
|
||||
"status": "Pausing/Resuming cleaning...",
|
||||
},
|
||||
VacuumEntityFeature.RETURN_HOME: {
|
||||
"payload": CONF_PAYLOAD_RETURN_TO_BASE,
|
||||
"status": "Returning home...",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class MqttVacuum(MqttEntity, VacuumEntity):
|
||||
"""Representation of a MQTT-controlled legacy vacuum."""
|
||||
|
||||
_attr_battery_level = 0
|
||||
_attr_is_on = False
|
||||
_attributes_extra_blocked = MQTT_LEGACY_VACUUM_ATTRIBUTES_BLOCKED
|
||||
_charging: bool = False
|
||||
_cleaning: bool = False
|
||||
_command_topic: str | None
|
||||
_docked: bool = False
|
||||
_default_name = DEFAULT_NAME
|
||||
_entity_id_format = ENTITY_ID_FORMAT
|
||||
_encoding: str | None
|
||||
_error: str | None = None
|
||||
_qos: bool
|
||||
_retain: bool
|
||||
_payloads: dict[str, str]
|
||||
_send_command_topic: str | None
|
||||
_set_fan_speed_topic: str | None
|
||||
_state_topics: dict[str, str | None]
|
||||
_templates: dict[
|
||||
str, Callable[[ReceivePayloadType, PayloadSentinel], ReceivePayloadType]
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def config_schema() -> vol.Schema:
|
||||
"""Return the config schema."""
|
||||
return DISCOVERY_SCHEMA_LEGACY
|
||||
|
||||
def _setup_from_config(self, config: ConfigType) -> None:
|
||||
"""(Re)Setup the entity."""
|
||||
supported_feature_strings = config[CONF_SUPPORTED_FEATURES]
|
||||
self._attr_supported_features = strings_to_services(
|
||||
supported_feature_strings, STRING_TO_SERVICE
|
||||
)
|
||||
self._attr_fan_speed_list = config[CONF_FAN_SPEED_LIST]
|
||||
self._qos = config[CONF_QOS]
|
||||
self._retain = config[CONF_RETAIN]
|
||||
self._encoding = config[CONF_ENCODING] or None
|
||||
|
||||
self._command_topic = config.get(CONF_COMMAND_TOPIC)
|
||||
self._set_fan_speed_topic = config.get(CONF_SET_FAN_SPEED_TOPIC)
|
||||
self._send_command_topic = config.get(CONF_SEND_COMMAND_TOPIC)
|
||||
|
||||
self._payloads = {
|
||||
key: config[key]
|
||||
for key in (
|
||||
CONF_PAYLOAD_TURN_ON,
|
||||
CONF_PAYLOAD_TURN_OFF,
|
||||
CONF_PAYLOAD_RETURN_TO_BASE,
|
||||
CONF_PAYLOAD_STOP,
|
||||
CONF_PAYLOAD_CLEAN_SPOT,
|
||||
CONF_PAYLOAD_LOCATE,
|
||||
CONF_PAYLOAD_START_PAUSE,
|
||||
)
|
||||
}
|
||||
self._state_topics = {
|
||||
key: config.get(key)
|
||||
for key in (
|
||||
CONF_BATTERY_LEVEL_TOPIC,
|
||||
CONF_CHARGING_TOPIC,
|
||||
CONF_CLEANING_TOPIC,
|
||||
CONF_DOCKED_TOPIC,
|
||||
CONF_ERROR_TOPIC,
|
||||
CONF_FAN_SPEED_TOPIC,
|
||||
)
|
||||
}
|
||||
self._templates = {
|
||||
key: MqttValueTemplate(
|
||||
config[key], entity=self
|
||||
).async_render_with_possible_json_value
|
||||
for key in (
|
||||
CONF_BATTERY_LEVEL_TEMPLATE,
|
||||
CONF_CHARGING_TEMPLATE,
|
||||
CONF_CLEANING_TEMPLATE,
|
||||
CONF_DOCKED_TEMPLATE,
|
||||
CONF_ERROR_TEMPLATE,
|
||||
CONF_FAN_SPEED_TEMPLATE,
|
||||
)
|
||||
if key in config
|
||||
}
|
||||
|
||||
def _prepare_subscribe_topics(self) -> None:
|
||||
"""(Re)Subscribe to topics."""
|
||||
|
||||
@callback
|
||||
@log_messages(self.hass, self.entity_id)
|
||||
@write_state_on_attr_change(
|
||||
self,
|
||||
{
|
||||
"_attr_battery_level",
|
||||
"_attr_fan_speed",
|
||||
"_attr_is_on",
|
||||
# We track _attr_status and _charging as they are used to
|
||||
# To determine the batery_icon.
|
||||
# We do not need to track _docked as it is
|
||||
# not leading to entity changes directly.
|
||||
"_attr_status",
|
||||
"_charging",
|
||||
},
|
||||
)
|
||||
def message_received(msg: ReceiveMessage) -> None:
|
||||
"""Handle new MQTT message."""
|
||||
if (
|
||||
msg.topic == self._state_topics[CONF_BATTERY_LEVEL_TOPIC]
|
||||
and CONF_BATTERY_LEVEL_TEMPLATE in self._config
|
||||
):
|
||||
battery_level = self._templates[CONF_BATTERY_LEVEL_TEMPLATE](
|
||||
msg.payload, PayloadSentinel.DEFAULT
|
||||
)
|
||||
if battery_level and battery_level is not PayloadSentinel.DEFAULT:
|
||||
self._attr_battery_level = max(0, min(100, int(battery_level)))
|
||||
|
||||
if (
|
||||
msg.topic == self._state_topics[CONF_CHARGING_TOPIC]
|
||||
and CONF_CHARGING_TEMPLATE in self._templates
|
||||
):
|
||||
charging = self._templates[CONF_CHARGING_TEMPLATE](
|
||||
msg.payload, PayloadSentinel.DEFAULT
|
||||
)
|
||||
if charging and charging is not PayloadSentinel.DEFAULT:
|
||||
self._charging = cv.boolean(charging)
|
||||
|
||||
if (
|
||||
msg.topic == self._state_topics[CONF_CLEANING_TOPIC]
|
||||
and CONF_CLEANING_TEMPLATE in self._config
|
||||
):
|
||||
cleaning = self._templates[CONF_CLEANING_TEMPLATE](
|
||||
msg.payload, PayloadSentinel.DEFAULT
|
||||
)
|
||||
if cleaning and cleaning is not PayloadSentinel.DEFAULT:
|
||||
self._attr_is_on = cv.boolean(cleaning)
|
||||
|
||||
if (
|
||||
msg.topic == self._state_topics[CONF_DOCKED_TOPIC]
|
||||
and CONF_DOCKED_TEMPLATE in self._config
|
||||
):
|
||||
docked = self._templates[CONF_DOCKED_TEMPLATE](
|
||||
msg.payload, PayloadSentinel.DEFAULT
|
||||
)
|
||||
if docked and docked is not PayloadSentinel.DEFAULT:
|
||||
self._docked = cv.boolean(docked)
|
||||
|
||||
if (
|
||||
msg.topic == self._state_topics[CONF_ERROR_TOPIC]
|
||||
and CONF_ERROR_TEMPLATE in self._config
|
||||
):
|
||||
error = self._templates[CONF_ERROR_TEMPLATE](
|
||||
msg.payload, PayloadSentinel.DEFAULT
|
||||
)
|
||||
if error is not PayloadSentinel.DEFAULT:
|
||||
self._error = cv.string(error)
|
||||
|
||||
if self._docked:
|
||||
if self._charging:
|
||||
self._attr_status = "Docked & Charging"
|
||||
else:
|
||||
self._attr_status = "Docked"
|
||||
elif self.is_on:
|
||||
self._attr_status = "Cleaning"
|
||||
elif self._error:
|
||||
self._attr_status = f"Error: {self._error}"
|
||||
else:
|
||||
self._attr_status = "Stopped"
|
||||
|
||||
if (
|
||||
msg.topic == self._state_topics[CONF_FAN_SPEED_TOPIC]
|
||||
and CONF_FAN_SPEED_TEMPLATE in self._config
|
||||
):
|
||||
fan_speed = self._templates[CONF_FAN_SPEED_TEMPLATE](
|
||||
msg.payload, PayloadSentinel.DEFAULT
|
||||
)
|
||||
if fan_speed and fan_speed is not PayloadSentinel.DEFAULT:
|
||||
self._attr_fan_speed = str(fan_speed)
|
||||
|
||||
topics_list = {topic for topic in self._state_topics.values() if topic}
|
||||
self._sub_state = subscription.async_prepare_subscribe_topics(
|
||||
self.hass,
|
||||
self._sub_state,
|
||||
{
|
||||
f"topic{i}": {
|
||||
"topic": topic,
|
||||
"msg_callback": message_received,
|
||||
"qos": self._qos,
|
||||
"encoding": self._encoding,
|
||||
}
|
||||
for i, topic in enumerate(topics_list)
|
||||
},
|
||||
)
|
||||
|
||||
async def _subscribe_topics(self) -> None:
|
||||
"""(Re)Subscribe to topics."""
|
||||
await subscription.async_subscribe_topics(self.hass, self._sub_state)
|
||||
|
||||
@property
|
||||
def battery_icon(self) -> str:
|
||||
"""Return the battery icon for the vacuum cleaner.
|
||||
|
||||
No need to check VacuumEntityFeature.BATTERY, this won't be called if
|
||||
battery_level is None.
|
||||
"""
|
||||
return icon_for_battery_level(
|
||||
battery_level=self.battery_level, charging=self._charging
|
||||
)
|
||||
|
||||
async def _async_publish_command(self, feature: VacuumEntityFeature) -> None:
|
||||
"""Publish a command."""
|
||||
|
||||
if self._command_topic is None:
|
||||
return
|
||||
|
||||
await self.async_publish(
|
||||
self._command_topic,
|
||||
self._payloads[_COMMANDS[feature]["payload"]],
|
||||
qos=self._qos,
|
||||
retain=self._retain,
|
||||
encoding=self._encoding,
|
||||
)
|
||||
self._attr_status = _COMMANDS[feature]["status"]
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
"""Turn the vacuum on."""
|
||||
await self._async_publish_command(VacuumEntityFeature.TURN_ON)
|
||||
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
"""Turn the vacuum off."""
|
||||
await self._async_publish_command(VacuumEntityFeature.TURN_OFF)
|
||||
|
||||
async def async_stop(self, **kwargs: Any) -> None:
|
||||
"""Stop the vacuum."""
|
||||
await self._async_publish_command(VacuumEntityFeature.STOP)
|
||||
|
||||
async def async_clean_spot(self, **kwargs: Any) -> None:
|
||||
"""Perform a spot clean-up."""
|
||||
await self._async_publish_command(VacuumEntityFeature.CLEAN_SPOT)
|
||||
|
||||
async def async_locate(self, **kwargs: Any) -> None:
|
||||
"""Locate the vacuum (usually by playing a song)."""
|
||||
await self._async_publish_command(VacuumEntityFeature.LOCATE)
|
||||
|
||||
async def async_start_pause(self, **kwargs: Any) -> None:
|
||||
"""Start, pause or resume the cleaning task."""
|
||||
await self._async_publish_command(VacuumEntityFeature.PAUSE)
|
||||
|
||||
async def async_return_to_base(self, **kwargs: Any) -> None:
|
||||
"""Tell the vacuum to return to its dock."""
|
||||
await self._async_publish_command(VacuumEntityFeature.RETURN_HOME)
|
||||
|
||||
async def async_set_fan_speed(self, fan_speed: str, **kwargs: Any) -> None:
|
||||
"""Set fan speed."""
|
||||
if (
|
||||
self._set_fan_speed_topic is None
|
||||
or (self.supported_features & VacuumEntityFeature.FAN_SPEED == 0)
|
||||
or fan_speed not in self.fan_speed_list
|
||||
):
|
||||
return None
|
||||
|
||||
await self.async_publish(
|
||||
self._set_fan_speed_topic,
|
||||
fan_speed,
|
||||
self._qos,
|
||||
self._retain,
|
||||
self._encoding,
|
||||
)
|
||||
self._attr_status = f"Setting fan to {fan_speed}..."
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_send_command(
|
||||
self,
|
||||
command: str,
|
||||
params: dict[str, Any] | list[Any] | None = None,
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
"""Send a command to a vacuum cleaner."""
|
||||
if (
|
||||
self._send_command_topic is None
|
||||
or self.supported_features & VacuumEntityFeature.SEND_COMMAND == 0
|
||||
):
|
||||
return
|
||||
if params:
|
||||
message: dict[str, Any] = {"command": command}
|
||||
message.update(params)
|
||||
message_payload = json_dumps(message)
|
||||
else:
|
||||
message_payload = command
|
||||
await self.async_publish(
|
||||
self._send_command_topic,
|
||||
message_payload,
|
||||
self._qos,
|
||||
self._retain,
|
||||
self._encoding,
|
||||
)
|
||||
self._attr_status = f"Sending command {message_payload}..."
|
||||
self.async_write_ha_state()
|
|
@ -481,11 +481,11 @@ async def test_discover_alarm_control_panel(
|
|||
"vacuum",
|
||||
),
|
||||
(
|
||||
"homeassistant/vacuum/object/bla/config",
|
||||
'{ "name": "Hello World 17", "obj_id": "hello_id", "state_topic": "test-topic", "schema": "legacy" }',
|
||||
"vacuum.hello_id",
|
||||
"homeassistant/valve/object/bla/config",
|
||||
'{ "name": "Hello World 17", "obj_id": "hello_id", "state_topic": "test-topic" }',
|
||||
"valve.hello_id",
|
||||
"Hello World 17",
|
||||
"vacuum",
|
||||
"valve",
|
||||
),
|
||||
(
|
||||
"homeassistant/lock/object/bla/config",
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -7,13 +7,14 @@ from unittest.mock import patch
|
|||
import pytest
|
||||
|
||||
from homeassistant.components import mqtt, vacuum
|
||||
from homeassistant.components.mqtt import vacuum as mqttvacuum
|
||||
from homeassistant.components.mqtt.const import CONF_COMMAND_TOPIC, CONF_STATE_TOPIC
|
||||
from homeassistant.components.mqtt.vacuum import CONF_SCHEMA, schema_state as mqttvacuum
|
||||
from homeassistant.components.mqtt.vacuum.const import MQTT_VACUUM_ATTRIBUTES_BLOCKED
|
||||
from homeassistant.components.mqtt.vacuum.schema import services_to_strings
|
||||
from homeassistant.components.mqtt.vacuum.schema_state import (
|
||||
from homeassistant.components.mqtt.vacuum import (
|
||||
ALL_SERVICES,
|
||||
CONF_SCHEMA,
|
||||
MQTT_VACUUM_ATTRIBUTES_BLOCKED,
|
||||
SERVICE_TO_STRING,
|
||||
services_to_strings,
|
||||
)
|
||||
from homeassistant.components.vacuum import (
|
||||
ATTR_BATTERY_ICON,
|
||||
|
@ -586,7 +587,7 @@ async def test_discovery_update_unchanged_vacuum(
|
|||
"""Test update of discovered vacuum."""
|
||||
data1 = '{ "schema": "state", "name": "Beer", "command_topic": "test_topic"}'
|
||||
with patch(
|
||||
"homeassistant.components.mqtt.vacuum.schema_state.MqttStateVacuum.discovery_update"
|
||||
"homeassistant.components.mqtt.vacuum.MqttStateVacuum.discovery_update"
|
||||
) as discovery_update:
|
||||
await help_test_discovery_update_unchanged(
|
||||
hass,
|
Loading…
Reference in New Issue