Add MQTT object_id option (#58728)
* Add MQTT object_id option * Add MQTT object_id option * Add MQTT object_id option * Add MQTT object_id option - Fix light and vacuum * Add MQTT object_id option - Fix light and vacuum * Add MQTT object_id option - Fix lock * Add MQTT object_id option - Fix device * Add MQTT object_id option - Fix device * Update tests/components/mqtt/test_discovery.py Co-authored-by: Erik Montnemery <erik@montnemery.com> * Change deprecated method Co-authored-by: Erik Montnemery <erik@montnemery.com>
This commit is contained in:
parent
5151c4d99b
commit
67c2747027
26 changed files with 232 additions and 6 deletions
|
@ -19,6 +19,7 @@ from .const import ( # noqa: F401
|
||||||
CONF_SCAN_INTERVAL,
|
CONF_SCAN_INTERVAL,
|
||||||
CONF_TRACK_NEW,
|
CONF_TRACK_NEW,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
|
ENTITY_ID_FORMAT,
|
||||||
SOURCE_TYPE_BLUETOOTH,
|
SOURCE_TYPE_BLUETOOTH,
|
||||||
SOURCE_TYPE_BLUETOOTH_LE,
|
SOURCE_TYPE_BLUETOOTH_LE,
|
||||||
SOURCE_TYPE_GPS,
|
SOURCE_TYPE_GPS,
|
||||||
|
|
|
@ -6,6 +6,7 @@ from typing import Final
|
||||||
LOGGER: Final = logging.getLogger(__package__)
|
LOGGER: Final = logging.getLogger(__package__)
|
||||||
|
|
||||||
DOMAIN: Final = "device_tracker"
|
DOMAIN: Final = "device_tracker"
|
||||||
|
ENTITY_ID_FORMAT: Final = DOMAIN + ".{}"
|
||||||
|
|
||||||
PLATFORM_TYPE_LEGACY: Final = "legacy"
|
PLATFORM_TYPE_LEGACY: Final = "legacy"
|
||||||
PLATFORM_TYPE_ENTITY: Final = "entity_platform"
|
PLATFORM_TYPE_ENTITY: Final = "entity_platform"
|
||||||
|
|
|
@ -47,6 +47,8 @@ _LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
SCAN_INTERVAL = timedelta(seconds=60)
|
SCAN_INTERVAL = timedelta(seconds=60)
|
||||||
|
|
||||||
|
ENTITY_ID_FORMAT = DOMAIN + ".{}"
|
||||||
|
|
||||||
DEVICE_CLASSES = [DEVICE_CLASS_HUMIDIFIER, DEVICE_CLASS_DEHUMIDIFIER]
|
DEVICE_CLASSES = [DEVICE_CLASS_HUMIDIFIER, DEVICE_CLASS_DEHUMIDIFIER]
|
||||||
|
|
||||||
DEVICE_CLASSES_SCHEMA = vol.All(vol.Lower, vol.In(DEVICE_CLASSES))
|
DEVICE_CLASSES_SCHEMA = vol.All(vol.Lower, vol.In(DEVICE_CLASSES))
|
||||||
|
|
|
@ -97,6 +97,7 @@ ABBREVIATIONS = {
|
||||||
"mode_stat_tpl": "mode_state_template",
|
"mode_stat_tpl": "mode_state_template",
|
||||||
"modes": "modes",
|
"modes": "modes",
|
||||||
"name": "name",
|
"name": "name",
|
||||||
|
"obj_id": "object_id",
|
||||||
"off_dly": "off_delay",
|
"off_dly": "off_delay",
|
||||||
"on_cmd_type": "on_command_type",
|
"on_cmd_type": "on_command_type",
|
||||||
"ops": "options",
|
"ops": "options",
|
||||||
|
|
|
@ -126,6 +126,7 @@ async def _async_setup_entity(
|
||||||
class MqttAlarm(MqttEntity, alarm.AlarmControlPanelEntity):
|
class MqttAlarm(MqttEntity, alarm.AlarmControlPanelEntity):
|
||||||
"""Representation of a MQTT alarm status."""
|
"""Representation of a MQTT alarm status."""
|
||||||
|
|
||||||
|
_entity_id_format = alarm.ENTITY_ID_FORMAT
|
||||||
_attributes_extra_blocked = MQTT_ALARM_ATTRIBUTES_BLOCKED
|
_attributes_extra_blocked = MQTT_ALARM_ATTRIBUTES_BLOCKED
|
||||||
|
|
||||||
def __init__(self, hass, config, config_entry, discovery_data):
|
def __init__(self, hass, config, config_entry, discovery_data):
|
||||||
|
|
|
@ -87,6 +87,8 @@ async def _async_setup_entity(
|
||||||
class MqttBinarySensor(MqttEntity, BinarySensorEntity):
|
class MqttBinarySensor(MqttEntity, BinarySensorEntity):
|
||||||
"""Representation a binary sensor that is updated by MQTT."""
|
"""Representation a binary sensor that is updated by MQTT."""
|
||||||
|
|
||||||
|
_entity_id_format = binary_sensor.ENTITY_ID_FORMAT
|
||||||
|
|
||||||
def __init__(self, hass, config, config_entry, discovery_data):
|
def __init__(self, hass, config, config_entry, discovery_data):
|
||||||
"""Initialize the MQTT binary sensor."""
|
"""Initialize the MQTT binary sensor."""
|
||||||
self._state = None
|
self._state = None
|
||||||
|
|
|
@ -66,6 +66,7 @@ async def _async_setup_entity(
|
||||||
class MqttCamera(MqttEntity, Camera):
|
class MqttCamera(MqttEntity, Camera):
|
||||||
"""representation of a MQTT camera."""
|
"""representation of a MQTT camera."""
|
||||||
|
|
||||||
|
_entity_id_format = camera.ENTITY_ID_FORMAT
|
||||||
_attributes_extra_blocked = MQTT_CAMERA_ATTRIBUTES_BLOCKED
|
_attributes_extra_blocked = MQTT_CAMERA_ATTRIBUTES_BLOCKED
|
||||||
|
|
||||||
def __init__(self, hass, config, config_entry, discovery_data):
|
def __init__(self, hass, config, config_entry, discovery_data):
|
||||||
|
|
|
@ -297,6 +297,7 @@ async def _async_setup_entity(
|
||||||
class MqttClimate(MqttEntity, ClimateEntity):
|
class MqttClimate(MqttEntity, ClimateEntity):
|
||||||
"""Representation of an MQTT climate device."""
|
"""Representation of an MQTT climate device."""
|
||||||
|
|
||||||
|
_entity_id_format = climate.ENTITY_ID_FORMAT
|
||||||
_attributes_extra_blocked = MQTT_CLIMATE_ATTRIBUTES_BLOCKED
|
_attributes_extra_blocked = MQTT_CLIMATE_ATTRIBUTES_BLOCKED
|
||||||
|
|
||||||
def __init__(self, hass, config, config_entry, discovery_data):
|
def __init__(self, hass, config, config_entry, discovery_data):
|
||||||
|
|
|
@ -224,6 +224,7 @@ async def _async_setup_entity(
|
||||||
class MqttCover(MqttEntity, CoverEntity):
|
class MqttCover(MqttEntity, CoverEntity):
|
||||||
"""Representation of a cover that can be controlled using MQTT."""
|
"""Representation of a cover that can be controlled using MQTT."""
|
||||||
|
|
||||||
|
_entity_id_format = cover.ENTITY_ID_FORMAT
|
||||||
_attributes_extra_blocked = MQTT_COVER_ATTRIBUTES_BLOCKED
|
_attributes_extra_blocked = MQTT_COVER_ATTRIBUTES_BLOCKED
|
||||||
|
|
||||||
def __init__(self, hass, config, config_entry, discovery_data):
|
def __init__(self, hass, config, config_entry, discovery_data):
|
||||||
|
|
|
@ -58,6 +58,8 @@ async def _async_setup_entity(
|
||||||
class MqttDeviceTracker(MqttEntity, TrackerEntity):
|
class MqttDeviceTracker(MqttEntity, TrackerEntity):
|
||||||
"""Representation of a device tracker using MQTT."""
|
"""Representation of a device tracker using MQTT."""
|
||||||
|
|
||||||
|
_entity_id_format = device_tracker.ENTITY_ID_FORMAT
|
||||||
|
|
||||||
def __init__(self, hass, config, config_entry, discovery_data):
|
def __init__(self, hass, config, config_entry, discovery_data):
|
||||||
"""Initialize the tracker."""
|
"""Initialize the tracker."""
|
||||||
self._location_name = None
|
self._location_name = None
|
||||||
|
|
|
@ -230,6 +230,7 @@ async def _async_setup_entity(
|
||||||
class MqttFan(MqttEntity, FanEntity):
|
class MqttFan(MqttEntity, FanEntity):
|
||||||
"""A MQTT fan component."""
|
"""A MQTT fan component."""
|
||||||
|
|
||||||
|
_entity_id_format = fan.ENTITY_ID_FORMAT
|
||||||
_attributes_extra_blocked = MQTT_FAN_ATTRIBUTES_BLOCKED
|
_attributes_extra_blocked = MQTT_FAN_ATTRIBUTES_BLOCKED
|
||||||
|
|
||||||
def __init__(self, hass, config, config_entry, discovery_data):
|
def __init__(self, hass, config, config_entry, discovery_data):
|
||||||
|
|
|
@ -163,6 +163,7 @@ async def _async_setup_entity(
|
||||||
class MqttHumidifier(MqttEntity, HumidifierEntity):
|
class MqttHumidifier(MqttEntity, HumidifierEntity):
|
||||||
"""A MQTT humidifier component."""
|
"""A MQTT humidifier component."""
|
||||||
|
|
||||||
|
_entity_id_format = humidifier.ENTITY_ID_FORMAT
|
||||||
_attributes_extra_blocked = MQTT_HUMIDIFIER_ATTRIBUTES_BLOCKED
|
_attributes_extra_blocked = MQTT_HUMIDIFIER_ATTRIBUTES_BLOCKED
|
||||||
|
|
||||||
def __init__(self, hass, config, config_entry, discovery_data):
|
def __init__(self, hass, config, config_entry, discovery_data):
|
||||||
|
|
|
@ -29,6 +29,7 @@ from homeassistant.components.light import (
|
||||||
COLOR_MODE_UNKNOWN,
|
COLOR_MODE_UNKNOWN,
|
||||||
COLOR_MODE_WHITE,
|
COLOR_MODE_WHITE,
|
||||||
COLOR_MODE_XY,
|
COLOR_MODE_XY,
|
||||||
|
ENTITY_ID_FORMAT,
|
||||||
SUPPORT_BRIGHTNESS,
|
SUPPORT_BRIGHTNESS,
|
||||||
SUPPORT_COLOR,
|
SUPPORT_COLOR,
|
||||||
SUPPORT_COLOR_TEMP,
|
SUPPORT_COLOR_TEMP,
|
||||||
|
@ -239,6 +240,7 @@ async def async_setup_entity_basic(
|
||||||
class MqttLight(MqttEntity, LightEntity, RestoreEntity):
|
class MqttLight(MqttEntity, LightEntity, RestoreEntity):
|
||||||
"""Representation of a MQTT light."""
|
"""Representation of a MQTT light."""
|
||||||
|
|
||||||
|
_entity_id_format = ENTITY_ID_FORMAT
|
||||||
_attributes_extra_blocked = MQTT_LIGHT_ATTRIBUTES_BLOCKED
|
_attributes_extra_blocked = MQTT_LIGHT_ATTRIBUTES_BLOCKED
|
||||||
|
|
||||||
def __init__(self, hass, config, config_entry, discovery_data):
|
def __init__(self, hass, config, config_entry, discovery_data):
|
||||||
|
|
|
@ -24,6 +24,7 @@ from homeassistant.components.light import (
|
||||||
COLOR_MODE_RGBW,
|
COLOR_MODE_RGBW,
|
||||||
COLOR_MODE_RGBWW,
|
COLOR_MODE_RGBWW,
|
||||||
COLOR_MODE_XY,
|
COLOR_MODE_XY,
|
||||||
|
ENTITY_ID_FORMAT,
|
||||||
FLASH_LONG,
|
FLASH_LONG,
|
||||||
FLASH_SHORT,
|
FLASH_SHORT,
|
||||||
SUPPORT_BRIGHTNESS,
|
SUPPORT_BRIGHTNESS,
|
||||||
|
@ -167,6 +168,7 @@ async def async_setup_entity_json(
|
||||||
class MqttLightJson(MqttEntity, LightEntity, RestoreEntity):
|
class MqttLightJson(MqttEntity, LightEntity, RestoreEntity):
|
||||||
"""Representation of a MQTT JSON light."""
|
"""Representation of a MQTT JSON light."""
|
||||||
|
|
||||||
|
_entity_id_format = ENTITY_ID_FORMAT
|
||||||
_attributes_extra_blocked = MQTT_LIGHT_ATTRIBUTES_BLOCKED
|
_attributes_extra_blocked = MQTT_LIGHT_ATTRIBUTES_BLOCKED
|
||||||
|
|
||||||
def __init__(self, hass, config, config_entry, discovery_data):
|
def __init__(self, hass, config, config_entry, discovery_data):
|
||||||
|
|
|
@ -11,6 +11,7 @@ from homeassistant.components.light import (
|
||||||
ATTR_HS_COLOR,
|
ATTR_HS_COLOR,
|
||||||
ATTR_TRANSITION,
|
ATTR_TRANSITION,
|
||||||
ATTR_WHITE_VALUE,
|
ATTR_WHITE_VALUE,
|
||||||
|
ENTITY_ID_FORMAT,
|
||||||
SUPPORT_BRIGHTNESS,
|
SUPPORT_BRIGHTNESS,
|
||||||
SUPPORT_COLOR,
|
SUPPORT_COLOR,
|
||||||
SUPPORT_COLOR_TEMP,
|
SUPPORT_COLOR_TEMP,
|
||||||
|
@ -97,6 +98,7 @@ async def async_setup_entity_template(
|
||||||
class MqttLightTemplate(MqttEntity, LightEntity, RestoreEntity):
|
class MqttLightTemplate(MqttEntity, LightEntity, RestoreEntity):
|
||||||
"""Representation of a MQTT Template light."""
|
"""Representation of a MQTT Template light."""
|
||||||
|
|
||||||
|
_entity_id_format = ENTITY_ID_FORMAT
|
||||||
_attributes_extra_blocked = MQTT_LIGHT_ATTRIBUTES_BLOCKED
|
_attributes_extra_blocked = MQTT_LIGHT_ATTRIBUTES_BLOCKED
|
||||||
|
|
||||||
def __init__(self, hass, config, config_entry, discovery_data):
|
def __init__(self, hass, config, config_entry, discovery_data):
|
||||||
|
|
|
@ -78,6 +78,7 @@ async def _async_setup_entity(
|
||||||
class MqttLock(MqttEntity, LockEntity):
|
class MqttLock(MqttEntity, LockEntity):
|
||||||
"""Representation of a lock that can be toggled using MQTT."""
|
"""Representation of a lock that can be toggled using MQTT."""
|
||||||
|
|
||||||
|
_entity_id_format = lock.ENTITY_ID_FORMAT
|
||||||
_attributes_extra_blocked = MQTT_LOCK_ATTRIBUTES_BLOCKED
|
_attributes_extra_blocked = MQTT_LOCK_ATTRIBUTES_BLOCKED
|
||||||
|
|
||||||
def __init__(self, hass, config, config_entry, discovery_data):
|
def __init__(self, hass, config, config_entry, discovery_data):
|
||||||
|
|
|
@ -28,7 +28,12 @@ from homeassistant.helpers.dispatcher import (
|
||||||
async_dispatcher_connect,
|
async_dispatcher_connect,
|
||||||
async_dispatcher_send,
|
async_dispatcher_send,
|
||||||
)
|
)
|
||||||
from homeassistant.helpers.entity import ENTITY_CATEGORIES_SCHEMA, DeviceInfo, Entity
|
from homeassistant.helpers.entity import (
|
||||||
|
ENTITY_CATEGORIES_SCHEMA,
|
||||||
|
DeviceInfo,
|
||||||
|
Entity,
|
||||||
|
async_generate_entity_id,
|
||||||
|
)
|
||||||
from homeassistant.helpers.typing import ConfigType
|
from homeassistant.helpers.typing import ConfigType
|
||||||
|
|
||||||
from . import DATA_MQTT, debug_info, publish, subscription
|
from . import DATA_MQTT, debug_info, publish, subscription
|
||||||
|
@ -82,6 +87,7 @@ CONF_VIA_DEVICE = "via_device"
|
||||||
CONF_DEPRECATED_VIA_HUB = "via_hub"
|
CONF_DEPRECATED_VIA_HUB = "via_hub"
|
||||||
CONF_SUGGESTED_AREA = "suggested_area"
|
CONF_SUGGESTED_AREA = "suggested_area"
|
||||||
CONF_CONFIGURATION_URL = "configuration_url"
|
CONF_CONFIGURATION_URL = "configuration_url"
|
||||||
|
CONF_OBJECT_ID = "object_id"
|
||||||
|
|
||||||
MQTT_ATTRIBUTES_BLOCKED = {
|
MQTT_ATTRIBUTES_BLOCKED = {
|
||||||
"assumed_state",
|
"assumed_state",
|
||||||
|
@ -183,6 +189,7 @@ MQTT_ENTITY_COMMON_SCHEMA = MQTT_AVAILABILITY_SCHEMA.extend(
|
||||||
vol.Optional(CONF_ICON): cv.icon,
|
vol.Optional(CONF_ICON): cv.icon,
|
||||||
vol.Optional(CONF_JSON_ATTRS_TOPIC): valid_subscribe_topic,
|
vol.Optional(CONF_JSON_ATTRS_TOPIC): valid_subscribe_topic,
|
||||||
vol.Optional(CONF_JSON_ATTRS_TEMPLATE): cv.template,
|
vol.Optional(CONF_JSON_ATTRS_TEMPLATE): cv.template,
|
||||||
|
vol.Optional(CONF_OBJECT_ID): cv.string,
|
||||||
vol.Optional(CONF_UNIQUE_ID): cv.string,
|
vol.Optional(CONF_UNIQUE_ID): cv.string,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -210,6 +217,14 @@ async def async_setup_entry_helper(hass, domain, async_setup, schema):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def init_entity_id_from_config(hass, entity, config, entity_id_format):
|
||||||
|
"""Set entity_id from object_id if defined in config."""
|
||||||
|
if CONF_OBJECT_ID in config:
|
||||||
|
entity.entity_id = async_generate_entity_id(
|
||||||
|
entity_id_format, config[CONF_OBJECT_ID], None, hass
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class MqttAttributes(Entity):
|
class MqttAttributes(Entity):
|
||||||
"""Mixin used for platforms that support JSON attributes."""
|
"""Mixin used for platforms that support JSON attributes."""
|
||||||
|
|
||||||
|
@ -588,6 +603,8 @@ class MqttEntity(
|
||||||
):
|
):
|
||||||
"""Representation of an MQTT entity."""
|
"""Representation of an MQTT entity."""
|
||||||
|
|
||||||
|
_entity_id_format: str
|
||||||
|
|
||||||
def __init__(self, hass, config, config_entry, discovery_data):
|
def __init__(self, hass, config, config_entry, discovery_data):
|
||||||
"""Init the MQTT Entity."""
|
"""Init the MQTT Entity."""
|
||||||
self.hass = hass
|
self.hass = hass
|
||||||
|
@ -598,12 +615,21 @@ class MqttEntity(
|
||||||
# Load config
|
# Load config
|
||||||
self._setup_from_config(self._config)
|
self._setup_from_config(self._config)
|
||||||
|
|
||||||
|
# Initialize entity_id from config
|
||||||
|
self._init_entity_id()
|
||||||
|
|
||||||
# Initialize mixin classes
|
# Initialize mixin classes
|
||||||
MqttAttributes.__init__(self, config)
|
MqttAttributes.__init__(self, config)
|
||||||
MqttAvailability.__init__(self, config)
|
MqttAvailability.__init__(self, config)
|
||||||
MqttDiscoveryUpdate.__init__(self, discovery_data, self.discovery_update)
|
MqttDiscoveryUpdate.__init__(self, discovery_data, self.discovery_update)
|
||||||
MqttEntityDeviceInfo.__init__(self, config.get(CONF_DEVICE), config_entry)
|
MqttEntityDeviceInfo.__init__(self, config.get(CONF_DEVICE), config_entry)
|
||||||
|
|
||||||
|
def _init_entity_id(self):
|
||||||
|
"""Set entity_id from object_id if defined in config."""
|
||||||
|
init_entity_id_from_config(
|
||||||
|
self.hass, self, self._config, self._entity_id_format
|
||||||
|
)
|
||||||
|
|
||||||
async def async_added_to_hass(self):
|
async def async_added_to_hass(self):
|
||||||
"""Subscribe mqtt events."""
|
"""Subscribe mqtt events."""
|
||||||
await super().async_added_to_hass()
|
await super().async_added_to_hass()
|
||||||
|
|
|
@ -114,6 +114,7 @@ async def _async_setup_entity(
|
||||||
class MqttNumber(MqttEntity, NumberEntity, RestoreEntity):
|
class MqttNumber(MqttEntity, NumberEntity, RestoreEntity):
|
||||||
"""representation of an MQTT number."""
|
"""representation of an MQTT number."""
|
||||||
|
|
||||||
|
_entity_id_format = number.ENTITY_ID_FORMAT
|
||||||
_attributes_extra_blocked = MQTT_NUMBER_ATTRIBUTES_BLOCKED
|
_attributes_extra_blocked = MQTT_NUMBER_ATTRIBUTES_BLOCKED
|
||||||
|
|
||||||
def __init__(self, hass, config, config_entry, discovery_data):
|
def __init__(self, hass, config, config_entry, discovery_data):
|
||||||
|
|
|
@ -15,10 +15,12 @@ from . import PLATFORMS
|
||||||
from .. import mqtt
|
from .. import mqtt
|
||||||
from .const import CONF_COMMAND_TOPIC, CONF_QOS, CONF_RETAIN, DOMAIN
|
from .const import CONF_COMMAND_TOPIC, CONF_QOS, CONF_RETAIN, DOMAIN
|
||||||
from .mixins import (
|
from .mixins import (
|
||||||
|
CONF_OBJECT_ID,
|
||||||
MQTT_AVAILABILITY_SCHEMA,
|
MQTT_AVAILABILITY_SCHEMA,
|
||||||
MqttAvailability,
|
MqttAvailability,
|
||||||
MqttDiscoveryUpdate,
|
MqttDiscoveryUpdate,
|
||||||
async_setup_entry_helper,
|
async_setup_entry_helper,
|
||||||
|
init_entity_id_from_config,
|
||||||
)
|
)
|
||||||
|
|
||||||
DEFAULT_NAME = "MQTT Scene"
|
DEFAULT_NAME = "MQTT Scene"
|
||||||
|
@ -32,6 +34,7 @@ PLATFORM_SCHEMA = mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend(
|
||||||
vol.Optional(CONF_PAYLOAD_ON): cv.string,
|
vol.Optional(CONF_PAYLOAD_ON): cv.string,
|
||||||
vol.Optional(CONF_UNIQUE_ID): cv.string,
|
vol.Optional(CONF_UNIQUE_ID): cv.string,
|
||||||
vol.Optional(CONF_RETAIN, default=DEFAULT_RETAIN): cv.boolean,
|
vol.Optional(CONF_RETAIN, default=DEFAULT_RETAIN): cv.boolean,
|
||||||
|
vol.Optional(CONF_OBJECT_ID): cv.string,
|
||||||
}
|
}
|
||||||
).extend(MQTT_AVAILABILITY_SCHEMA.schema)
|
).extend(MQTT_AVAILABILITY_SCHEMA.schema)
|
||||||
|
|
||||||
|
@ -43,22 +46,22 @@ async def async_setup_platform(
|
||||||
):
|
):
|
||||||
"""Set up MQTT scene through configuration.yaml."""
|
"""Set up MQTT scene through configuration.yaml."""
|
||||||
await async_setup_reload_service(hass, DOMAIN, PLATFORMS)
|
await async_setup_reload_service(hass, DOMAIN, PLATFORMS)
|
||||||
await _async_setup_entity(async_add_entities, config)
|
await _async_setup_entity(hass, async_add_entities, config)
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
"""Set up MQTT scene dynamically through MQTT discovery."""
|
"""Set up MQTT scene dynamically through MQTT discovery."""
|
||||||
setup = functools.partial(
|
setup = functools.partial(
|
||||||
_async_setup_entity, async_add_entities, config_entry=config_entry
|
_async_setup_entity, hass, async_add_entities, config_entry=config_entry
|
||||||
)
|
)
|
||||||
await async_setup_entry_helper(hass, scene.DOMAIN, setup, DISCOVERY_SCHEMA)
|
await async_setup_entry_helper(hass, scene.DOMAIN, setup, DISCOVERY_SCHEMA)
|
||||||
|
|
||||||
|
|
||||||
async def _async_setup_entity(
|
async def _async_setup_entity(
|
||||||
async_add_entities, config, config_entry=None, discovery_data=None
|
hass, async_add_entities, config, config_entry=None, discovery_data=None
|
||||||
):
|
):
|
||||||
"""Set up the MQTT scene."""
|
"""Set up the MQTT scene."""
|
||||||
async_add_entities([MqttScene(config, config_entry, discovery_data)])
|
async_add_entities([MqttScene(hass, config, config_entry, discovery_data)])
|
||||||
|
|
||||||
|
|
||||||
class MqttScene(
|
class MqttScene(
|
||||||
|
@ -68,8 +71,11 @@ class MqttScene(
|
||||||
):
|
):
|
||||||
"""Representation of a scene that can be activated using MQTT."""
|
"""Representation of a scene that can be activated using MQTT."""
|
||||||
|
|
||||||
def __init__(self, config, config_entry, discovery_data):
|
_entity_id_format = scene.DOMAIN + ".{}"
|
||||||
|
|
||||||
|
def __init__(self, hass, config, config_entry, discovery_data):
|
||||||
"""Initialize the MQTT scene."""
|
"""Initialize the MQTT scene."""
|
||||||
|
self.hass = hass
|
||||||
self._state = False
|
self._state = False
|
||||||
self._sub_state = None
|
self._sub_state = None
|
||||||
|
|
||||||
|
@ -78,9 +84,18 @@ class MqttScene(
|
||||||
# Load config
|
# Load config
|
||||||
self._setup_from_config(config)
|
self._setup_from_config(config)
|
||||||
|
|
||||||
|
# Initialize entity_id from config
|
||||||
|
self._init_entity_id()
|
||||||
|
|
||||||
MqttAvailability.__init__(self, config)
|
MqttAvailability.__init__(self, config)
|
||||||
MqttDiscoveryUpdate.__init__(self, discovery_data, self.discovery_update)
|
MqttDiscoveryUpdate.__init__(self, discovery_data, self.discovery_update)
|
||||||
|
|
||||||
|
def _init_entity_id(self):
|
||||||
|
"""Set entity_id from object_id if defined in config."""
|
||||||
|
init_entity_id_from_config(
|
||||||
|
self.hass, self, self._config, self._entity_id_format
|
||||||
|
)
|
||||||
|
|
||||||
async def async_added_to_hass(self):
|
async def async_added_to_hass(self):
|
||||||
"""Subscribe to MQTT events."""
|
"""Subscribe to MQTT events."""
|
||||||
await super().async_added_to_hass()
|
await super().async_added_to_hass()
|
||||||
|
|
|
@ -90,6 +90,8 @@ async def _async_setup_entity(
|
||||||
class MqttSelect(MqttEntity, SelectEntity, RestoreEntity):
|
class MqttSelect(MqttEntity, SelectEntity, RestoreEntity):
|
||||||
"""representation of an MQTT select."""
|
"""representation of an MQTT select."""
|
||||||
|
|
||||||
|
_entity_id_format = select.ENTITY_ID_FORMAT
|
||||||
|
|
||||||
_attributes_extra_blocked = MQTT_SELECT_ATTRIBUTES_BLOCKED
|
_attributes_extra_blocked = MQTT_SELECT_ATTRIBUTES_BLOCKED
|
||||||
|
|
||||||
def __init__(self, hass, config, config_entry, discovery_data):
|
def __init__(self, hass, config, config_entry, discovery_data):
|
||||||
|
|
|
@ -11,6 +11,7 @@ from homeassistant.components import sensor
|
||||||
from homeassistant.components.sensor import (
|
from homeassistant.components.sensor import (
|
||||||
CONF_STATE_CLASS,
|
CONF_STATE_CLASS,
|
||||||
DEVICE_CLASSES_SCHEMA,
|
DEVICE_CLASSES_SCHEMA,
|
||||||
|
ENTITY_ID_FORMAT,
|
||||||
STATE_CLASSES_SCHEMA,
|
STATE_CLASSES_SCHEMA,
|
||||||
SensorEntity,
|
SensorEntity,
|
||||||
)
|
)
|
||||||
|
@ -132,6 +133,7 @@ async def _async_setup_entity(
|
||||||
class MqttSensor(MqttEntity, SensorEntity):
|
class MqttSensor(MqttEntity, SensorEntity):
|
||||||
"""Representation of a sensor that can be updated using MQTT."""
|
"""Representation of a sensor that can be updated using MQTT."""
|
||||||
|
|
||||||
|
_entity_id_format = ENTITY_ID_FORMAT
|
||||||
_attr_last_reset = None
|
_attr_last_reset = None
|
||||||
_attributes_extra_blocked = MQTT_SENSOR_ATTRIBUTES_BLOCKED
|
_attributes_extra_blocked = MQTT_SENSOR_ATTRIBUTES_BLOCKED
|
||||||
|
|
||||||
|
|
|
@ -84,6 +84,7 @@ async def _async_setup_entity(
|
||||||
class MqttSwitch(MqttEntity, SwitchEntity, RestoreEntity):
|
class MqttSwitch(MqttEntity, SwitchEntity, RestoreEntity):
|
||||||
"""Representation of a switch that can be toggled using MQTT."""
|
"""Representation of a switch that can be toggled using MQTT."""
|
||||||
|
|
||||||
|
_entity_id_format = switch.ENTITY_ID_FORMAT
|
||||||
_attributes_extra_blocked = MQTT_SWITCH_ATTRIBUTES_BLOCKED
|
_attributes_extra_blocked = MQTT_SWITCH_ATTRIBUTES_BLOCKED
|
||||||
|
|
||||||
def __init__(self, hass, config, config_entry, discovery_data):
|
def __init__(self, hass, config, config_entry, discovery_data):
|
||||||
|
|
|
@ -5,6 +5,7 @@ import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.vacuum import (
|
from homeassistant.components.vacuum import (
|
||||||
ATTR_STATUS,
|
ATTR_STATUS,
|
||||||
|
ENTITY_ID_FORMAT,
|
||||||
SUPPORT_BATTERY,
|
SUPPORT_BATTERY,
|
||||||
SUPPORT_CLEAN_SPOT,
|
SUPPORT_CLEAN_SPOT,
|
||||||
SUPPORT_FAN_SPEED,
|
SUPPORT_FAN_SPEED,
|
||||||
|
@ -169,6 +170,7 @@ async def async_setup_entity_legacy(
|
||||||
class MqttVacuum(MqttEntity, VacuumEntity):
|
class MqttVacuum(MqttEntity, VacuumEntity):
|
||||||
"""Representation of a MQTT-controlled legacy vacuum."""
|
"""Representation of a MQTT-controlled legacy vacuum."""
|
||||||
|
|
||||||
|
_entity_id_format = ENTITY_ID_FORMAT
|
||||||
_attributes_extra_blocked = MQTT_LEGACY_VACUUM_ATTRIBUTES_BLOCKED
|
_attributes_extra_blocked = MQTT_LEGACY_VACUUM_ATTRIBUTES_BLOCKED
|
||||||
|
|
||||||
def __init__(self, hass, config, config_entry, discovery_data):
|
def __init__(self, hass, config, config_entry, discovery_data):
|
||||||
|
|
|
@ -4,6 +4,7 @@ import json
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.vacuum import (
|
from homeassistant.components.vacuum import (
|
||||||
|
ENTITY_ID_FORMAT,
|
||||||
STATE_CLEANING,
|
STATE_CLEANING,
|
||||||
STATE_DOCKED,
|
STATE_DOCKED,
|
||||||
STATE_ERROR,
|
STATE_ERROR,
|
||||||
|
@ -149,6 +150,7 @@ async def async_setup_entity_state(
|
||||||
class MqttStateVacuum(MqttEntity, StateVacuumEntity):
|
class MqttStateVacuum(MqttEntity, StateVacuumEntity):
|
||||||
"""Representation of a MQTT-controlled state vacuum."""
|
"""Representation of a MQTT-controlled state vacuum."""
|
||||||
|
|
||||||
|
_entity_id_format = ENTITY_ID_FORMAT
|
||||||
_attributes_extra_blocked = MQTT_VACUUM_ATTRIBUTES_BLOCKED
|
_attributes_extra_blocked = MQTT_VACUUM_ATTRIBUTES_BLOCKED
|
||||||
|
|
||||||
def __init__(self, hass, config, config_entry, discovery_data):
|
def __init__(self, hass, config, config_entry, discovery_data):
|
||||||
|
|
|
@ -40,6 +40,7 @@ from homeassistant.loader import bind_hass
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
DOMAIN = "vacuum"
|
DOMAIN = "vacuum"
|
||||||
|
ENTITY_ID_FORMAT = DOMAIN + ".{}"
|
||||||
SCAN_INTERVAL = timedelta(seconds=20)
|
SCAN_INTERVAL = timedelta(seconds=20)
|
||||||
|
|
||||||
ATTR_BATTERY_ICON = "battery_icon"
|
ATTR_BATTERY_ICON = "battery_icon"
|
||||||
|
|
|
@ -191,6 +191,158 @@ async def test_discover_alarm_control_panel(hass, mqtt_mock, caplog):
|
||||||
assert ("alarm_control_panel", "bla") in hass.data[ALREADY_DISCOVERED]
|
assert ("alarm_control_panel", "bla") in hass.data[ALREADY_DISCOVERED]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"topic, config, entity_id, name, domain",
|
||||||
|
[
|
||||||
|
(
|
||||||
|
"homeassistant/alarm_control_panel/object/bla/config",
|
||||||
|
'{ "name": "Hello World 1", "obj_id": "hello_id", "state_topic": "test-topic", "command_topic": "test-topic" }',
|
||||||
|
"alarm_control_panel.hello_id",
|
||||||
|
"Hello World 1",
|
||||||
|
"alarm_control_panel",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"homeassistant/binary_sensor/object/bla/config",
|
||||||
|
'{ "name": "Hello World 2", "obj_id": "hello_id", "state_topic": "test-topic" }',
|
||||||
|
"binary_sensor.hello_id",
|
||||||
|
"Hello World 2",
|
||||||
|
"binary_sensor",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"homeassistant/camera/object/bla/config",
|
||||||
|
'{ "name": "Hello World 3", "obj_id": "hello_id", "state_topic": "test-topic", "topic": "test-topic" }',
|
||||||
|
"camera.hello_id",
|
||||||
|
"Hello World 3",
|
||||||
|
"camera",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"homeassistant/climate/object/bla/config",
|
||||||
|
'{ "name": "Hello World 4", "obj_id": "hello_id", "state_topic": "test-topic" }',
|
||||||
|
"climate.hello_id",
|
||||||
|
"Hello World 4",
|
||||||
|
"climate",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"homeassistant/cover/object/bla/config",
|
||||||
|
'{ "name": "Hello World 5", "obj_id": "hello_id", "state_topic": "test-topic" }',
|
||||||
|
"cover.hello_id",
|
||||||
|
"Hello World 5",
|
||||||
|
"cover",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"homeassistant/fan/object/bla/config",
|
||||||
|
'{ "name": "Hello World 6", "obj_id": "hello_id", "state_topic": "test-topic", "command_topic": "test-topic" }',
|
||||||
|
"fan.hello_id",
|
||||||
|
"Hello World 6",
|
||||||
|
"fan",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"homeassistant/humidifier/object/bla/config",
|
||||||
|
'{ "name": "Hello World 7", "obj_id": "hello_id", "state_topic": "test-topic", "target_humidity_command_topic": "test-topic", "command_topic": "test-topic" }',
|
||||||
|
"humidifier.hello_id",
|
||||||
|
"Hello World 7",
|
||||||
|
"humidifier",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"homeassistant/number/object/bla/config",
|
||||||
|
'{ "name": "Hello World 8", "obj_id": "hello_id", "state_topic": "test-topic", "command_topic": "test-topic" }',
|
||||||
|
"number.hello_id",
|
||||||
|
"Hello World 8",
|
||||||
|
"number",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"homeassistant/scene/object/bla/config",
|
||||||
|
'{ "name": "Hello World 9", "obj_id": "hello_id", "state_topic": "test-topic", "command_topic": "test-topic" }',
|
||||||
|
"scene.hello_id",
|
||||||
|
"Hello World 9",
|
||||||
|
"scene",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"homeassistant/select/object/bla/config",
|
||||||
|
'{ "name": "Hello World 10", "obj_id": "hello_id", "state_topic": "test-topic", "options": [ "opt1", "opt2" ], "command_topic": "test-topic" }',
|
||||||
|
"select.hello_id",
|
||||||
|
"Hello World 10",
|
||||||
|
"select",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"homeassistant/sensor/object/bla/config",
|
||||||
|
'{ "name": "Hello World 11", "obj_id": "hello_id", "state_topic": "test-topic" }',
|
||||||
|
"sensor.hello_id",
|
||||||
|
"Hello World 11",
|
||||||
|
"sensor",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"homeassistant/switch/object/bla/config",
|
||||||
|
'{ "name": "Hello World 12", "obj_id": "hello_id", "state_topic": "test-topic", "command_topic": "test-topic" }',
|
||||||
|
"switch.hello_id",
|
||||||
|
"Hello World 12",
|
||||||
|
"switch",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"homeassistant/light/object/bla/config",
|
||||||
|
'{ "name": "Hello World 13", "obj_id": "hello_id", "state_topic": "test-topic", "command_topic": "test-topic" }',
|
||||||
|
"light.hello_id",
|
||||||
|
"Hello World 13",
|
||||||
|
"light",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"homeassistant/light/object/bla/config",
|
||||||
|
'{ "name": "Hello World 14", "obj_id": "hello_id", "state_topic": "test-topic", "command_topic": "test-topic", "schema": "json" }',
|
||||||
|
"light.hello_id",
|
||||||
|
"Hello World 14",
|
||||||
|
"light",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"homeassistant/light/object/bla/config",
|
||||||
|
'{ "name": "Hello World 15", "obj_id": "hello_id", "state_topic": "test-topic", "command_off_template": "template", "command_on_template": "template", "command_topic": "test-topic", "schema": "template" }',
|
||||||
|
"light.hello_id",
|
||||||
|
"Hello World 15",
|
||||||
|
"light",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"homeassistant/vacuum/object/bla/config",
|
||||||
|
'{ "name": "Hello World 16", "obj_id": "hello_id", "state_topic": "test-topic", "schema": "state" }',
|
||||||
|
"vacuum.hello_id",
|
||||||
|
"Hello World 16",
|
||||||
|
"vacuum",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"homeassistant/vacuum/object/bla/config",
|
||||||
|
'{ "name": "Hello World 17", "obj_id": "hello_id", "state_topic": "test-topic", "schema": "legacy" }',
|
||||||
|
"vacuum.hello_id",
|
||||||
|
"Hello World 17",
|
||||||
|
"vacuum",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"homeassistant/lock/object/bla/config",
|
||||||
|
'{ "name": "Hello World 18", "obj_id": "hello_id", "state_topic": "test-topic", "command_topic": "test-topic" }',
|
||||||
|
"lock.hello_id",
|
||||||
|
"Hello World 18",
|
||||||
|
"lock",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"homeassistant/device_tracker/object/bla/config",
|
||||||
|
'{ "name": "Hello World 19", "obj_id": "hello_id", "state_topic": "test-topic" }',
|
||||||
|
"device_tracker.hello_id",
|
||||||
|
"Hello World 19",
|
||||||
|
"device_tracker",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_discovery_with_object_id(
|
||||||
|
hass, mqtt_mock, caplog, topic, config, entity_id, name, domain
|
||||||
|
):
|
||||||
|
"""Test discovering an MQTT entity with object_id."""
|
||||||
|
async_fire_mqtt_message(hass, topic, config)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
|
||||||
|
assert state is not None
|
||||||
|
assert state.name == name
|
||||||
|
assert (domain, "object bla") in hass.data[ALREADY_DISCOVERED]
|
||||||
|
|
||||||
|
|
||||||
async def test_discovery_incl_nodeid(hass, mqtt_mock, caplog):
|
async def test_discovery_incl_nodeid(hass, mqtt_mock, caplog):
|
||||||
"""Test sending in correct JSON with optional node_id included."""
|
"""Test sending in correct JSON with optional node_id included."""
|
||||||
async_fire_mqtt_message(
|
async_fire_mqtt_message(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue