Move MQTT entity helpers to separate file (#44838)
* Move MQTT entity helpers to separate file * Fix imports * Update MQTT number * Review comments * Fix formatting
This commit is contained in:
parent
3a88a4120e
commit
b85efd343f
25 changed files with 716 additions and 660 deletions
|
@ -3,7 +3,6 @@ import asyncio
|
||||||
from functools import lru_cache, partial, wraps
|
from functools import lru_cache, partial, wraps
|
||||||
import inspect
|
import inspect
|
||||||
from itertools import groupby
|
from itertools import groupby
|
||||||
import json
|
|
||||||
import logging
|
import logging
|
||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
import os
|
import os
|
||||||
|
@ -20,8 +19,6 @@ from homeassistant import config_entries
|
||||||
from homeassistant.components import websocket_api
|
from homeassistant.components import websocket_api
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_CLIENT_ID,
|
CONF_CLIENT_ID,
|
||||||
CONF_DEVICE,
|
|
||||||
CONF_NAME,
|
|
||||||
CONF_PASSWORD,
|
CONF_PASSWORD,
|
||||||
CONF_PAYLOAD,
|
CONF_PAYLOAD,
|
||||||
CONF_PORT,
|
CONF_PORT,
|
||||||
|
@ -35,12 +32,7 @@ from homeassistant.const import CONF_UNIQUE_ID # noqa: F401
|
||||||
from homeassistant.core import CoreState, Event, HassJob, ServiceCall, callback
|
from homeassistant.core import CoreState, Event, HassJob, ServiceCall, callback
|
||||||
from homeassistant.exceptions import HomeAssistantError, Unauthorized
|
from homeassistant.exceptions import HomeAssistantError, Unauthorized
|
||||||
from homeassistant.helpers import config_validation as cv, event, template
|
from homeassistant.helpers import config_validation as cv, event, template
|
||||||
from homeassistant.helpers.dispatcher import (
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect, dispatcher_send
|
||||||
async_dispatcher_connect,
|
|
||||||
async_dispatcher_send,
|
|
||||||
dispatcher_send,
|
|
||||||
)
|
|
||||||
from homeassistant.helpers.entity import Entity
|
|
||||||
from homeassistant.helpers.typing import ConfigType, HomeAssistantType, ServiceDataType
|
from homeassistant.helpers.typing import ConfigType, HomeAssistantType, ServiceDataType
|
||||||
from homeassistant.loader import bind_hass
|
from homeassistant.loader import bind_hass
|
||||||
from homeassistant.util import dt as dt_util
|
from homeassistant.util import dt as dt_util
|
||||||
|
@ -51,9 +43,6 @@ from homeassistant.util.logging import catch_log_exception
|
||||||
from . import config_flow # noqa: F401 pylint: disable=unused-import
|
from . import config_flow # noqa: F401 pylint: disable=unused-import
|
||||||
from . import debug_info, discovery
|
from . import debug_info, discovery
|
||||||
from .const import (
|
from .const import (
|
||||||
ATTR_DISCOVERY_HASH,
|
|
||||||
ATTR_DISCOVERY_PAYLOAD,
|
|
||||||
ATTR_DISCOVERY_TOPIC,
|
|
||||||
ATTR_PAYLOAD,
|
ATTR_PAYLOAD,
|
||||||
ATTR_QOS,
|
ATTR_QOS,
|
||||||
ATTR_RETAIN,
|
ATTR_RETAIN,
|
||||||
|
@ -68,8 +57,6 @@ from .const import (
|
||||||
DATA_MQTT_CONFIG,
|
DATA_MQTT_CONFIG,
|
||||||
DEFAULT_BIRTH,
|
DEFAULT_BIRTH,
|
||||||
DEFAULT_DISCOVERY,
|
DEFAULT_DISCOVERY,
|
||||||
DEFAULT_PAYLOAD_AVAILABLE,
|
|
||||||
DEFAULT_PAYLOAD_NOT_AVAILABLE,
|
|
||||||
DEFAULT_PREFIX,
|
DEFAULT_PREFIX,
|
||||||
DEFAULT_QOS,
|
DEFAULT_QOS,
|
||||||
DEFAULT_RETAIN,
|
DEFAULT_RETAIN,
|
||||||
|
@ -79,16 +66,8 @@ from .const import (
|
||||||
MQTT_DISCONNECTED,
|
MQTT_DISCONNECTED,
|
||||||
PROTOCOL_311,
|
PROTOCOL_311,
|
||||||
)
|
)
|
||||||
from .debug_info import log_messages
|
from .discovery import LAST_DISCOVERY
|
||||||
from .discovery import (
|
|
||||||
LAST_DISCOVERY,
|
|
||||||
MQTT_DISCOVERY_DONE,
|
|
||||||
MQTT_DISCOVERY_UPDATED,
|
|
||||||
clear_discovery_hash,
|
|
||||||
set_discovery_hash,
|
|
||||||
)
|
|
||||||
from .models import Message, MessageCallbackType, PublishPayloadType
|
from .models import Message, MessageCallbackType, PublishPayloadType
|
||||||
from .subscription import async_subscribe_topics, async_unsubscribe_topics
|
|
||||||
from .util import _VALID_QOS_SCHEMA, valid_publish_topic, valid_subscribe_topic
|
from .util import _VALID_QOS_SCHEMA, valid_publish_topic, valid_subscribe_topic
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
@ -108,20 +87,6 @@ CONF_TLS_VERSION = "tls_version"
|
||||||
|
|
||||||
CONF_COMMAND_TOPIC = "command_topic"
|
CONF_COMMAND_TOPIC = "command_topic"
|
||||||
CONF_TOPIC = "topic"
|
CONF_TOPIC = "topic"
|
||||||
CONF_AVAILABILITY = "availability"
|
|
||||||
CONF_AVAILABILITY_TOPIC = "availability_topic"
|
|
||||||
CONF_PAYLOAD_AVAILABLE = "payload_available"
|
|
||||||
CONF_PAYLOAD_NOT_AVAILABLE = "payload_not_available"
|
|
||||||
CONF_JSON_ATTRS_TOPIC = "json_attributes_topic"
|
|
||||||
CONF_JSON_ATTRS_TEMPLATE = "json_attributes_template"
|
|
||||||
|
|
||||||
CONF_IDENTIFIERS = "identifiers"
|
|
||||||
CONF_CONNECTIONS = "connections"
|
|
||||||
CONF_MANUFACTURER = "manufacturer"
|
|
||||||
CONF_MODEL = "model"
|
|
||||||
CONF_SW_VERSION = "sw_version"
|
|
||||||
CONF_VIA_DEVICE = "via_device"
|
|
||||||
CONF_DEPRECATED_VIA_HUB = "via_hub"
|
|
||||||
|
|
||||||
PROTOCOL_31 = "3.1"
|
PROTOCOL_31 = "3.1"
|
||||||
|
|
||||||
|
@ -158,16 +123,6 @@ PLATFORMS = [
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def validate_device_has_at_least_one_identifier(value: ConfigType) -> ConfigType:
|
|
||||||
"""Validate that a device info entry has at least one identifying value."""
|
|
||||||
if not value.get(CONF_IDENTIFIERS) and not value.get(CONF_CONNECTIONS):
|
|
||||||
raise vol.Invalid(
|
|
||||||
"Device must have at least one identifying value in "
|
|
||||||
"'identifiers' and/or 'connections'"
|
|
||||||
)
|
|
||||||
return value
|
|
||||||
|
|
||||||
|
|
||||||
CLIENT_KEY_AUTH_MSG = (
|
CLIENT_KEY_AUTH_MSG = (
|
||||||
"client_key and client_cert must both be present in "
|
"client_key and client_cert must both be present in "
|
||||||
"the MQTT broker configuration"
|
"the MQTT broker configuration"
|
||||||
|
@ -243,69 +198,6 @@ CONFIG_SCHEMA = vol.Schema(
|
||||||
|
|
||||||
SCHEMA_BASE = {vol.Optional(CONF_QOS, default=DEFAULT_QOS): _VALID_QOS_SCHEMA}
|
SCHEMA_BASE = {vol.Optional(CONF_QOS, default=DEFAULT_QOS): _VALID_QOS_SCHEMA}
|
||||||
|
|
||||||
MQTT_AVAILABILITY_SINGLE_SCHEMA = vol.Schema(
|
|
||||||
{
|
|
||||||
vol.Exclusive(CONF_AVAILABILITY_TOPIC, "availability"): valid_subscribe_topic,
|
|
||||||
vol.Optional(
|
|
||||||
CONF_PAYLOAD_AVAILABLE, default=DEFAULT_PAYLOAD_AVAILABLE
|
|
||||||
): cv.string,
|
|
||||||
vol.Optional(
|
|
||||||
CONF_PAYLOAD_NOT_AVAILABLE, default=DEFAULT_PAYLOAD_NOT_AVAILABLE
|
|
||||||
): cv.string,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
MQTT_AVAILABILITY_LIST_SCHEMA = vol.Schema(
|
|
||||||
{
|
|
||||||
vol.Exclusive(CONF_AVAILABILITY, "availability"): vol.All(
|
|
||||||
cv.ensure_list,
|
|
||||||
[
|
|
||||||
{
|
|
||||||
vol.Optional(CONF_TOPIC): valid_subscribe_topic,
|
|
||||||
vol.Optional(
|
|
||||||
CONF_PAYLOAD_AVAILABLE, default=DEFAULT_PAYLOAD_AVAILABLE
|
|
||||||
): cv.string,
|
|
||||||
vol.Optional(
|
|
||||||
CONF_PAYLOAD_NOT_AVAILABLE,
|
|
||||||
default=DEFAULT_PAYLOAD_NOT_AVAILABLE,
|
|
||||||
): cv.string,
|
|
||||||
}
|
|
||||||
],
|
|
||||||
),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
MQTT_AVAILABILITY_SCHEMA = MQTT_AVAILABILITY_SINGLE_SCHEMA.extend(
|
|
||||||
MQTT_AVAILABILITY_LIST_SCHEMA.schema
|
|
||||||
)
|
|
||||||
|
|
||||||
MQTT_ENTITY_DEVICE_INFO_SCHEMA = vol.All(
|
|
||||||
cv.deprecated(CONF_DEPRECATED_VIA_HUB, CONF_VIA_DEVICE),
|
|
||||||
vol.Schema(
|
|
||||||
{
|
|
||||||
vol.Optional(CONF_IDENTIFIERS, default=list): vol.All(
|
|
||||||
cv.ensure_list, [cv.string]
|
|
||||||
),
|
|
||||||
vol.Optional(CONF_CONNECTIONS, default=list): vol.All(
|
|
||||||
cv.ensure_list, [vol.All(vol.Length(2), [cv.string])]
|
|
||||||
),
|
|
||||||
vol.Optional(CONF_MANUFACTURER): cv.string,
|
|
||||||
vol.Optional(CONF_MODEL): cv.string,
|
|
||||||
vol.Optional(CONF_NAME): cv.string,
|
|
||||||
vol.Optional(CONF_SW_VERSION): cv.string,
|
|
||||||
vol.Optional(CONF_VIA_DEVICE): cv.string,
|
|
||||||
}
|
|
||||||
),
|
|
||||||
validate_device_has_at_least_one_identifier,
|
|
||||||
)
|
|
||||||
|
|
||||||
MQTT_JSON_ATTRS_SCHEMA = vol.Schema(
|
|
||||||
{
|
|
||||||
vol.Optional(CONF_JSON_ATTRS_TOPIC): valid_subscribe_topic,
|
|
||||||
vol.Optional(CONF_JSON_ATTRS_TEMPLATE): cv.template,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
MQTT_BASE_PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend(SCHEMA_BASE)
|
MQTT_BASE_PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend(SCHEMA_BASE)
|
||||||
|
|
||||||
# Sensor type platforms subscribe to MQTT events
|
# Sensor type platforms subscribe to MQTT events
|
||||||
|
@ -1085,347 +977,6 @@ def _matcher_for_topic(subscription: str) -> Any:
|
||||||
return lambda topic: next(matcher.iter_match(topic), False)
|
return lambda topic: next(matcher.iter_match(topic), False)
|
||||||
|
|
||||||
|
|
||||||
class MqttAttributes(Entity):
|
|
||||||
"""Mixin used for platforms that support JSON attributes."""
|
|
||||||
|
|
||||||
def __init__(self, config: dict) -> None:
|
|
||||||
"""Initialize the JSON attributes mixin."""
|
|
||||||
self._attributes = None
|
|
||||||
self._attributes_sub_state = None
|
|
||||||
self._attributes_config = config
|
|
||||||
|
|
||||||
async def async_added_to_hass(self) -> None:
|
|
||||||
"""Subscribe MQTT events."""
|
|
||||||
await super().async_added_to_hass()
|
|
||||||
await self._attributes_subscribe_topics()
|
|
||||||
|
|
||||||
async def attributes_discovery_update(self, config: dict):
|
|
||||||
"""Handle updated discovery message."""
|
|
||||||
self._attributes_config = config
|
|
||||||
await self._attributes_subscribe_topics()
|
|
||||||
|
|
||||||
async def _attributes_subscribe_topics(self):
|
|
||||||
"""(Re)Subscribe to topics."""
|
|
||||||
attr_tpl = self._attributes_config.get(CONF_JSON_ATTRS_TEMPLATE)
|
|
||||||
if attr_tpl is not None:
|
|
||||||
attr_tpl.hass = self.hass
|
|
||||||
|
|
||||||
@callback
|
|
||||||
@log_messages(self.hass, self.entity_id)
|
|
||||||
def attributes_message_received(msg: Message) -> None:
|
|
||||||
try:
|
|
||||||
payload = msg.payload
|
|
||||||
if attr_tpl is not None:
|
|
||||||
payload = attr_tpl.async_render_with_possible_json_value(payload)
|
|
||||||
json_dict = json.loads(payload)
|
|
||||||
if isinstance(json_dict, dict):
|
|
||||||
self._attributes = json_dict
|
|
||||||
self.async_write_ha_state()
|
|
||||||
else:
|
|
||||||
_LOGGER.warning("JSON result was not a dictionary")
|
|
||||||
self._attributes = None
|
|
||||||
except ValueError:
|
|
||||||
_LOGGER.warning("Erroneous JSON: %s", payload)
|
|
||||||
self._attributes = None
|
|
||||||
|
|
||||||
self._attributes_sub_state = await async_subscribe_topics(
|
|
||||||
self.hass,
|
|
||||||
self._attributes_sub_state,
|
|
||||||
{
|
|
||||||
CONF_JSON_ATTRS_TOPIC: {
|
|
||||||
"topic": self._attributes_config.get(CONF_JSON_ATTRS_TOPIC),
|
|
||||||
"msg_callback": attributes_message_received,
|
|
||||||
"qos": self._attributes_config.get(CONF_QOS),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
async def async_will_remove_from_hass(self):
|
|
||||||
"""Unsubscribe when removed."""
|
|
||||||
self._attributes_sub_state = await async_unsubscribe_topics(
|
|
||||||
self.hass, self._attributes_sub_state
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def device_state_attributes(self):
|
|
||||||
"""Return the state attributes."""
|
|
||||||
return self._attributes
|
|
||||||
|
|
||||||
|
|
||||||
class MqttAvailability(Entity):
|
|
||||||
"""Mixin used for platforms that report availability."""
|
|
||||||
|
|
||||||
def __init__(self, config: dict) -> None:
|
|
||||||
"""Initialize the availability mixin."""
|
|
||||||
self._availability_sub_state = None
|
|
||||||
self._available = False
|
|
||||||
self._availability_setup_from_config(config)
|
|
||||||
|
|
||||||
async def async_added_to_hass(self) -> None:
|
|
||||||
"""Subscribe MQTT events."""
|
|
||||||
await super().async_added_to_hass()
|
|
||||||
await self._availability_subscribe_topics()
|
|
||||||
self.async_on_remove(
|
|
||||||
async_dispatcher_connect(self.hass, MQTT_CONNECTED, self.async_mqtt_connect)
|
|
||||||
)
|
|
||||||
self.async_on_remove(
|
|
||||||
async_dispatcher_connect(
|
|
||||||
self.hass, MQTT_DISCONNECTED, self.async_mqtt_connect
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
async def availability_discovery_update(self, config: dict):
|
|
||||||
"""Handle updated discovery message."""
|
|
||||||
self._availability_setup_from_config(config)
|
|
||||||
await self._availability_subscribe_topics()
|
|
||||||
|
|
||||||
def _availability_setup_from_config(self, config):
|
|
||||||
"""(Re)Setup."""
|
|
||||||
self._avail_topics = {}
|
|
||||||
if CONF_AVAILABILITY_TOPIC in config:
|
|
||||||
self._avail_topics[config[CONF_AVAILABILITY_TOPIC]] = {
|
|
||||||
CONF_PAYLOAD_AVAILABLE: config[CONF_PAYLOAD_AVAILABLE],
|
|
||||||
CONF_PAYLOAD_NOT_AVAILABLE: config[CONF_PAYLOAD_NOT_AVAILABLE],
|
|
||||||
}
|
|
||||||
|
|
||||||
if CONF_AVAILABILITY in config:
|
|
||||||
for avail in config[CONF_AVAILABILITY]:
|
|
||||||
self._avail_topics[avail[CONF_TOPIC]] = {
|
|
||||||
CONF_PAYLOAD_AVAILABLE: avail[CONF_PAYLOAD_AVAILABLE],
|
|
||||||
CONF_PAYLOAD_NOT_AVAILABLE: avail[CONF_PAYLOAD_NOT_AVAILABLE],
|
|
||||||
}
|
|
||||||
|
|
||||||
self._avail_config = config
|
|
||||||
|
|
||||||
async def _availability_subscribe_topics(self):
|
|
||||||
"""(Re)Subscribe to topics."""
|
|
||||||
|
|
||||||
@callback
|
|
||||||
@log_messages(self.hass, self.entity_id)
|
|
||||||
def availability_message_received(msg: Message) -> None:
|
|
||||||
"""Handle a new received MQTT availability message."""
|
|
||||||
topic = msg.topic
|
|
||||||
if msg.payload == self._avail_topics[topic][CONF_PAYLOAD_AVAILABLE]:
|
|
||||||
self._available = True
|
|
||||||
elif msg.payload == self._avail_topics[topic][CONF_PAYLOAD_NOT_AVAILABLE]:
|
|
||||||
self._available = False
|
|
||||||
|
|
||||||
self.async_write_ha_state()
|
|
||||||
|
|
||||||
topics = {}
|
|
||||||
for topic in self._avail_topics:
|
|
||||||
topics[f"availability_{topic}"] = {
|
|
||||||
"topic": topic,
|
|
||||||
"msg_callback": availability_message_received,
|
|
||||||
"qos": self._avail_config[CONF_QOS],
|
|
||||||
}
|
|
||||||
|
|
||||||
self._availability_sub_state = await async_subscribe_topics(
|
|
||||||
self.hass,
|
|
||||||
self._availability_sub_state,
|
|
||||||
topics,
|
|
||||||
)
|
|
||||||
|
|
||||||
@callback
|
|
||||||
def async_mqtt_connect(self):
|
|
||||||
"""Update state on connection/disconnection to MQTT broker."""
|
|
||||||
if not self.hass.is_stopping:
|
|
||||||
self.async_write_ha_state()
|
|
||||||
|
|
||||||
async def async_will_remove_from_hass(self):
|
|
||||||
"""Unsubscribe when removed."""
|
|
||||||
self._availability_sub_state = await async_unsubscribe_topics(
|
|
||||||
self.hass, self._availability_sub_state
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def available(self) -> bool:
|
|
||||||
"""Return if the device is available."""
|
|
||||||
if not self.hass.data[DATA_MQTT].connected and not self.hass.is_stopping:
|
|
||||||
return False
|
|
||||||
return not self._avail_topics or self._available
|
|
||||||
|
|
||||||
|
|
||||||
async def cleanup_device_registry(hass, device_id):
|
|
||||||
"""Remove device registry entry if there are no remaining entities or triggers."""
|
|
||||||
# Local import to avoid circular dependencies
|
|
||||||
# pylint: disable=import-outside-toplevel
|
|
||||||
from . import device_trigger, tag
|
|
||||||
|
|
||||||
device_registry = await hass.helpers.device_registry.async_get_registry()
|
|
||||||
entity_registry = await hass.helpers.entity_registry.async_get_registry()
|
|
||||||
if (
|
|
||||||
device_id
|
|
||||||
and not hass.helpers.entity_registry.async_entries_for_device(
|
|
||||||
entity_registry, device_id, include_disabled_entities=True
|
|
||||||
)
|
|
||||||
and not await device_trigger.async_get_triggers(hass, device_id)
|
|
||||||
and not tag.async_has_tags(hass, device_id)
|
|
||||||
):
|
|
||||||
device_registry.async_remove_device(device_id)
|
|
||||||
|
|
||||||
|
|
||||||
class MqttDiscoveryUpdate(Entity):
|
|
||||||
"""Mixin used to handle updated discovery message."""
|
|
||||||
|
|
||||||
def __init__(self, discovery_data, discovery_update=None) -> None:
|
|
||||||
"""Initialize the discovery update mixin."""
|
|
||||||
self._discovery_data = discovery_data
|
|
||||||
self._discovery_update = discovery_update
|
|
||||||
self._remove_signal = None
|
|
||||||
self._removed_from_hass = False
|
|
||||||
|
|
||||||
async def async_added_to_hass(self) -> None:
|
|
||||||
"""Subscribe to discovery updates."""
|
|
||||||
await super().async_added_to_hass()
|
|
||||||
self._removed_from_hass = False
|
|
||||||
discovery_hash = (
|
|
||||||
self._discovery_data[ATTR_DISCOVERY_HASH] if self._discovery_data else None
|
|
||||||
)
|
|
||||||
|
|
||||||
async def _async_remove_state_and_registry_entry(self) -> None:
|
|
||||||
"""Remove entity's state and entity registry entry.
|
|
||||||
|
|
||||||
Remove entity from entity registry if it is registered, this also removes the state.
|
|
||||||
If the entity is not in the entity registry, just remove the state.
|
|
||||||
"""
|
|
||||||
entity_registry = (
|
|
||||||
await self.hass.helpers.entity_registry.async_get_registry()
|
|
||||||
)
|
|
||||||
if entity_registry.async_is_registered(self.entity_id):
|
|
||||||
entity_entry = entity_registry.async_get(self.entity_id)
|
|
||||||
entity_registry.async_remove(self.entity_id)
|
|
||||||
await cleanup_device_registry(self.hass, entity_entry.device_id)
|
|
||||||
else:
|
|
||||||
await self.async_remove()
|
|
||||||
|
|
||||||
async def discovery_callback(payload):
|
|
||||||
"""Handle discovery update."""
|
|
||||||
_LOGGER.info(
|
|
||||||
"Got update for entity with hash: %s '%s'",
|
|
||||||
discovery_hash,
|
|
||||||
payload,
|
|
||||||
)
|
|
||||||
old_payload = self._discovery_data[ATTR_DISCOVERY_PAYLOAD]
|
|
||||||
debug_info.update_entity_discovery_data(self.hass, payload, self.entity_id)
|
|
||||||
if not payload:
|
|
||||||
# Empty payload: Remove component
|
|
||||||
_LOGGER.info("Removing component: %s", self.entity_id)
|
|
||||||
self._cleanup_discovery_on_remove()
|
|
||||||
await _async_remove_state_and_registry_entry(self)
|
|
||||||
elif self._discovery_update:
|
|
||||||
if old_payload != self._discovery_data[ATTR_DISCOVERY_PAYLOAD]:
|
|
||||||
# Non-empty, changed payload: Notify component
|
|
||||||
_LOGGER.info("Updating component: %s", self.entity_id)
|
|
||||||
await self._discovery_update(payload)
|
|
||||||
else:
|
|
||||||
# Non-empty, unchanged payload: Ignore to avoid changing states
|
|
||||||
_LOGGER.info("Ignoring unchanged update for: %s", self.entity_id)
|
|
||||||
async_dispatcher_send(
|
|
||||||
self.hass, MQTT_DISCOVERY_DONE.format(discovery_hash), None
|
|
||||||
)
|
|
||||||
|
|
||||||
if discovery_hash:
|
|
||||||
debug_info.add_entity_discovery_data(
|
|
||||||
self.hass, self._discovery_data, self.entity_id
|
|
||||||
)
|
|
||||||
# Set in case the entity has been removed and is re-added, for example when changing entity_id
|
|
||||||
set_discovery_hash(self.hass, discovery_hash)
|
|
||||||
self._remove_signal = async_dispatcher_connect(
|
|
||||||
self.hass,
|
|
||||||
MQTT_DISCOVERY_UPDATED.format(discovery_hash),
|
|
||||||
discovery_callback,
|
|
||||||
)
|
|
||||||
async_dispatcher_send(
|
|
||||||
self.hass, MQTT_DISCOVERY_DONE.format(discovery_hash), None
|
|
||||||
)
|
|
||||||
|
|
||||||
async def async_removed_from_registry(self) -> None:
|
|
||||||
"""Clear retained discovery topic in broker."""
|
|
||||||
if not self._removed_from_hass:
|
|
||||||
discovery_topic = self._discovery_data[ATTR_DISCOVERY_TOPIC]
|
|
||||||
publish(self.hass, discovery_topic, "", retain=True)
|
|
||||||
|
|
||||||
@callback
|
|
||||||
def add_to_platform_abort(self) -> None:
|
|
||||||
"""Abort adding an entity to a platform."""
|
|
||||||
if self._discovery_data:
|
|
||||||
discovery_hash = self._discovery_data[ATTR_DISCOVERY_HASH]
|
|
||||||
clear_discovery_hash(self.hass, discovery_hash)
|
|
||||||
async_dispatcher_send(
|
|
||||||
self.hass, MQTT_DISCOVERY_DONE.format(discovery_hash), None
|
|
||||||
)
|
|
||||||
super().add_to_platform_abort()
|
|
||||||
|
|
||||||
async def async_will_remove_from_hass(self) -> None:
|
|
||||||
"""Stop listening to signal and cleanup discovery data.."""
|
|
||||||
self._cleanup_discovery_on_remove()
|
|
||||||
|
|
||||||
def _cleanup_discovery_on_remove(self) -> None:
|
|
||||||
"""Stop listening to signal and cleanup discovery data."""
|
|
||||||
if self._discovery_data and not self._removed_from_hass:
|
|
||||||
debug_info.remove_entity_data(self.hass, self.entity_id)
|
|
||||||
clear_discovery_hash(self.hass, self._discovery_data[ATTR_DISCOVERY_HASH])
|
|
||||||
self._removed_from_hass = True
|
|
||||||
|
|
||||||
if self._remove_signal:
|
|
||||||
self._remove_signal()
|
|
||||||
self._remove_signal = None
|
|
||||||
|
|
||||||
|
|
||||||
def device_info_from_config(config):
|
|
||||||
"""Return a device description for device registry."""
|
|
||||||
if not config:
|
|
||||||
return None
|
|
||||||
|
|
||||||
info = {
|
|
||||||
"identifiers": {(DOMAIN, id_) for id_ in config[CONF_IDENTIFIERS]},
|
|
||||||
"connections": {tuple(x) for x in config[CONF_CONNECTIONS]},
|
|
||||||
}
|
|
||||||
|
|
||||||
if CONF_MANUFACTURER in config:
|
|
||||||
info["manufacturer"] = config[CONF_MANUFACTURER]
|
|
||||||
|
|
||||||
if CONF_MODEL in config:
|
|
||||||
info["model"] = config[CONF_MODEL]
|
|
||||||
|
|
||||||
if CONF_NAME in config:
|
|
||||||
info["name"] = config[CONF_NAME]
|
|
||||||
|
|
||||||
if CONF_SW_VERSION in config:
|
|
||||||
info["sw_version"] = config[CONF_SW_VERSION]
|
|
||||||
|
|
||||||
if CONF_VIA_DEVICE in config:
|
|
||||||
info["via_device"] = (DOMAIN, config[CONF_VIA_DEVICE])
|
|
||||||
|
|
||||||
return info
|
|
||||||
|
|
||||||
|
|
||||||
class MqttEntityDeviceInfo(Entity):
|
|
||||||
"""Mixin used for mqtt platforms that support the device registry."""
|
|
||||||
|
|
||||||
def __init__(self, device_config: Optional[ConfigType], config_entry=None) -> None:
|
|
||||||
"""Initialize the device mixin."""
|
|
||||||
self._device_config = device_config
|
|
||||||
self._config_entry = config_entry
|
|
||||||
|
|
||||||
async def device_info_discovery_update(self, config: dict):
|
|
||||||
"""Handle updated discovery message."""
|
|
||||||
self._device_config = config.get(CONF_DEVICE)
|
|
||||||
device_registry = await self.hass.helpers.device_registry.async_get_registry()
|
|
||||||
config_entry_id = self._config_entry.entry_id
|
|
||||||
device_info = self.device_info
|
|
||||||
|
|
||||||
if config_entry_id is not None and device_info is not None:
|
|
||||||
device_info["config_entry_id"] = config_entry_id
|
|
||||||
device_registry.async_get_or_create(**device_info)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def device_info(self):
|
|
||||||
"""Return a device description for device registry."""
|
|
||||||
return device_info_from_config(self._device_config)
|
|
||||||
|
|
||||||
|
|
||||||
@websocket_api.websocket_command(
|
@websocket_api.websocket_command(
|
||||||
{vol.Required("type"): "mqtt/device/debug_info", vol.Required("device_id"): str}
|
{vol.Required("type"): "mqtt/device/debug_info", vol.Required("device_id"): str}
|
||||||
)
|
)
|
||||||
|
|
|
@ -37,22 +37,27 @@ from homeassistant.helpers.reload import async_setup_reload_service
|
||||||
from homeassistant.helpers.typing import ConfigType, HomeAssistantType
|
from homeassistant.helpers.typing import ConfigType, HomeAssistantType
|
||||||
|
|
||||||
from . import (
|
from . import (
|
||||||
ATTR_DISCOVERY_HASH,
|
|
||||||
CONF_COMMAND_TOPIC,
|
CONF_COMMAND_TOPIC,
|
||||||
CONF_QOS,
|
CONF_QOS,
|
||||||
CONF_RETAIN,
|
CONF_RETAIN,
|
||||||
CONF_STATE_TOPIC,
|
CONF_STATE_TOPIC,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
PLATFORMS,
|
PLATFORMS,
|
||||||
|
subscription,
|
||||||
|
)
|
||||||
|
from .. import mqtt
|
||||||
|
from .const import ATTR_DISCOVERY_HASH
|
||||||
|
from .debug_info import log_messages
|
||||||
|
from .discovery import MQTT_DISCOVERY_DONE, MQTT_DISCOVERY_NEW, clear_discovery_hash
|
||||||
|
from .mixins import (
|
||||||
|
MQTT_AVAILABILITY_SCHEMA,
|
||||||
|
MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
||||||
|
MQTT_JSON_ATTRS_SCHEMA,
|
||||||
MqttAttributes,
|
MqttAttributes,
|
||||||
MqttAvailability,
|
MqttAvailability,
|
||||||
MqttDiscoveryUpdate,
|
MqttDiscoveryUpdate,
|
||||||
MqttEntityDeviceInfo,
|
MqttEntityDeviceInfo,
|
||||||
subscription,
|
|
||||||
)
|
)
|
||||||
from .. import mqtt
|
|
||||||
from .debug_info import log_messages
|
|
||||||
from .discovery import MQTT_DISCOVERY_DONE, MQTT_DISCOVERY_NEW, clear_discovery_hash
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -82,7 +87,7 @@ PLATFORM_SCHEMA = (
|
||||||
CONF_COMMAND_TEMPLATE, default=DEFAULT_COMMAND_TEMPLATE
|
CONF_COMMAND_TEMPLATE, default=DEFAULT_COMMAND_TEMPLATE
|
||||||
): cv.template,
|
): cv.template,
|
||||||
vol.Required(CONF_COMMAND_TOPIC): mqtt.valid_publish_topic,
|
vol.Required(CONF_COMMAND_TOPIC): mqtt.valid_publish_topic,
|
||||||
vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
vol.Optional(CONF_DEVICE): MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
||||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||||
vol.Optional(CONF_PAYLOAD_ARM_AWAY, default=DEFAULT_ARM_AWAY): cv.string,
|
vol.Optional(CONF_PAYLOAD_ARM_AWAY, default=DEFAULT_ARM_AWAY): cv.string,
|
||||||
vol.Optional(CONF_PAYLOAD_ARM_HOME, default=DEFAULT_ARM_HOME): cv.string,
|
vol.Optional(CONF_PAYLOAD_ARM_HOME, default=DEFAULT_ARM_HOME): cv.string,
|
||||||
|
@ -97,8 +102,8 @@ PLATFORM_SCHEMA = (
|
||||||
vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
|
vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema)
|
.extend(MQTT_AVAILABILITY_SCHEMA.schema)
|
||||||
.extend(mqtt.MQTT_JSON_ATTRS_SCHEMA.schema)
|
.extend(MQTT_JSON_ATTRS_SCHEMA.schema)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -31,21 +31,20 @@ from homeassistant.helpers.reload import async_setup_reload_service
|
||||||
from homeassistant.helpers.typing import ConfigType, HomeAssistantType
|
from homeassistant.helpers.typing import ConfigType, HomeAssistantType
|
||||||
from homeassistant.util import dt as dt_util
|
from homeassistant.util import dt as dt_util
|
||||||
|
|
||||||
from . import (
|
from . import CONF_QOS, CONF_STATE_TOPIC, DOMAIN, PLATFORMS, subscription
|
||||||
ATTR_DISCOVERY_HASH,
|
from .. import mqtt
|
||||||
CONF_QOS,
|
from .const import ATTR_DISCOVERY_HASH
|
||||||
CONF_STATE_TOPIC,
|
from .debug_info import log_messages
|
||||||
DOMAIN,
|
from .discovery import MQTT_DISCOVERY_DONE, MQTT_DISCOVERY_NEW, clear_discovery_hash
|
||||||
PLATFORMS,
|
from .mixins import (
|
||||||
|
MQTT_AVAILABILITY_SCHEMA,
|
||||||
|
MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
||||||
|
MQTT_JSON_ATTRS_SCHEMA,
|
||||||
MqttAttributes,
|
MqttAttributes,
|
||||||
MqttAvailability,
|
MqttAvailability,
|
||||||
MqttDiscoveryUpdate,
|
MqttDiscoveryUpdate,
|
||||||
MqttEntityDeviceInfo,
|
MqttEntityDeviceInfo,
|
||||||
subscription,
|
|
||||||
)
|
)
|
||||||
from .. import mqtt
|
|
||||||
from .debug_info import log_messages
|
|
||||||
from .discovery import MQTT_DISCOVERY_DONE, MQTT_DISCOVERY_NEW, clear_discovery_hash
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -59,7 +58,7 @@ CONF_EXPIRE_AFTER = "expire_after"
|
||||||
PLATFORM_SCHEMA = (
|
PLATFORM_SCHEMA = (
|
||||||
mqtt.MQTT_RO_PLATFORM_SCHEMA.extend(
|
mqtt.MQTT_RO_PLATFORM_SCHEMA.extend(
|
||||||
{
|
{
|
||||||
vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
vol.Optional(CONF_DEVICE): MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
||||||
vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA,
|
vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA,
|
||||||
vol.Optional(CONF_EXPIRE_AFTER): cv.positive_int,
|
vol.Optional(CONF_EXPIRE_AFTER): cv.positive_int,
|
||||||
vol.Optional(CONF_FORCE_UPDATE, default=DEFAULT_FORCE_UPDATE): cv.boolean,
|
vol.Optional(CONF_FORCE_UPDATE, default=DEFAULT_FORCE_UPDATE): cv.boolean,
|
||||||
|
@ -70,8 +69,8 @@ PLATFORM_SCHEMA = (
|
||||||
vol.Optional(CONF_UNIQUE_ID): cv.string,
|
vol.Optional(CONF_UNIQUE_ID): cv.string,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema)
|
.extend(MQTT_AVAILABILITY_SCHEMA.schema)
|
||||||
.extend(mqtt.MQTT_JSON_ATTRS_SCHEMA.schema)
|
.extend(MQTT_JSON_ATTRS_SCHEMA.schema)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -15,20 +15,20 @@ from homeassistant.helpers.dispatcher import (
|
||||||
from homeassistant.helpers.reload import async_setup_reload_service
|
from homeassistant.helpers.reload import async_setup_reload_service
|
||||||
from homeassistant.helpers.typing import ConfigType, HomeAssistantType
|
from homeassistant.helpers.typing import ConfigType, HomeAssistantType
|
||||||
|
|
||||||
from . import (
|
from . import CONF_QOS, DOMAIN, PLATFORMS, subscription
|
||||||
ATTR_DISCOVERY_HASH,
|
from .. import mqtt
|
||||||
CONF_QOS,
|
from .const import ATTR_DISCOVERY_HASH
|
||||||
DOMAIN,
|
from .debug_info import log_messages
|
||||||
PLATFORMS,
|
from .discovery import MQTT_DISCOVERY_DONE, MQTT_DISCOVERY_NEW, clear_discovery_hash
|
||||||
|
from .mixins import (
|
||||||
|
MQTT_AVAILABILITY_SCHEMA,
|
||||||
|
MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
||||||
|
MQTT_JSON_ATTRS_SCHEMA,
|
||||||
MqttAttributes,
|
MqttAttributes,
|
||||||
MqttAvailability,
|
MqttAvailability,
|
||||||
MqttDiscoveryUpdate,
|
MqttDiscoveryUpdate,
|
||||||
MqttEntityDeviceInfo,
|
MqttEntityDeviceInfo,
|
||||||
subscription,
|
|
||||||
)
|
)
|
||||||
from .. import mqtt
|
|
||||||
from .debug_info import log_messages
|
|
||||||
from .discovery import MQTT_DISCOVERY_DONE, MQTT_DISCOVERY_NEW, clear_discovery_hash
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -38,14 +38,14 @@ DEFAULT_NAME = "MQTT Camera"
|
||||||
PLATFORM_SCHEMA = (
|
PLATFORM_SCHEMA = (
|
||||||
mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend(
|
mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend(
|
||||||
{
|
{
|
||||||
vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
vol.Optional(CONF_DEVICE): MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
||||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||||
vol.Required(CONF_TOPIC): mqtt.valid_subscribe_topic,
|
vol.Required(CONF_TOPIC): mqtt.valid_subscribe_topic,
|
||||||
vol.Optional(CONF_UNIQUE_ID): cv.string,
|
vol.Optional(CONF_UNIQUE_ID): cv.string,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema)
|
.extend(MQTT_AVAILABILITY_SCHEMA.schema)
|
||||||
.extend(mqtt.MQTT_JSON_ATTRS_SCHEMA.schema)
|
.extend(MQTT_JSON_ATTRS_SCHEMA.schema)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -55,21 +55,26 @@ from homeassistant.helpers.reload import async_setup_reload_service
|
||||||
from homeassistant.helpers.typing import ConfigType, HomeAssistantType
|
from homeassistant.helpers.typing import ConfigType, HomeAssistantType
|
||||||
|
|
||||||
from . import (
|
from . import (
|
||||||
ATTR_DISCOVERY_HASH,
|
|
||||||
CONF_QOS,
|
CONF_QOS,
|
||||||
CONF_RETAIN,
|
CONF_RETAIN,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
MQTT_BASE_PLATFORM_SCHEMA,
|
MQTT_BASE_PLATFORM_SCHEMA,
|
||||||
PLATFORMS,
|
PLATFORMS,
|
||||||
|
subscription,
|
||||||
|
)
|
||||||
|
from .. import mqtt
|
||||||
|
from .const import ATTR_DISCOVERY_HASH
|
||||||
|
from .debug_info import log_messages
|
||||||
|
from .discovery import MQTT_DISCOVERY_DONE, MQTT_DISCOVERY_NEW, clear_discovery_hash
|
||||||
|
from .mixins import (
|
||||||
|
MQTT_AVAILABILITY_SCHEMA,
|
||||||
|
MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
||||||
|
MQTT_JSON_ATTRS_SCHEMA,
|
||||||
MqttAttributes,
|
MqttAttributes,
|
||||||
MqttAvailability,
|
MqttAvailability,
|
||||||
MqttDiscoveryUpdate,
|
MqttDiscoveryUpdate,
|
||||||
MqttEntityDeviceInfo,
|
MqttEntityDeviceInfo,
|
||||||
subscription,
|
|
||||||
)
|
)
|
||||||
from .. import mqtt
|
|
||||||
from .debug_info import log_messages
|
|
||||||
from .discovery import MQTT_DISCOVERY_DONE, MQTT_DISCOVERY_NEW, clear_discovery_hash
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -174,7 +179,7 @@ PLATFORM_SCHEMA = (
|
||||||
vol.Optional(CONF_AWAY_MODE_STATE_TOPIC): mqtt.valid_subscribe_topic,
|
vol.Optional(CONF_AWAY_MODE_STATE_TOPIC): mqtt.valid_subscribe_topic,
|
||||||
vol.Optional(CONF_CURRENT_TEMP_TEMPLATE): cv.template,
|
vol.Optional(CONF_CURRENT_TEMP_TEMPLATE): cv.template,
|
||||||
vol.Optional(CONF_CURRENT_TEMP_TOPIC): mqtt.valid_subscribe_topic,
|
vol.Optional(CONF_CURRENT_TEMP_TOPIC): mqtt.valid_subscribe_topic,
|
||||||
vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
vol.Optional(CONF_DEVICE): MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
||||||
vol.Optional(CONF_FAN_MODE_COMMAND_TOPIC): mqtt.valid_publish_topic,
|
vol.Optional(CONF_FAN_MODE_COMMAND_TOPIC): mqtt.valid_publish_topic,
|
||||||
vol.Optional(
|
vol.Optional(
|
||||||
CONF_FAN_MODE_LIST,
|
CONF_FAN_MODE_LIST,
|
||||||
|
@ -237,8 +242,8 @@ PLATFORM_SCHEMA = (
|
||||||
vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
|
vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema)
|
.extend(MQTT_AVAILABILITY_SCHEMA.schema)
|
||||||
.extend(mqtt.MQTT_JSON_ATTRS_SCHEMA.schema)
|
.extend(MQTT_JSON_ATTRS_SCHEMA.schema)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -41,22 +41,27 @@ from homeassistant.helpers.reload import async_setup_reload_service
|
||||||
from homeassistant.helpers.typing import ConfigType, HomeAssistantType
|
from homeassistant.helpers.typing import ConfigType, HomeAssistantType
|
||||||
|
|
||||||
from . import (
|
from . import (
|
||||||
ATTR_DISCOVERY_HASH,
|
|
||||||
CONF_COMMAND_TOPIC,
|
CONF_COMMAND_TOPIC,
|
||||||
CONF_QOS,
|
CONF_QOS,
|
||||||
CONF_RETAIN,
|
CONF_RETAIN,
|
||||||
CONF_STATE_TOPIC,
|
CONF_STATE_TOPIC,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
PLATFORMS,
|
PLATFORMS,
|
||||||
|
subscription,
|
||||||
|
)
|
||||||
|
from .. import mqtt
|
||||||
|
from .const import ATTR_DISCOVERY_HASH
|
||||||
|
from .debug_info import log_messages
|
||||||
|
from .discovery import MQTT_DISCOVERY_DONE, MQTT_DISCOVERY_NEW, clear_discovery_hash
|
||||||
|
from .mixins import (
|
||||||
|
MQTT_AVAILABILITY_SCHEMA,
|
||||||
|
MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
||||||
|
MQTT_JSON_ATTRS_SCHEMA,
|
||||||
MqttAttributes,
|
MqttAttributes,
|
||||||
MqttAvailability,
|
MqttAvailability,
|
||||||
MqttDiscoveryUpdate,
|
MqttDiscoveryUpdate,
|
||||||
MqttEntityDeviceInfo,
|
MqttEntityDeviceInfo,
|
||||||
subscription,
|
|
||||||
)
|
)
|
||||||
from .. import mqtt
|
|
||||||
from .debug_info import log_messages
|
|
||||||
from .discovery import MQTT_DISCOVERY_DONE, MQTT_DISCOVERY_NEW, clear_discovery_hash
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -126,7 +131,7 @@ PLATFORM_SCHEMA = vol.All(
|
||||||
mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend(
|
mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend(
|
||||||
{
|
{
|
||||||
vol.Optional(CONF_COMMAND_TOPIC): mqtt.valid_publish_topic,
|
vol.Optional(CONF_COMMAND_TOPIC): mqtt.valid_publish_topic,
|
||||||
vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
vol.Optional(CONF_DEVICE): MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
||||||
vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA,
|
vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA,
|
||||||
vol.Optional(CONF_GET_POSITION_TOPIC): mqtt.valid_subscribe_topic,
|
vol.Optional(CONF_GET_POSITION_TOPIC): mqtt.valid_subscribe_topic,
|
||||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||||
|
@ -167,8 +172,8 @@ PLATFORM_SCHEMA = vol.All(
|
||||||
vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
|
vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema)
|
.extend(MQTT_AVAILABILITY_SCHEMA.schema)
|
||||||
.extend(mqtt.MQTT_JSON_ATTRS_SCHEMA.schema),
|
.extend(MQTT_JSON_ATTRS_SCHEMA.schema),
|
||||||
validate_options,
|
validate_options,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,9 @@ from homeassistant.helpers.dispatcher import (
|
||||||
async_dispatcher_send,
|
async_dispatcher_send,
|
||||||
)
|
)
|
||||||
|
|
||||||
from . import ATTR_DISCOVERY_HASH, device_trigger
|
from . import device_trigger
|
||||||
from .. import mqtt
|
from .. import mqtt
|
||||||
|
from .const import ATTR_DISCOVERY_HASH
|
||||||
from .discovery import MQTT_DISCOVERY_DONE, MQTT_DISCOVERY_NEW, clear_discovery_hash
|
from .discovery import MQTT_DISCOVERY_DONE, MQTT_DISCOVERY_NEW, clear_discovery_hash
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
|
@ -25,17 +25,20 @@ from homeassistant.helpers.dispatcher import (
|
||||||
async_dispatcher_send,
|
async_dispatcher_send,
|
||||||
)
|
)
|
||||||
|
|
||||||
from .. import (
|
from .. import subscription
|
||||||
MqttAttributes,
|
|
||||||
MqttAvailability,
|
|
||||||
MqttDiscoveryUpdate,
|
|
||||||
MqttEntityDeviceInfo,
|
|
||||||
subscription,
|
|
||||||
)
|
|
||||||
from ... import mqtt
|
from ... import mqtt
|
||||||
from ..const import ATTR_DISCOVERY_HASH, CONF_QOS, CONF_STATE_TOPIC
|
from ..const import ATTR_DISCOVERY_HASH, CONF_QOS, CONF_STATE_TOPIC
|
||||||
from ..debug_info import log_messages
|
from ..debug_info import log_messages
|
||||||
from ..discovery import MQTT_DISCOVERY_DONE, MQTT_DISCOVERY_NEW, clear_discovery_hash
|
from ..discovery import MQTT_DISCOVERY_DONE, MQTT_DISCOVERY_NEW, clear_discovery_hash
|
||||||
|
from ..mixins import (
|
||||||
|
MQTT_AVAILABILITY_SCHEMA,
|
||||||
|
MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
||||||
|
MQTT_JSON_ATTRS_SCHEMA,
|
||||||
|
MqttAttributes,
|
||||||
|
MqttAvailability,
|
||||||
|
MqttDiscoveryUpdate,
|
||||||
|
MqttEntityDeviceInfo,
|
||||||
|
)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -46,7 +49,7 @@ CONF_SOURCE_TYPE = "source_type"
|
||||||
PLATFORM_SCHEMA_DISCOVERY = (
|
PLATFORM_SCHEMA_DISCOVERY = (
|
||||||
mqtt.MQTT_RO_PLATFORM_SCHEMA.extend(
|
mqtt.MQTT_RO_PLATFORM_SCHEMA.extend(
|
||||||
{
|
{
|
||||||
vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
vol.Optional(CONF_DEVICE): MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
||||||
vol.Optional(CONF_ICON): cv.icon,
|
vol.Optional(CONF_ICON): cv.icon,
|
||||||
vol.Optional(CONF_NAME): cv.string,
|
vol.Optional(CONF_NAME): cv.string,
|
||||||
vol.Optional(CONF_PAYLOAD_HOME, default=STATE_HOME): cv.string,
|
vol.Optional(CONF_PAYLOAD_HOME, default=STATE_HOME): cv.string,
|
||||||
|
@ -55,8 +58,8 @@ PLATFORM_SCHEMA_DISCOVERY = (
|
||||||
vol.Optional(CONF_UNIQUE_ID): cv.string,
|
vol.Optional(CONF_UNIQUE_ID): cv.string,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema)
|
.extend(MQTT_AVAILABILITY_SCHEMA.schema)
|
||||||
.extend(mqtt.MQTT_JSON_ATTRS_SCHEMA.schema)
|
.extend(MQTT_JSON_ATTRS_SCHEMA.schema)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,13 @@ import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.automation import AutomationActionType
|
from homeassistant.components.automation import AutomationActionType
|
||||||
from homeassistant.components.device_automation import TRIGGER_BASE_SCHEMA
|
from homeassistant.components.device_automation import TRIGGER_BASE_SCHEMA
|
||||||
from homeassistant.const import CONF_DEVICE_ID, CONF_DOMAIN, CONF_PLATFORM, CONF_TYPE
|
from homeassistant.const import (
|
||||||
|
CONF_DEVICE,
|
||||||
|
CONF_DEVICE_ID,
|
||||||
|
CONF_DOMAIN,
|
||||||
|
CONF_PLATFORM,
|
||||||
|
CONF_TYPE,
|
||||||
|
)
|
||||||
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
|
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.helpers import config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
|
@ -17,21 +23,18 @@ from homeassistant.helpers.dispatcher import (
|
||||||
)
|
)
|
||||||
from homeassistant.helpers.typing import ConfigType, HomeAssistantType
|
from homeassistant.helpers.typing import ConfigType, HomeAssistantType
|
||||||
|
|
||||||
from . import (
|
from . import CONF_PAYLOAD, CONF_QOS, DOMAIN, debug_info, trigger as mqtt_trigger
|
||||||
ATTR_DISCOVERY_HASH,
|
|
||||||
ATTR_DISCOVERY_TOPIC,
|
|
||||||
CONF_CONNECTIONS,
|
|
||||||
CONF_DEVICE,
|
|
||||||
CONF_IDENTIFIERS,
|
|
||||||
CONF_PAYLOAD,
|
|
||||||
CONF_QOS,
|
|
||||||
DOMAIN,
|
|
||||||
cleanup_device_registry,
|
|
||||||
debug_info,
|
|
||||||
trigger as mqtt_trigger,
|
|
||||||
)
|
|
||||||
from .. import mqtt
|
from .. import mqtt
|
||||||
|
from .const import ATTR_DISCOVERY_HASH, ATTR_DISCOVERY_TOPIC
|
||||||
from .discovery import MQTT_DISCOVERY_DONE, MQTT_DISCOVERY_UPDATED, clear_discovery_hash
|
from .discovery import MQTT_DISCOVERY_DONE, MQTT_DISCOVERY_UPDATED, clear_discovery_hash
|
||||||
|
from .mixins import (
|
||||||
|
CONF_CONNECTIONS,
|
||||||
|
CONF_IDENTIFIERS,
|
||||||
|
MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
||||||
|
cleanup_device_registry,
|
||||||
|
device_info_from_config,
|
||||||
|
validate_device_has_at_least_one_identifier,
|
||||||
|
)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -62,13 +65,13 @@ TRIGGER_SCHEMA = TRIGGER_BASE_SCHEMA.extend(
|
||||||
TRIGGER_DISCOVERY_SCHEMA = mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend(
|
TRIGGER_DISCOVERY_SCHEMA = mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend(
|
||||||
{
|
{
|
||||||
vol.Required(CONF_AUTOMATION_TYPE): str,
|
vol.Required(CONF_AUTOMATION_TYPE): str,
|
||||||
vol.Required(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
vol.Required(CONF_DEVICE): MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
||||||
vol.Required(CONF_TOPIC): mqtt.valid_subscribe_topic,
|
vol.Required(CONF_TOPIC): mqtt.valid_subscribe_topic,
|
||||||
vol.Optional(CONF_PAYLOAD, default=None): vol.Any(None, cv.string),
|
vol.Optional(CONF_PAYLOAD, default=None): vol.Any(None, cv.string),
|
||||||
vol.Required(CONF_TYPE): cv.string,
|
vol.Required(CONF_TYPE): cv.string,
|
||||||
vol.Required(CONF_SUBTYPE): cv.string,
|
vol.Required(CONF_SUBTYPE): cv.string,
|
||||||
},
|
},
|
||||||
mqtt.validate_device_has_at_least_one_identifier,
|
validate_device_has_at_least_one_identifier,
|
||||||
)
|
)
|
||||||
|
|
||||||
DEVICE_TRIGGERS = "mqtt_device_triggers"
|
DEVICE_TRIGGERS = "mqtt_device_triggers"
|
||||||
|
@ -172,7 +175,7 @@ async def _update_device(hass, config_entry, config):
|
||||||
"""Update device registry."""
|
"""Update device registry."""
|
||||||
device_registry = await hass.helpers.device_registry.async_get_registry()
|
device_registry = await hass.helpers.device_registry.async_get_registry()
|
||||||
config_entry_id = config_entry.entry_id
|
config_entry_id = config_entry.entry_id
|
||||||
device_info = mqtt.device_info_from_config(config[CONF_DEVICE])
|
device_info = device_info_from_config(config[CONF_DEVICE])
|
||||||
|
|
||||||
if config_entry_id is not None and device_info is not None:
|
if config_entry_id is not None and device_info is not None:
|
||||||
device_info["config_entry_id"] = config_entry_id
|
device_info["config_entry_id"] = config_entry_id
|
||||||
|
|
|
@ -33,22 +33,27 @@ from homeassistant.helpers.reload import async_setup_reload_service
|
||||||
from homeassistant.helpers.typing import ConfigType, HomeAssistantType
|
from homeassistant.helpers.typing import ConfigType, HomeAssistantType
|
||||||
|
|
||||||
from . import (
|
from . import (
|
||||||
ATTR_DISCOVERY_HASH,
|
|
||||||
CONF_COMMAND_TOPIC,
|
CONF_COMMAND_TOPIC,
|
||||||
CONF_QOS,
|
CONF_QOS,
|
||||||
CONF_RETAIN,
|
CONF_RETAIN,
|
||||||
CONF_STATE_TOPIC,
|
CONF_STATE_TOPIC,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
PLATFORMS,
|
PLATFORMS,
|
||||||
|
subscription,
|
||||||
|
)
|
||||||
|
from .. import mqtt
|
||||||
|
from .const import ATTR_DISCOVERY_HASH
|
||||||
|
from .debug_info import log_messages
|
||||||
|
from .discovery import MQTT_DISCOVERY_DONE, MQTT_DISCOVERY_NEW, clear_discovery_hash
|
||||||
|
from .mixins import (
|
||||||
|
MQTT_AVAILABILITY_SCHEMA,
|
||||||
|
MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
||||||
|
MQTT_JSON_ATTRS_SCHEMA,
|
||||||
MqttAttributes,
|
MqttAttributes,
|
||||||
MqttAvailability,
|
MqttAvailability,
|
||||||
MqttDiscoveryUpdate,
|
MqttDiscoveryUpdate,
|
||||||
MqttEntityDeviceInfo,
|
MqttEntityDeviceInfo,
|
||||||
subscription,
|
|
||||||
)
|
)
|
||||||
from .. import mqtt
|
|
||||||
from .debug_info import log_messages
|
|
||||||
from .discovery import MQTT_DISCOVERY_DONE, MQTT_DISCOVERY_NEW, clear_discovery_hash
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -80,7 +85,7 @@ OSCILLATION = "oscillation"
|
||||||
PLATFORM_SCHEMA = (
|
PLATFORM_SCHEMA = (
|
||||||
mqtt.MQTT_RW_PLATFORM_SCHEMA.extend(
|
mqtt.MQTT_RW_PLATFORM_SCHEMA.extend(
|
||||||
{
|
{
|
||||||
vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
vol.Optional(CONF_DEVICE): MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
||||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||||
vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean,
|
vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean,
|
||||||
vol.Optional(CONF_OSCILLATION_COMMAND_TOPIC): mqtt.valid_publish_topic,
|
vol.Optional(CONF_OSCILLATION_COMMAND_TOPIC): mqtt.valid_publish_topic,
|
||||||
|
@ -109,8 +114,8 @@ PLATFORM_SCHEMA = (
|
||||||
vol.Optional(CONF_UNIQUE_ID): cv.string,
|
vol.Optional(CONF_UNIQUE_ID): cv.string,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema)
|
.extend(MQTT_AVAILABILITY_SCHEMA.schema)
|
||||||
.extend(mqtt.MQTT_JSON_ATTRS_SCHEMA.schema)
|
.extend(MQTT_JSON_ATTRS_SCHEMA.schema)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,8 @@ from homeassistant.helpers.dispatcher import (
|
||||||
from homeassistant.helpers.reload import async_setup_reload_service
|
from homeassistant.helpers.reload import async_setup_reload_service
|
||||||
from homeassistant.helpers.typing import ConfigType, HomeAssistantType
|
from homeassistant.helpers.typing import ConfigType, HomeAssistantType
|
||||||
|
|
||||||
from .. import ATTR_DISCOVERY_HASH, DOMAIN, PLATFORMS
|
from .. import DOMAIN, PLATFORMS
|
||||||
|
from ..const import ATTR_DISCOVERY_HASH
|
||||||
from ..discovery import MQTT_DISCOVERY_DONE, MQTT_DISCOVERY_NEW, clear_discovery_hash
|
from ..discovery import MQTT_DISCOVERY_DONE, MQTT_DISCOVERY_NEW, clear_discovery_hash
|
||||||
from .schema import CONF_SCHEMA, MQTT_LIGHT_SCHEMA_SCHEMA
|
from .schema import CONF_SCHEMA, MQTT_LIGHT_SCHEMA_SCHEMA
|
||||||
from .schema_basic import PLATFORM_SCHEMA_BASIC, async_setup_entity_basic
|
from .schema_basic import PLATFORM_SCHEMA_BASIC, async_setup_entity_basic
|
||||||
|
|
|
@ -31,19 +31,18 @@ import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers.restore_state import RestoreEntity
|
from homeassistant.helpers.restore_state import RestoreEntity
|
||||||
import homeassistant.util.color as color_util
|
import homeassistant.util.color as color_util
|
||||||
|
|
||||||
from .. import (
|
from .. import CONF_COMMAND_TOPIC, CONF_QOS, CONF_RETAIN, CONF_STATE_TOPIC, subscription
|
||||||
CONF_COMMAND_TOPIC,
|
from ... import mqtt
|
||||||
CONF_QOS,
|
from ..debug_info import log_messages
|
||||||
CONF_RETAIN,
|
from ..mixins import (
|
||||||
CONF_STATE_TOPIC,
|
MQTT_AVAILABILITY_SCHEMA,
|
||||||
|
MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
||||||
|
MQTT_JSON_ATTRS_SCHEMA,
|
||||||
MqttAttributes,
|
MqttAttributes,
|
||||||
MqttAvailability,
|
MqttAvailability,
|
||||||
MqttDiscoveryUpdate,
|
MqttDiscoveryUpdate,
|
||||||
MqttEntityDeviceInfo,
|
MqttEntityDeviceInfo,
|
||||||
subscription,
|
|
||||||
)
|
)
|
||||||
from ... import mqtt
|
|
||||||
from ..debug_info import log_messages
|
|
||||||
from .schema import MQTT_LIGHT_SCHEMA_SCHEMA
|
from .schema import MQTT_LIGHT_SCHEMA_SCHEMA
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
@ -114,7 +113,7 @@ PLATFORM_SCHEMA_BASIC = (
|
||||||
vol.Optional(CONF_COLOR_TEMP_COMMAND_TOPIC): mqtt.valid_publish_topic,
|
vol.Optional(CONF_COLOR_TEMP_COMMAND_TOPIC): mqtt.valid_publish_topic,
|
||||||
vol.Optional(CONF_COLOR_TEMP_STATE_TOPIC): mqtt.valid_subscribe_topic,
|
vol.Optional(CONF_COLOR_TEMP_STATE_TOPIC): mqtt.valid_subscribe_topic,
|
||||||
vol.Optional(CONF_COLOR_TEMP_VALUE_TEMPLATE): cv.template,
|
vol.Optional(CONF_COLOR_TEMP_VALUE_TEMPLATE): cv.template,
|
||||||
vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
vol.Optional(CONF_DEVICE): MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
||||||
vol.Optional(CONF_EFFECT_COMMAND_TOPIC): mqtt.valid_publish_topic,
|
vol.Optional(CONF_EFFECT_COMMAND_TOPIC): mqtt.valid_publish_topic,
|
||||||
vol.Optional(CONF_EFFECT_LIST): vol.All(cv.ensure_list, [cv.string]),
|
vol.Optional(CONF_EFFECT_LIST): vol.All(cv.ensure_list, [cv.string]),
|
||||||
vol.Optional(CONF_EFFECT_STATE_TOPIC): mqtt.valid_subscribe_topic,
|
vol.Optional(CONF_EFFECT_STATE_TOPIC): mqtt.valid_subscribe_topic,
|
||||||
|
@ -148,8 +147,8 @@ PLATFORM_SCHEMA_BASIC = (
|
||||||
vol.Optional(CONF_XY_VALUE_TEMPLATE): cv.template,
|
vol.Optional(CONF_XY_VALUE_TEMPLATE): cv.template,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema)
|
.extend(MQTT_AVAILABILITY_SCHEMA.schema)
|
||||||
.extend(mqtt.MQTT_JSON_ATTRS_SCHEMA.schema)
|
.extend(MQTT_JSON_ATTRS_SCHEMA.schema)
|
||||||
.extend(MQTT_LIGHT_SCHEMA_SCHEMA.schema)
|
.extend(MQTT_LIGHT_SCHEMA_SCHEMA.schema)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -42,19 +42,18 @@ from homeassistant.helpers.restore_state import RestoreEntity
|
||||||
from homeassistant.helpers.typing import ConfigType
|
from homeassistant.helpers.typing import ConfigType
|
||||||
import homeassistant.util.color as color_util
|
import homeassistant.util.color as color_util
|
||||||
|
|
||||||
from .. import (
|
from .. import CONF_COMMAND_TOPIC, CONF_QOS, CONF_RETAIN, CONF_STATE_TOPIC, subscription
|
||||||
CONF_COMMAND_TOPIC,
|
from ... import mqtt
|
||||||
CONF_QOS,
|
from ..debug_info import log_messages
|
||||||
CONF_RETAIN,
|
from ..mixins import (
|
||||||
CONF_STATE_TOPIC,
|
MQTT_AVAILABILITY_SCHEMA,
|
||||||
|
MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
||||||
|
MQTT_JSON_ATTRS_SCHEMA,
|
||||||
MqttAttributes,
|
MqttAttributes,
|
||||||
MqttAvailability,
|
MqttAvailability,
|
||||||
MqttDiscoveryUpdate,
|
MqttDiscoveryUpdate,
|
||||||
MqttEntityDeviceInfo,
|
MqttEntityDeviceInfo,
|
||||||
subscription,
|
|
||||||
)
|
)
|
||||||
from ... import mqtt
|
|
||||||
from ..debug_info import log_messages
|
|
||||||
from .schema import MQTT_LIGHT_SCHEMA_SCHEMA
|
from .schema import MQTT_LIGHT_SCHEMA_SCHEMA
|
||||||
from .schema_basic import CONF_BRIGHTNESS_SCALE
|
from .schema_basic import CONF_BRIGHTNESS_SCALE
|
||||||
|
|
||||||
|
@ -93,7 +92,7 @@ PLATFORM_SCHEMA_JSON = (
|
||||||
CONF_BRIGHTNESS_SCALE, default=DEFAULT_BRIGHTNESS_SCALE
|
CONF_BRIGHTNESS_SCALE, default=DEFAULT_BRIGHTNESS_SCALE
|
||||||
): vol.All(vol.Coerce(int), vol.Range(min=1)),
|
): vol.All(vol.Coerce(int), vol.Range(min=1)),
|
||||||
vol.Optional(CONF_COLOR_TEMP, default=DEFAULT_COLOR_TEMP): cv.boolean,
|
vol.Optional(CONF_COLOR_TEMP, default=DEFAULT_COLOR_TEMP): cv.boolean,
|
||||||
vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
vol.Optional(CONF_DEVICE): MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
||||||
vol.Optional(CONF_EFFECT, default=DEFAULT_EFFECT): cv.boolean,
|
vol.Optional(CONF_EFFECT, default=DEFAULT_EFFECT): cv.boolean,
|
||||||
vol.Optional(CONF_EFFECT_LIST): vol.All(cv.ensure_list, [cv.string]),
|
vol.Optional(CONF_EFFECT_LIST): vol.All(cv.ensure_list, [cv.string]),
|
||||||
vol.Optional(
|
vol.Optional(
|
||||||
|
@ -118,8 +117,8 @@ PLATFORM_SCHEMA_JSON = (
|
||||||
vol.Optional(CONF_XY, default=DEFAULT_XY): cv.boolean,
|
vol.Optional(CONF_XY, default=DEFAULT_XY): cv.boolean,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema)
|
.extend(MQTT_AVAILABILITY_SCHEMA.schema)
|
||||||
.extend(mqtt.MQTT_JSON_ATTRS_SCHEMA.schema)
|
.extend(MQTT_JSON_ATTRS_SCHEMA.schema)
|
||||||
.extend(MQTT_LIGHT_SCHEMA_SCHEMA.schema)
|
.extend(MQTT_LIGHT_SCHEMA_SCHEMA.schema)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -33,19 +33,18 @@ import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers.restore_state import RestoreEntity
|
from homeassistant.helpers.restore_state import RestoreEntity
|
||||||
import homeassistant.util.color as color_util
|
import homeassistant.util.color as color_util
|
||||||
|
|
||||||
from .. import (
|
from .. import CONF_COMMAND_TOPIC, CONF_QOS, CONF_RETAIN, CONF_STATE_TOPIC, subscription
|
||||||
CONF_COMMAND_TOPIC,
|
from ... import mqtt
|
||||||
CONF_QOS,
|
from ..debug_info import log_messages
|
||||||
CONF_RETAIN,
|
from ..mixins import (
|
||||||
CONF_STATE_TOPIC,
|
MQTT_AVAILABILITY_SCHEMA,
|
||||||
|
MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
||||||
|
MQTT_JSON_ATTRS_SCHEMA,
|
||||||
MqttAttributes,
|
MqttAttributes,
|
||||||
MqttAvailability,
|
MqttAvailability,
|
||||||
MqttDiscoveryUpdate,
|
MqttDiscoveryUpdate,
|
||||||
MqttEntityDeviceInfo,
|
MqttEntityDeviceInfo,
|
||||||
subscription,
|
|
||||||
)
|
)
|
||||||
from ... import mqtt
|
|
||||||
from ..debug_info import log_messages
|
|
||||||
from .schema import MQTT_LIGHT_SCHEMA_SCHEMA
|
from .schema import MQTT_LIGHT_SCHEMA_SCHEMA
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
@ -77,7 +76,7 @@ PLATFORM_SCHEMA_TEMPLATE = (
|
||||||
vol.Optional(CONF_COLOR_TEMP_TEMPLATE): cv.template,
|
vol.Optional(CONF_COLOR_TEMP_TEMPLATE): cv.template,
|
||||||
vol.Required(CONF_COMMAND_OFF_TEMPLATE): cv.template,
|
vol.Required(CONF_COMMAND_OFF_TEMPLATE): cv.template,
|
||||||
vol.Required(CONF_COMMAND_ON_TEMPLATE): cv.template,
|
vol.Required(CONF_COMMAND_ON_TEMPLATE): cv.template,
|
||||||
vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
vol.Optional(CONF_DEVICE): MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
||||||
vol.Optional(CONF_EFFECT_LIST): vol.All(cv.ensure_list, [cv.string]),
|
vol.Optional(CONF_EFFECT_LIST): vol.All(cv.ensure_list, [cv.string]),
|
||||||
vol.Optional(CONF_EFFECT_TEMPLATE): cv.template,
|
vol.Optional(CONF_EFFECT_TEMPLATE): cv.template,
|
||||||
vol.Optional(CONF_GREEN_TEMPLATE): cv.template,
|
vol.Optional(CONF_GREEN_TEMPLATE): cv.template,
|
||||||
|
@ -91,8 +90,8 @@ PLATFORM_SCHEMA_TEMPLATE = (
|
||||||
vol.Optional(CONF_WHITE_VALUE_TEMPLATE): cv.template,
|
vol.Optional(CONF_WHITE_VALUE_TEMPLATE): cv.template,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema)
|
.extend(MQTT_AVAILABILITY_SCHEMA.schema)
|
||||||
.extend(mqtt.MQTT_JSON_ATTRS_SCHEMA.schema)
|
.extend(MQTT_JSON_ATTRS_SCHEMA.schema)
|
||||||
.extend(MQTT_LIGHT_SCHEMA_SCHEMA.schema)
|
.extend(MQTT_LIGHT_SCHEMA_SCHEMA.schema)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -22,22 +22,27 @@ from homeassistant.helpers.reload import async_setup_reload_service
|
||||||
from homeassistant.helpers.typing import ConfigType, HomeAssistantType
|
from homeassistant.helpers.typing import ConfigType, HomeAssistantType
|
||||||
|
|
||||||
from . import (
|
from . import (
|
||||||
ATTR_DISCOVERY_HASH,
|
|
||||||
CONF_COMMAND_TOPIC,
|
CONF_COMMAND_TOPIC,
|
||||||
CONF_QOS,
|
CONF_QOS,
|
||||||
CONF_RETAIN,
|
CONF_RETAIN,
|
||||||
CONF_STATE_TOPIC,
|
CONF_STATE_TOPIC,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
PLATFORMS,
|
PLATFORMS,
|
||||||
|
subscription,
|
||||||
|
)
|
||||||
|
from .. import mqtt
|
||||||
|
from .const import ATTR_DISCOVERY_HASH
|
||||||
|
from .debug_info import log_messages
|
||||||
|
from .discovery import MQTT_DISCOVERY_DONE, MQTT_DISCOVERY_NEW, clear_discovery_hash
|
||||||
|
from .mixins import (
|
||||||
|
MQTT_AVAILABILITY_SCHEMA,
|
||||||
|
MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
||||||
|
MQTT_JSON_ATTRS_SCHEMA,
|
||||||
MqttAttributes,
|
MqttAttributes,
|
||||||
MqttAvailability,
|
MqttAvailability,
|
||||||
MqttDiscoveryUpdate,
|
MqttDiscoveryUpdate,
|
||||||
MqttEntityDeviceInfo,
|
MqttEntityDeviceInfo,
|
||||||
subscription,
|
|
||||||
)
|
)
|
||||||
from .. import mqtt
|
|
||||||
from .debug_info import log_messages
|
|
||||||
from .discovery import MQTT_DISCOVERY_DONE, MQTT_DISCOVERY_NEW, clear_discovery_hash
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -57,7 +62,7 @@ DEFAULT_STATE_UNLOCKED = "UNLOCKED"
|
||||||
PLATFORM_SCHEMA = (
|
PLATFORM_SCHEMA = (
|
||||||
mqtt.MQTT_RW_PLATFORM_SCHEMA.extend(
|
mqtt.MQTT_RW_PLATFORM_SCHEMA.extend(
|
||||||
{
|
{
|
||||||
vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
vol.Optional(CONF_DEVICE): MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
||||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||||
vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean,
|
vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean,
|
||||||
vol.Optional(CONF_PAYLOAD_LOCK, default=DEFAULT_PAYLOAD_LOCK): cv.string,
|
vol.Optional(CONF_PAYLOAD_LOCK, default=DEFAULT_PAYLOAD_LOCK): cv.string,
|
||||||
|
@ -71,8 +76,8 @@ PLATFORM_SCHEMA = (
|
||||||
vol.Optional(CONF_UNIQUE_ID): cv.string,
|
vol.Optional(CONF_UNIQUE_ID): cv.string,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema)
|
.extend(MQTT_AVAILABILITY_SCHEMA.schema)
|
||||||
.extend(mqtt.MQTT_JSON_ATTRS_SCHEMA.schema)
|
.extend(MQTT_JSON_ATTRS_SCHEMA.schema)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
472
homeassistant/components/mqtt/mixins.py
Normal file
472
homeassistant/components/mqtt/mixins.py
Normal file
|
@ -0,0 +1,472 @@
|
||||||
|
"""MQTT component mixins and helpers."""
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
|
from homeassistant.const import CONF_DEVICE, CONF_NAME
|
||||||
|
from homeassistant.core import callback
|
||||||
|
from homeassistant.helpers import config_validation as cv
|
||||||
|
from homeassistant.helpers.dispatcher import (
|
||||||
|
async_dispatcher_connect,
|
||||||
|
async_dispatcher_send,
|
||||||
|
)
|
||||||
|
from homeassistant.helpers.entity import Entity
|
||||||
|
from homeassistant.helpers.typing import ConfigType
|
||||||
|
|
||||||
|
from . import CONF_TOPIC, DATA_MQTT, debug_info, publish
|
||||||
|
from .const import (
|
||||||
|
ATTR_DISCOVERY_HASH,
|
||||||
|
ATTR_DISCOVERY_PAYLOAD,
|
||||||
|
ATTR_DISCOVERY_TOPIC,
|
||||||
|
CONF_QOS,
|
||||||
|
DEFAULT_PAYLOAD_AVAILABLE,
|
||||||
|
DEFAULT_PAYLOAD_NOT_AVAILABLE,
|
||||||
|
DOMAIN,
|
||||||
|
MQTT_CONNECTED,
|
||||||
|
MQTT_DISCONNECTED,
|
||||||
|
)
|
||||||
|
from .debug_info import log_messages
|
||||||
|
from .discovery import (
|
||||||
|
MQTT_DISCOVERY_DONE,
|
||||||
|
MQTT_DISCOVERY_UPDATED,
|
||||||
|
clear_discovery_hash,
|
||||||
|
set_discovery_hash,
|
||||||
|
)
|
||||||
|
from .models import Message
|
||||||
|
from .subscription import async_subscribe_topics, async_unsubscribe_topics
|
||||||
|
from .util import valid_subscribe_topic
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
CONF_AVAILABILITY = "availability"
|
||||||
|
CONF_AVAILABILITY_TOPIC = "availability_topic"
|
||||||
|
CONF_PAYLOAD_AVAILABLE = "payload_available"
|
||||||
|
CONF_PAYLOAD_NOT_AVAILABLE = "payload_not_available"
|
||||||
|
CONF_JSON_ATTRS_TOPIC = "json_attributes_topic"
|
||||||
|
CONF_JSON_ATTRS_TEMPLATE = "json_attributes_template"
|
||||||
|
|
||||||
|
CONF_IDENTIFIERS = "identifiers"
|
||||||
|
CONF_CONNECTIONS = "connections"
|
||||||
|
CONF_MANUFACTURER = "manufacturer"
|
||||||
|
CONF_MODEL = "model"
|
||||||
|
CONF_SW_VERSION = "sw_version"
|
||||||
|
CONF_VIA_DEVICE = "via_device"
|
||||||
|
CONF_DEPRECATED_VIA_HUB = "via_hub"
|
||||||
|
|
||||||
|
MQTT_AVAILABILITY_SINGLE_SCHEMA = vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Exclusive(CONF_AVAILABILITY_TOPIC, "availability"): valid_subscribe_topic,
|
||||||
|
vol.Optional(
|
||||||
|
CONF_PAYLOAD_AVAILABLE, default=DEFAULT_PAYLOAD_AVAILABLE
|
||||||
|
): cv.string,
|
||||||
|
vol.Optional(
|
||||||
|
CONF_PAYLOAD_NOT_AVAILABLE, default=DEFAULT_PAYLOAD_NOT_AVAILABLE
|
||||||
|
): cv.string,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
MQTT_AVAILABILITY_LIST_SCHEMA = vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Exclusive(CONF_AVAILABILITY, "availability"): vol.All(
|
||||||
|
cv.ensure_list,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
vol.Optional(CONF_TOPIC): valid_subscribe_topic,
|
||||||
|
vol.Optional(
|
||||||
|
CONF_PAYLOAD_AVAILABLE, default=DEFAULT_PAYLOAD_AVAILABLE
|
||||||
|
): cv.string,
|
||||||
|
vol.Optional(
|
||||||
|
CONF_PAYLOAD_NOT_AVAILABLE,
|
||||||
|
default=DEFAULT_PAYLOAD_NOT_AVAILABLE,
|
||||||
|
): cv.string,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
MQTT_AVAILABILITY_SCHEMA = MQTT_AVAILABILITY_SINGLE_SCHEMA.extend(
|
||||||
|
MQTT_AVAILABILITY_LIST_SCHEMA.schema
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_device_has_at_least_one_identifier(value: ConfigType) -> ConfigType:
|
||||||
|
"""Validate that a device info entry has at least one identifying value."""
|
||||||
|
if value.get(CONF_IDENTIFIERS) or value.get(CONF_CONNECTIONS):
|
||||||
|
return value
|
||||||
|
raise vol.Invalid(
|
||||||
|
"Device must have at least one identifying value in "
|
||||||
|
"'identifiers' and/or 'connections'"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
MQTT_ENTITY_DEVICE_INFO_SCHEMA = vol.All(
|
||||||
|
cv.deprecated(CONF_DEPRECATED_VIA_HUB, CONF_VIA_DEVICE),
|
||||||
|
vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Optional(CONF_IDENTIFIERS, default=list): vol.All(
|
||||||
|
cv.ensure_list, [cv.string]
|
||||||
|
),
|
||||||
|
vol.Optional(CONF_CONNECTIONS, default=list): vol.All(
|
||||||
|
cv.ensure_list, [vol.All(vol.Length(2), [cv.string])]
|
||||||
|
),
|
||||||
|
vol.Optional(CONF_MANUFACTURER): cv.string,
|
||||||
|
vol.Optional(CONF_MODEL): cv.string,
|
||||||
|
vol.Optional(CONF_NAME): cv.string,
|
||||||
|
vol.Optional(CONF_SW_VERSION): cv.string,
|
||||||
|
vol.Optional(CONF_VIA_DEVICE): cv.string,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
validate_device_has_at_least_one_identifier,
|
||||||
|
)
|
||||||
|
|
||||||
|
MQTT_JSON_ATTRS_SCHEMA = vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Optional(CONF_JSON_ATTRS_TOPIC): valid_subscribe_topic,
|
||||||
|
vol.Optional(CONF_JSON_ATTRS_TEMPLATE): cv.template,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class MqttAttributes(Entity):
|
||||||
|
"""Mixin used for platforms that support JSON attributes."""
|
||||||
|
|
||||||
|
def __init__(self, config: dict) -> None:
|
||||||
|
"""Initialize the JSON attributes mixin."""
|
||||||
|
self._attributes = None
|
||||||
|
self._attributes_sub_state = None
|
||||||
|
self._attributes_config = config
|
||||||
|
|
||||||
|
async def async_added_to_hass(self) -> None:
|
||||||
|
"""Subscribe MQTT events."""
|
||||||
|
await super().async_added_to_hass()
|
||||||
|
await self._attributes_subscribe_topics()
|
||||||
|
|
||||||
|
async def attributes_discovery_update(self, config: dict):
|
||||||
|
"""Handle updated discovery message."""
|
||||||
|
self._attributes_config = config
|
||||||
|
await self._attributes_subscribe_topics()
|
||||||
|
|
||||||
|
async def _attributes_subscribe_topics(self):
|
||||||
|
"""(Re)Subscribe to topics."""
|
||||||
|
attr_tpl = self._attributes_config.get(CONF_JSON_ATTRS_TEMPLATE)
|
||||||
|
if attr_tpl is not None:
|
||||||
|
attr_tpl.hass = self.hass
|
||||||
|
|
||||||
|
@callback
|
||||||
|
@log_messages(self.hass, self.entity_id)
|
||||||
|
def attributes_message_received(msg: Message) -> None:
|
||||||
|
try:
|
||||||
|
payload = msg.payload
|
||||||
|
if attr_tpl is not None:
|
||||||
|
payload = attr_tpl.async_render_with_possible_json_value(payload)
|
||||||
|
json_dict = json.loads(payload)
|
||||||
|
if isinstance(json_dict, dict):
|
||||||
|
self._attributes = json_dict
|
||||||
|
self.async_write_ha_state()
|
||||||
|
else:
|
||||||
|
_LOGGER.warning("JSON result was not a dictionary")
|
||||||
|
self._attributes = None
|
||||||
|
except ValueError:
|
||||||
|
_LOGGER.warning("Erroneous JSON: %s", payload)
|
||||||
|
self._attributes = None
|
||||||
|
|
||||||
|
self._attributes_sub_state = await async_subscribe_topics(
|
||||||
|
self.hass,
|
||||||
|
self._attributes_sub_state,
|
||||||
|
{
|
||||||
|
CONF_JSON_ATTRS_TOPIC: {
|
||||||
|
"topic": self._attributes_config.get(CONF_JSON_ATTRS_TOPIC),
|
||||||
|
"msg_callback": attributes_message_received,
|
||||||
|
"qos": self._attributes_config.get(CONF_QOS),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
async def async_will_remove_from_hass(self):
|
||||||
|
"""Unsubscribe when removed."""
|
||||||
|
self._attributes_sub_state = await async_unsubscribe_topics(
|
||||||
|
self.hass, self._attributes_sub_state
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def device_state_attributes(self):
|
||||||
|
"""Return the state attributes."""
|
||||||
|
return self._attributes
|
||||||
|
|
||||||
|
|
||||||
|
class MqttAvailability(Entity):
|
||||||
|
"""Mixin used for platforms that report availability."""
|
||||||
|
|
||||||
|
def __init__(self, config: dict) -> None:
|
||||||
|
"""Initialize the availability mixin."""
|
||||||
|
self._availability_sub_state = None
|
||||||
|
self._available = False
|
||||||
|
self._availability_setup_from_config(config)
|
||||||
|
|
||||||
|
async def async_added_to_hass(self) -> None:
|
||||||
|
"""Subscribe MQTT events."""
|
||||||
|
await super().async_added_to_hass()
|
||||||
|
await self._availability_subscribe_topics()
|
||||||
|
self.async_on_remove(
|
||||||
|
async_dispatcher_connect(self.hass, MQTT_CONNECTED, self.async_mqtt_connect)
|
||||||
|
)
|
||||||
|
self.async_on_remove(
|
||||||
|
async_dispatcher_connect(
|
||||||
|
self.hass, MQTT_DISCONNECTED, self.async_mqtt_connect
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
async def availability_discovery_update(self, config: dict):
|
||||||
|
"""Handle updated discovery message."""
|
||||||
|
self._availability_setup_from_config(config)
|
||||||
|
await self._availability_subscribe_topics()
|
||||||
|
|
||||||
|
def _availability_setup_from_config(self, config):
|
||||||
|
"""(Re)Setup."""
|
||||||
|
self._avail_topics = {}
|
||||||
|
if CONF_AVAILABILITY_TOPIC in config:
|
||||||
|
self._avail_topics[config[CONF_AVAILABILITY_TOPIC]] = {
|
||||||
|
CONF_PAYLOAD_AVAILABLE: config[CONF_PAYLOAD_AVAILABLE],
|
||||||
|
CONF_PAYLOAD_NOT_AVAILABLE: config[CONF_PAYLOAD_NOT_AVAILABLE],
|
||||||
|
}
|
||||||
|
|
||||||
|
if CONF_AVAILABILITY in config:
|
||||||
|
for avail in config[CONF_AVAILABILITY]:
|
||||||
|
self._avail_topics[avail[CONF_TOPIC]] = {
|
||||||
|
CONF_PAYLOAD_AVAILABLE: avail[CONF_PAYLOAD_AVAILABLE],
|
||||||
|
CONF_PAYLOAD_NOT_AVAILABLE: avail[CONF_PAYLOAD_NOT_AVAILABLE],
|
||||||
|
}
|
||||||
|
|
||||||
|
self._avail_config = config
|
||||||
|
|
||||||
|
async def _availability_subscribe_topics(self):
|
||||||
|
"""(Re)Subscribe to topics."""
|
||||||
|
|
||||||
|
@callback
|
||||||
|
@log_messages(self.hass, self.entity_id)
|
||||||
|
def availability_message_received(msg: Message) -> None:
|
||||||
|
"""Handle a new received MQTT availability message."""
|
||||||
|
topic = msg.topic
|
||||||
|
if msg.payload == self._avail_topics[topic][CONF_PAYLOAD_AVAILABLE]:
|
||||||
|
self._available = True
|
||||||
|
elif msg.payload == self._avail_topics[topic][CONF_PAYLOAD_NOT_AVAILABLE]:
|
||||||
|
self._available = False
|
||||||
|
|
||||||
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
topics = {
|
||||||
|
f"availability_{topic}": {
|
||||||
|
"topic": topic,
|
||||||
|
"msg_callback": availability_message_received,
|
||||||
|
"qos": self._avail_config[CONF_QOS],
|
||||||
|
}
|
||||||
|
for topic in self._avail_topics
|
||||||
|
}
|
||||||
|
|
||||||
|
self._availability_sub_state = await async_subscribe_topics(
|
||||||
|
self.hass,
|
||||||
|
self._availability_sub_state,
|
||||||
|
topics,
|
||||||
|
)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_mqtt_connect(self):
|
||||||
|
"""Update state on connection/disconnection to MQTT broker."""
|
||||||
|
if not self.hass.is_stopping:
|
||||||
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
async def async_will_remove_from_hass(self):
|
||||||
|
"""Unsubscribe when removed."""
|
||||||
|
self._availability_sub_state = await async_unsubscribe_topics(
|
||||||
|
self.hass, self._availability_sub_state
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def available(self) -> bool:
|
||||||
|
"""Return if the device is available."""
|
||||||
|
if not self.hass.data[DATA_MQTT].connected and not self.hass.is_stopping:
|
||||||
|
return False
|
||||||
|
return not self._avail_topics or self._available
|
||||||
|
|
||||||
|
|
||||||
|
async def cleanup_device_registry(hass, device_id):
|
||||||
|
"""Remove device registry entry if there are no remaining entities or triggers."""
|
||||||
|
# Local import to avoid circular dependencies
|
||||||
|
# pylint: disable=import-outside-toplevel
|
||||||
|
from . import device_trigger, tag
|
||||||
|
|
||||||
|
device_registry = await hass.helpers.device_registry.async_get_registry()
|
||||||
|
entity_registry = await hass.helpers.entity_registry.async_get_registry()
|
||||||
|
if (
|
||||||
|
device_id
|
||||||
|
and not hass.helpers.entity_registry.async_entries_for_device(
|
||||||
|
entity_registry, device_id, include_disabled_entities=True
|
||||||
|
)
|
||||||
|
and not await device_trigger.async_get_triggers(hass, device_id)
|
||||||
|
and not tag.async_has_tags(hass, device_id)
|
||||||
|
):
|
||||||
|
device_registry.async_remove_device(device_id)
|
||||||
|
|
||||||
|
|
||||||
|
class MqttDiscoveryUpdate(Entity):
|
||||||
|
"""Mixin used to handle updated discovery message."""
|
||||||
|
|
||||||
|
def __init__(self, discovery_data, discovery_update=None) -> None:
|
||||||
|
"""Initialize the discovery update mixin."""
|
||||||
|
self._discovery_data = discovery_data
|
||||||
|
self._discovery_update = discovery_update
|
||||||
|
self._remove_signal = None
|
||||||
|
self._removed_from_hass = False
|
||||||
|
|
||||||
|
async def async_added_to_hass(self) -> None:
|
||||||
|
"""Subscribe to discovery updates."""
|
||||||
|
await super().async_added_to_hass()
|
||||||
|
self._removed_from_hass = False
|
||||||
|
discovery_hash = (
|
||||||
|
self._discovery_data[ATTR_DISCOVERY_HASH] if self._discovery_data else None
|
||||||
|
)
|
||||||
|
|
||||||
|
async def _async_remove_state_and_registry_entry(self) -> None:
|
||||||
|
"""Remove entity's state and entity registry entry.
|
||||||
|
|
||||||
|
Remove entity from entity registry if it is registered, this also removes the state.
|
||||||
|
If the entity is not in the entity registry, just remove the state.
|
||||||
|
"""
|
||||||
|
entity_registry = (
|
||||||
|
await self.hass.helpers.entity_registry.async_get_registry()
|
||||||
|
)
|
||||||
|
if entity_registry.async_is_registered(self.entity_id):
|
||||||
|
entity_entry = entity_registry.async_get(self.entity_id)
|
||||||
|
entity_registry.async_remove(self.entity_id)
|
||||||
|
await cleanup_device_registry(self.hass, entity_entry.device_id)
|
||||||
|
else:
|
||||||
|
await self.async_remove()
|
||||||
|
|
||||||
|
async def discovery_callback(payload):
|
||||||
|
"""Handle discovery update."""
|
||||||
|
_LOGGER.info(
|
||||||
|
"Got update for entity with hash: %s '%s'",
|
||||||
|
discovery_hash,
|
||||||
|
payload,
|
||||||
|
)
|
||||||
|
old_payload = self._discovery_data[ATTR_DISCOVERY_PAYLOAD]
|
||||||
|
debug_info.update_entity_discovery_data(self.hass, payload, self.entity_id)
|
||||||
|
if not payload:
|
||||||
|
# Empty payload: Remove component
|
||||||
|
_LOGGER.info("Removing component: %s", self.entity_id)
|
||||||
|
self._cleanup_discovery_on_remove()
|
||||||
|
await _async_remove_state_and_registry_entry(self)
|
||||||
|
elif self._discovery_update:
|
||||||
|
if old_payload != self._discovery_data[ATTR_DISCOVERY_PAYLOAD]:
|
||||||
|
# Non-empty, changed payload: Notify component
|
||||||
|
_LOGGER.info("Updating component: %s", self.entity_id)
|
||||||
|
await self._discovery_update(payload)
|
||||||
|
else:
|
||||||
|
# Non-empty, unchanged payload: Ignore to avoid changing states
|
||||||
|
_LOGGER.info("Ignoring unchanged update for: %s", self.entity_id)
|
||||||
|
async_dispatcher_send(
|
||||||
|
self.hass, MQTT_DISCOVERY_DONE.format(discovery_hash), None
|
||||||
|
)
|
||||||
|
|
||||||
|
if discovery_hash:
|
||||||
|
debug_info.add_entity_discovery_data(
|
||||||
|
self.hass, self._discovery_data, self.entity_id
|
||||||
|
)
|
||||||
|
# Set in case the entity has been removed and is re-added, for example when changing entity_id
|
||||||
|
set_discovery_hash(self.hass, discovery_hash)
|
||||||
|
self._remove_signal = async_dispatcher_connect(
|
||||||
|
self.hass,
|
||||||
|
MQTT_DISCOVERY_UPDATED.format(discovery_hash),
|
||||||
|
discovery_callback,
|
||||||
|
)
|
||||||
|
async_dispatcher_send(
|
||||||
|
self.hass, MQTT_DISCOVERY_DONE.format(discovery_hash), None
|
||||||
|
)
|
||||||
|
|
||||||
|
async def async_removed_from_registry(self) -> None:
|
||||||
|
"""Clear retained discovery topic in broker."""
|
||||||
|
if not self._removed_from_hass:
|
||||||
|
discovery_topic = self._discovery_data[ATTR_DISCOVERY_TOPIC]
|
||||||
|
publish(self.hass, discovery_topic, "", retain=True)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def add_to_platform_abort(self) -> None:
|
||||||
|
"""Abort adding an entity to a platform."""
|
||||||
|
if self._discovery_data:
|
||||||
|
discovery_hash = self._discovery_data[ATTR_DISCOVERY_HASH]
|
||||||
|
clear_discovery_hash(self.hass, discovery_hash)
|
||||||
|
async_dispatcher_send(
|
||||||
|
self.hass, MQTT_DISCOVERY_DONE.format(discovery_hash), None
|
||||||
|
)
|
||||||
|
super().add_to_platform_abort()
|
||||||
|
|
||||||
|
async def async_will_remove_from_hass(self) -> None:
|
||||||
|
"""Stop listening to signal and cleanup discovery data.."""
|
||||||
|
self._cleanup_discovery_on_remove()
|
||||||
|
|
||||||
|
def _cleanup_discovery_on_remove(self) -> None:
|
||||||
|
"""Stop listening to signal and cleanup discovery data."""
|
||||||
|
if self._discovery_data and not self._removed_from_hass:
|
||||||
|
debug_info.remove_entity_data(self.hass, self.entity_id)
|
||||||
|
clear_discovery_hash(self.hass, self._discovery_data[ATTR_DISCOVERY_HASH])
|
||||||
|
self._removed_from_hass = True
|
||||||
|
|
||||||
|
if self._remove_signal:
|
||||||
|
self._remove_signal()
|
||||||
|
self._remove_signal = None
|
||||||
|
|
||||||
|
|
||||||
|
def device_info_from_config(config):
|
||||||
|
"""Return a device description for device registry."""
|
||||||
|
if not config:
|
||||||
|
return None
|
||||||
|
|
||||||
|
info = {
|
||||||
|
"identifiers": {(DOMAIN, id_) for id_ in config[CONF_IDENTIFIERS]},
|
||||||
|
"connections": {tuple(x) for x in config[CONF_CONNECTIONS]},
|
||||||
|
}
|
||||||
|
|
||||||
|
if CONF_MANUFACTURER in config:
|
||||||
|
info["manufacturer"] = config[CONF_MANUFACTURER]
|
||||||
|
|
||||||
|
if CONF_MODEL in config:
|
||||||
|
info["model"] = config[CONF_MODEL]
|
||||||
|
|
||||||
|
if CONF_NAME in config:
|
||||||
|
info["name"] = config[CONF_NAME]
|
||||||
|
|
||||||
|
if CONF_SW_VERSION in config:
|
||||||
|
info["sw_version"] = config[CONF_SW_VERSION]
|
||||||
|
|
||||||
|
if CONF_VIA_DEVICE in config:
|
||||||
|
info["via_device"] = (DOMAIN, config[CONF_VIA_DEVICE])
|
||||||
|
|
||||||
|
return info
|
||||||
|
|
||||||
|
|
||||||
|
class MqttEntityDeviceInfo(Entity):
|
||||||
|
"""Mixin used for mqtt platforms that support the device registry."""
|
||||||
|
|
||||||
|
def __init__(self, device_config: Optional[ConfigType], config_entry=None) -> None:
|
||||||
|
"""Initialize the device mixin."""
|
||||||
|
self._device_config = device_config
|
||||||
|
self._config_entry = config_entry
|
||||||
|
|
||||||
|
async def device_info_discovery_update(self, config: dict):
|
||||||
|
"""Handle updated discovery message."""
|
||||||
|
self._device_config = config.get(CONF_DEVICE)
|
||||||
|
device_registry = await self.hass.helpers.device_registry.async_get_registry()
|
||||||
|
config_entry_id = self._config_entry.entry_id
|
||||||
|
device_info = self.device_info
|
||||||
|
|
||||||
|
if config_entry_id is not None and device_info is not None:
|
||||||
|
device_info["config_entry_id"] = config_entry_id
|
||||||
|
device_registry.async_get_or_create(**device_info)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def device_info(self):
|
||||||
|
"""Return a device description for device registry."""
|
||||||
|
return device_info_from_config(self._device_config)
|
|
@ -23,21 +23,26 @@ from homeassistant.helpers.restore_state import RestoreEntity
|
||||||
from homeassistant.helpers.typing import ConfigType, HomeAssistantType
|
from homeassistant.helpers.typing import ConfigType, HomeAssistantType
|
||||||
|
|
||||||
from . import (
|
from . import (
|
||||||
ATTR_DISCOVERY_HASH,
|
|
||||||
CONF_COMMAND_TOPIC,
|
CONF_COMMAND_TOPIC,
|
||||||
CONF_QOS,
|
CONF_QOS,
|
||||||
CONF_STATE_TOPIC,
|
CONF_STATE_TOPIC,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
PLATFORMS,
|
PLATFORMS,
|
||||||
|
subscription,
|
||||||
|
)
|
||||||
|
from .. import mqtt
|
||||||
|
from .const import ATTR_DISCOVERY_HASH
|
||||||
|
from .debug_info import log_messages
|
||||||
|
from .discovery import MQTT_DISCOVERY_DONE, MQTT_DISCOVERY_NEW, clear_discovery_hash
|
||||||
|
from .mixins import (
|
||||||
|
MQTT_AVAILABILITY_SCHEMA,
|
||||||
|
MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
||||||
|
MQTT_JSON_ATTRS_SCHEMA,
|
||||||
MqttAttributes,
|
MqttAttributes,
|
||||||
MqttAvailability,
|
MqttAvailability,
|
||||||
MqttDiscoveryUpdate,
|
MqttDiscoveryUpdate,
|
||||||
MqttEntityDeviceInfo,
|
MqttEntityDeviceInfo,
|
||||||
subscription,
|
|
||||||
)
|
)
|
||||||
from .. import mqtt
|
|
||||||
from .debug_info import log_messages
|
|
||||||
from .discovery import MQTT_DISCOVERY_DONE, MQTT_DISCOVERY_NEW, clear_discovery_hash
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -47,15 +52,15 @@ DEFAULT_OPTIMISTIC = False
|
||||||
PLATFORM_SCHEMA = (
|
PLATFORM_SCHEMA = (
|
||||||
mqtt.MQTT_RW_PLATFORM_SCHEMA.extend(
|
mqtt.MQTT_RW_PLATFORM_SCHEMA.extend(
|
||||||
{
|
{
|
||||||
vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
vol.Optional(CONF_DEVICE): MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
||||||
vol.Optional(CONF_ICON): cv.icon,
|
vol.Optional(CONF_ICON): cv.icon,
|
||||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||||
vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean,
|
vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean,
|
||||||
vol.Optional(CONF_UNIQUE_ID): cv.string,
|
vol.Optional(CONF_UNIQUE_ID): cv.string,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema)
|
.extend(MQTT_AVAILABILITY_SCHEMA.schema)
|
||||||
.extend(mqtt.MQTT_JSON_ATTRS_SCHEMA.schema)
|
.extend(MQTT_JSON_ATTRS_SCHEMA.schema)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -14,18 +14,11 @@ from homeassistant.helpers.dispatcher import (
|
||||||
from homeassistant.helpers.reload import async_setup_reload_service
|
from homeassistant.helpers.reload import async_setup_reload_service
|
||||||
from homeassistant.helpers.typing import ConfigType, HomeAssistantType
|
from homeassistant.helpers.typing import ConfigType, HomeAssistantType
|
||||||
|
|
||||||
from . import (
|
from . import CONF_COMMAND_TOPIC, CONF_QOS, CONF_RETAIN, DOMAIN, PLATFORMS
|
||||||
ATTR_DISCOVERY_HASH,
|
|
||||||
CONF_COMMAND_TOPIC,
|
|
||||||
CONF_QOS,
|
|
||||||
CONF_RETAIN,
|
|
||||||
DOMAIN,
|
|
||||||
PLATFORMS,
|
|
||||||
MqttAvailability,
|
|
||||||
MqttDiscoveryUpdate,
|
|
||||||
)
|
|
||||||
from .. import mqtt
|
from .. import mqtt
|
||||||
|
from .const import ATTR_DISCOVERY_HASH
|
||||||
from .discovery import MQTT_DISCOVERY_DONE, MQTT_DISCOVERY_NEW, clear_discovery_hash
|
from .discovery import MQTT_DISCOVERY_DONE, MQTT_DISCOVERY_NEW, clear_discovery_hash
|
||||||
|
from .mixins import MQTT_AVAILABILITY_SCHEMA, MqttAvailability, MqttDiscoveryUpdate
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -41,7 +34,7 @@ PLATFORM_SCHEMA = mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend(
|
||||||
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,
|
||||||
}
|
}
|
||||||
).extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema)
|
).extend(MQTT_AVAILABILITY_SCHEMA.schema)
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_platform(
|
async def async_setup_platform(
|
||||||
|
|
|
@ -29,21 +29,20 @@ from homeassistant.helpers.reload import async_setup_reload_service
|
||||||
from homeassistant.helpers.typing import ConfigType, HomeAssistantType
|
from homeassistant.helpers.typing import ConfigType, HomeAssistantType
|
||||||
from homeassistant.util import dt as dt_util
|
from homeassistant.util import dt as dt_util
|
||||||
|
|
||||||
from . import (
|
from . import CONF_QOS, CONF_STATE_TOPIC, DOMAIN, PLATFORMS, subscription
|
||||||
ATTR_DISCOVERY_HASH,
|
from .. import mqtt
|
||||||
CONF_QOS,
|
from .const import ATTR_DISCOVERY_HASH
|
||||||
CONF_STATE_TOPIC,
|
from .debug_info import log_messages
|
||||||
DOMAIN,
|
from .discovery import MQTT_DISCOVERY_DONE, MQTT_DISCOVERY_NEW, clear_discovery_hash
|
||||||
PLATFORMS,
|
from .mixins import (
|
||||||
|
MQTT_AVAILABILITY_SCHEMA,
|
||||||
|
MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
||||||
|
MQTT_JSON_ATTRS_SCHEMA,
|
||||||
MqttAttributes,
|
MqttAttributes,
|
||||||
MqttAvailability,
|
MqttAvailability,
|
||||||
MqttDiscoveryUpdate,
|
MqttDiscoveryUpdate,
|
||||||
MqttEntityDeviceInfo,
|
MqttEntityDeviceInfo,
|
||||||
subscription,
|
|
||||||
)
|
)
|
||||||
from .. import mqtt
|
|
||||||
from .debug_info import log_messages
|
|
||||||
from .discovery import MQTT_DISCOVERY_DONE, MQTT_DISCOVERY_NEW, clear_discovery_hash
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -54,7 +53,7 @@ DEFAULT_FORCE_UPDATE = False
|
||||||
PLATFORM_SCHEMA = (
|
PLATFORM_SCHEMA = (
|
||||||
mqtt.MQTT_RO_PLATFORM_SCHEMA.extend(
|
mqtt.MQTT_RO_PLATFORM_SCHEMA.extend(
|
||||||
{
|
{
|
||||||
vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
vol.Optional(CONF_DEVICE): MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
||||||
vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA,
|
vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA,
|
||||||
vol.Optional(CONF_EXPIRE_AFTER): cv.positive_int,
|
vol.Optional(CONF_EXPIRE_AFTER): cv.positive_int,
|
||||||
vol.Optional(CONF_FORCE_UPDATE, default=DEFAULT_FORCE_UPDATE): cv.boolean,
|
vol.Optional(CONF_FORCE_UPDATE, default=DEFAULT_FORCE_UPDATE): cv.boolean,
|
||||||
|
@ -64,8 +63,8 @@ PLATFORM_SCHEMA = (
|
||||||
vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string,
|
vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema)
|
.extend(MQTT_AVAILABILITY_SCHEMA.schema)
|
||||||
.extend(mqtt.MQTT_JSON_ATTRS_SCHEMA.schema)
|
.extend(MQTT_JSON_ATTRS_SCHEMA.schema)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -27,22 +27,27 @@ from homeassistant.helpers.restore_state import RestoreEntity
|
||||||
from homeassistant.helpers.typing import ConfigType, HomeAssistantType
|
from homeassistant.helpers.typing import ConfigType, HomeAssistantType
|
||||||
|
|
||||||
from . import (
|
from . import (
|
||||||
ATTR_DISCOVERY_HASH,
|
|
||||||
CONF_COMMAND_TOPIC,
|
CONF_COMMAND_TOPIC,
|
||||||
CONF_QOS,
|
CONF_QOS,
|
||||||
CONF_RETAIN,
|
CONF_RETAIN,
|
||||||
CONF_STATE_TOPIC,
|
CONF_STATE_TOPIC,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
PLATFORMS,
|
PLATFORMS,
|
||||||
|
subscription,
|
||||||
|
)
|
||||||
|
from .. import mqtt
|
||||||
|
from .const import ATTR_DISCOVERY_HASH
|
||||||
|
from .debug_info import log_messages
|
||||||
|
from .discovery import MQTT_DISCOVERY_DONE, MQTT_DISCOVERY_NEW, clear_discovery_hash
|
||||||
|
from .mixins import (
|
||||||
|
MQTT_AVAILABILITY_SCHEMA,
|
||||||
|
MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
||||||
|
MQTT_JSON_ATTRS_SCHEMA,
|
||||||
MqttAttributes,
|
MqttAttributes,
|
||||||
MqttAvailability,
|
MqttAvailability,
|
||||||
MqttDiscoveryUpdate,
|
MqttDiscoveryUpdate,
|
||||||
MqttEntityDeviceInfo,
|
MqttEntityDeviceInfo,
|
||||||
subscription,
|
|
||||||
)
|
)
|
||||||
from .. import mqtt
|
|
||||||
from .debug_info import log_messages
|
|
||||||
from .discovery import MQTT_DISCOVERY_DONE, MQTT_DISCOVERY_NEW, clear_discovery_hash
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -56,7 +61,7 @@ CONF_STATE_OFF = "state_off"
|
||||||
PLATFORM_SCHEMA = (
|
PLATFORM_SCHEMA = (
|
||||||
mqtt.MQTT_RW_PLATFORM_SCHEMA.extend(
|
mqtt.MQTT_RW_PLATFORM_SCHEMA.extend(
|
||||||
{
|
{
|
||||||
vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
vol.Optional(CONF_DEVICE): MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
||||||
vol.Optional(CONF_ICON): cv.icon,
|
vol.Optional(CONF_ICON): cv.icon,
|
||||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||||
vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean,
|
vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean,
|
||||||
|
@ -67,8 +72,8 @@ PLATFORM_SCHEMA = (
|
||||||
vol.Optional(CONF_UNIQUE_ID): cv.string,
|
vol.Optional(CONF_UNIQUE_ID): cv.string,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema)
|
.extend(MQTT_AVAILABILITY_SCHEMA.schema)
|
||||||
.extend(mqtt.MQTT_JSON_ATTRS_SCHEMA.schema)
|
.extend(MQTT_JSON_ATTRS_SCHEMA.schema)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ import logging
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.const import CONF_PLATFORM, CONF_VALUE_TEMPLATE
|
from homeassistant.const import CONF_DEVICE, CONF_PLATFORM, CONF_VALUE_TEMPLATE
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers.device_registry import EVENT_DEVICE_REGISTRY_UPDATED
|
from homeassistant.helpers.device_registry import EVENT_DEVICE_REGISTRY_UPDATED
|
||||||
from homeassistant.helpers.dispatcher import (
|
from homeassistant.helpers.dispatcher import (
|
||||||
|
@ -11,25 +11,23 @@ from homeassistant.helpers.dispatcher import (
|
||||||
async_dispatcher_send,
|
async_dispatcher_send,
|
||||||
)
|
)
|
||||||
|
|
||||||
from . import (
|
from . import CONF_QOS, CONF_TOPIC, DOMAIN, subscription
|
||||||
ATTR_DISCOVERY_HASH,
|
|
||||||
ATTR_DISCOVERY_TOPIC,
|
|
||||||
CONF_CONNECTIONS,
|
|
||||||
CONF_DEVICE,
|
|
||||||
CONF_IDENTIFIERS,
|
|
||||||
CONF_QOS,
|
|
||||||
CONF_TOPIC,
|
|
||||||
DOMAIN,
|
|
||||||
cleanup_device_registry,
|
|
||||||
subscription,
|
|
||||||
)
|
|
||||||
from .. import mqtt
|
from .. import mqtt
|
||||||
|
from .const import ATTR_DISCOVERY_HASH, ATTR_DISCOVERY_TOPIC
|
||||||
from .discovery import (
|
from .discovery import (
|
||||||
MQTT_DISCOVERY_DONE,
|
MQTT_DISCOVERY_DONE,
|
||||||
MQTT_DISCOVERY_NEW,
|
MQTT_DISCOVERY_NEW,
|
||||||
MQTT_DISCOVERY_UPDATED,
|
MQTT_DISCOVERY_UPDATED,
|
||||||
clear_discovery_hash,
|
clear_discovery_hash,
|
||||||
)
|
)
|
||||||
|
from .mixins import (
|
||||||
|
CONF_CONNECTIONS,
|
||||||
|
CONF_IDENTIFIERS,
|
||||||
|
MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
||||||
|
cleanup_device_registry,
|
||||||
|
device_info_from_config,
|
||||||
|
validate_device_has_at_least_one_identifier,
|
||||||
|
)
|
||||||
from .util import valid_subscribe_topic
|
from .util import valid_subscribe_topic
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
@ -39,12 +37,12 @@ TAGS = "mqtt_tags"
|
||||||
|
|
||||||
PLATFORM_SCHEMA = mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend(
|
PLATFORM_SCHEMA = mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend(
|
||||||
{
|
{
|
||||||
vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
vol.Optional(CONF_DEVICE): MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
||||||
vol.Optional(CONF_PLATFORM): "mqtt",
|
vol.Optional(CONF_PLATFORM): "mqtt",
|
||||||
vol.Required(CONF_TOPIC): valid_subscribe_topic,
|
vol.Required(CONF_TOPIC): valid_subscribe_topic,
|
||||||
vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
|
vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
|
||||||
},
|
},
|
||||||
mqtt.validate_device_has_at_least_one_identifier,
|
validate_device_has_at_least_one_identifier,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -236,7 +234,7 @@ async def _update_device(hass, config_entry, config):
|
||||||
"""Update device registry."""
|
"""Update device registry."""
|
||||||
device_registry = await hass.helpers.device_registry.async_get_registry()
|
device_registry = await hass.helpers.device_registry.async_get_registry()
|
||||||
config_entry_id = config_entry.entry_id
|
config_entry_id = config_entry.entry_id
|
||||||
device_info = mqtt.device_info_from_config(config[CONF_DEVICE])
|
device_info = device_info_from_config(config[CONF_DEVICE])
|
||||||
|
|
||||||
if config_entry_id is not None and device_info is not None:
|
if config_entry_id is not None and device_info is not None:
|
||||||
device_info["config_entry_id"] = config_entry_id
|
device_info["config_entry_id"] = config_entry_id
|
||||||
|
|
|
@ -10,7 +10,8 @@ from homeassistant.helpers.dispatcher import (
|
||||||
)
|
)
|
||||||
from homeassistant.helpers.reload import async_setup_reload_service
|
from homeassistant.helpers.reload import async_setup_reload_service
|
||||||
|
|
||||||
from .. import ATTR_DISCOVERY_HASH, DOMAIN as MQTT_DOMAIN, PLATFORMS
|
from .. import DOMAIN as MQTT_DOMAIN, PLATFORMS
|
||||||
|
from ..const import ATTR_DISCOVERY_HASH
|
||||||
from ..discovery import MQTT_DISCOVERY_DONE, MQTT_DISCOVERY_NEW, clear_discovery_hash
|
from ..discovery import MQTT_DISCOVERY_DONE, MQTT_DISCOVERY_NEW, clear_discovery_hash
|
||||||
from .schema import CONF_SCHEMA, LEGACY, MQTT_VACUUM_SCHEMA, STATE
|
from .schema import CONF_SCHEMA, LEGACY, MQTT_VACUUM_SCHEMA, STATE
|
||||||
from .schema_legacy import PLATFORM_SCHEMA_LEGACY, async_setup_entity_legacy
|
from .schema_legacy import PLATFORM_SCHEMA_LEGACY, async_setup_entity_legacy
|
||||||
|
|
|
@ -28,15 +28,18 @@ from homeassistant.core import callback
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers.icon import icon_for_battery_level
|
from homeassistant.helpers.icon import icon_for_battery_level
|
||||||
|
|
||||||
from .. import (
|
from .. import subscription
|
||||||
|
from ... import mqtt
|
||||||
|
from ..debug_info import log_messages
|
||||||
|
from ..mixins import (
|
||||||
|
MQTT_AVAILABILITY_SCHEMA,
|
||||||
|
MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
||||||
|
MQTT_JSON_ATTRS_SCHEMA,
|
||||||
MqttAttributes,
|
MqttAttributes,
|
||||||
MqttAvailability,
|
MqttAvailability,
|
||||||
MqttDiscoveryUpdate,
|
MqttDiscoveryUpdate,
|
||||||
MqttEntityDeviceInfo,
|
MqttEntityDeviceInfo,
|
||||||
subscription,
|
|
||||||
)
|
)
|
||||||
from ... import mqtt
|
|
||||||
from ..debug_info import log_messages
|
|
||||||
from .schema import MQTT_VACUUM_SCHEMA, services_to_strings, strings_to_services
|
from .schema import MQTT_VACUUM_SCHEMA, services_to_strings, strings_to_services
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
@ -120,7 +123,7 @@ PLATFORM_SCHEMA_LEGACY = (
|
||||||
vol.Inclusive(CONF_CHARGING_TOPIC, "charging"): mqtt.valid_publish_topic,
|
vol.Inclusive(CONF_CHARGING_TOPIC, "charging"): mqtt.valid_publish_topic,
|
||||||
vol.Inclusive(CONF_CLEANING_TEMPLATE, "cleaning"): cv.template,
|
vol.Inclusive(CONF_CLEANING_TEMPLATE, "cleaning"): cv.template,
|
||||||
vol.Inclusive(CONF_CLEANING_TOPIC, "cleaning"): mqtt.valid_publish_topic,
|
vol.Inclusive(CONF_CLEANING_TOPIC, "cleaning"): mqtt.valid_publish_topic,
|
||||||
vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
vol.Optional(CONF_DEVICE): MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
||||||
vol.Inclusive(CONF_DOCKED_TEMPLATE, "docked"): cv.template,
|
vol.Inclusive(CONF_DOCKED_TEMPLATE, "docked"): cv.template,
|
||||||
vol.Inclusive(CONF_DOCKED_TOPIC, "docked"): mqtt.valid_publish_topic,
|
vol.Inclusive(CONF_DOCKED_TOPIC, "docked"): mqtt.valid_publish_topic,
|
||||||
vol.Inclusive(CONF_ERROR_TEMPLATE, "error"): cv.template,
|
vol.Inclusive(CONF_ERROR_TEMPLATE, "error"): cv.template,
|
||||||
|
@ -160,8 +163,8 @@ PLATFORM_SCHEMA_LEGACY = (
|
||||||
vol.Optional(mqtt.CONF_RETAIN, default=DEFAULT_RETAIN): cv.boolean,
|
vol.Optional(mqtt.CONF_RETAIN, default=DEFAULT_RETAIN): cv.boolean,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema)
|
.extend(MQTT_AVAILABILITY_SCHEMA.schema)
|
||||||
.extend(mqtt.MQTT_JSON_ATTRS_SCHEMA.schema)
|
.extend(MQTT_JSON_ATTRS_SCHEMA.schema)
|
||||||
.extend(MQTT_VACUUM_SCHEMA.schema)
|
.extend(MQTT_VACUUM_SCHEMA.schema)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -32,19 +32,18 @@ from homeassistant.const import (
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
|
||||||
from .. import (
|
from .. import CONF_COMMAND_TOPIC, CONF_QOS, CONF_RETAIN, CONF_STATE_TOPIC, subscription
|
||||||
CONF_COMMAND_TOPIC,
|
from ... import mqtt
|
||||||
CONF_QOS,
|
from ..debug_info import log_messages
|
||||||
CONF_RETAIN,
|
from ..mixins import (
|
||||||
CONF_STATE_TOPIC,
|
MQTT_AVAILABILITY_SCHEMA,
|
||||||
|
MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
||||||
|
MQTT_JSON_ATTRS_SCHEMA,
|
||||||
MqttAttributes,
|
MqttAttributes,
|
||||||
MqttAvailability,
|
MqttAvailability,
|
||||||
MqttDiscoveryUpdate,
|
MqttDiscoveryUpdate,
|
||||||
MqttEntityDeviceInfo,
|
MqttEntityDeviceInfo,
|
||||||
subscription,
|
|
||||||
)
|
)
|
||||||
from ... import mqtt
|
|
||||||
from ..debug_info import log_messages
|
|
||||||
from .schema import MQTT_VACUUM_SCHEMA, services_to_strings, strings_to_services
|
from .schema import MQTT_VACUUM_SCHEMA, services_to_strings, strings_to_services
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
@ -120,7 +119,7 @@ DEFAULT_PAYLOAD_PAUSE = "pause"
|
||||||
PLATFORM_SCHEMA_STATE = (
|
PLATFORM_SCHEMA_STATE = (
|
||||||
mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend(
|
mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend(
|
||||||
{
|
{
|
||||||
vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
vol.Optional(CONF_DEVICE): MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
||||||
vol.Optional(CONF_FAN_SPEED_LIST, default=[]): vol.All(
|
vol.Optional(CONF_FAN_SPEED_LIST, default=[]): vol.All(
|
||||||
cv.ensure_list, [cv.string]
|
cv.ensure_list, [cv.string]
|
||||||
),
|
),
|
||||||
|
@ -148,8 +147,8 @@ PLATFORM_SCHEMA_STATE = (
|
||||||
vol.Optional(CONF_RETAIN, default=DEFAULT_RETAIN): cv.boolean,
|
vol.Optional(CONF_RETAIN, default=DEFAULT_RETAIN): cv.boolean,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema)
|
.extend(MQTT_AVAILABILITY_SCHEMA.schema)
|
||||||
.extend(mqtt.MQTT_JSON_ATTRS_SCHEMA.schema)
|
.extend(MQTT_JSON_ATTRS_SCHEMA.schema)
|
||||||
.extend(MQTT_VACUUM_SCHEMA.schema)
|
.extend(MQTT_VACUUM_SCHEMA.schema)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components import mqtt, websocket_api
|
from homeassistant.components import mqtt, websocket_api
|
||||||
from homeassistant.components.mqtt import debug_info
|
from homeassistant.components.mqtt import debug_info
|
||||||
|
from homeassistant.components.mqtt.mixins import MQTT_ENTITY_DEVICE_INFO_SCHEMA
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_DOMAIN,
|
ATTR_DOMAIN,
|
||||||
ATTR_SERVICE,
|
ATTR_SERVICE,
|
||||||
|
@ -241,12 +242,12 @@ def test_validate_publish_topic():
|
||||||
def test_entity_device_info_schema():
|
def test_entity_device_info_schema():
|
||||||
"""Test MQTT entity device info validation."""
|
"""Test MQTT entity device info validation."""
|
||||||
# just identifier
|
# just identifier
|
||||||
mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA({"identifiers": ["abcd"]})
|
MQTT_ENTITY_DEVICE_INFO_SCHEMA({"identifiers": ["abcd"]})
|
||||||
mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA({"identifiers": "abcd"})
|
MQTT_ENTITY_DEVICE_INFO_SCHEMA({"identifiers": "abcd"})
|
||||||
# just connection
|
# just connection
|
||||||
mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA({"connections": [["mac", "02:5b:26:a8:dc:12"]]})
|
MQTT_ENTITY_DEVICE_INFO_SCHEMA({"connections": [["mac", "02:5b:26:a8:dc:12"]]})
|
||||||
# full device info
|
# full device info
|
||||||
mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA(
|
MQTT_ENTITY_DEVICE_INFO_SCHEMA(
|
||||||
{
|
{
|
||||||
"identifiers": ["helloworld", "hello"],
|
"identifiers": ["helloworld", "hello"],
|
||||||
"connections": [["mac", "02:5b:26:a8:dc:12"], ["zigbee", "zigbee_id"]],
|
"connections": [["mac", "02:5b:26:a8:dc:12"], ["zigbee", "zigbee_id"]],
|
||||||
|
@ -257,7 +258,7 @@ def test_entity_device_info_schema():
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
# full device info with via_device
|
# full device info with via_device
|
||||||
mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA(
|
MQTT_ENTITY_DEVICE_INFO_SCHEMA(
|
||||||
{
|
{
|
||||||
"identifiers": ["helloworld", "hello"],
|
"identifiers": ["helloworld", "hello"],
|
||||||
"connections": [["mac", "02:5b:26:a8:dc:12"], ["zigbee", "zigbee_id"]],
|
"connections": [["mac", "02:5b:26:a8:dc:12"], ["zigbee", "zigbee_id"]],
|
||||||
|
@ -270,7 +271,7 @@ def test_entity_device_info_schema():
|
||||||
)
|
)
|
||||||
# no identifiers
|
# no identifiers
|
||||||
with pytest.raises(vol.Invalid):
|
with pytest.raises(vol.Invalid):
|
||||||
mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA(
|
MQTT_ENTITY_DEVICE_INFO_SCHEMA(
|
||||||
{
|
{
|
||||||
"manufacturer": "Whatever",
|
"manufacturer": "Whatever",
|
||||||
"name": "Beer",
|
"name": "Beer",
|
||||||
|
@ -280,7 +281,7 @@ def test_entity_device_info_schema():
|
||||||
)
|
)
|
||||||
# empty identifiers
|
# empty identifiers
|
||||||
with pytest.raises(vol.Invalid):
|
with pytest.raises(vol.Invalid):
|
||||||
mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA(
|
MQTT_ENTITY_DEVICE_INFO_SCHEMA(
|
||||||
{"identifiers": [], "connections": [], "name": "Beer"}
|
{"identifiers": [], "connections": [], "name": "Beer"}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue