Improve MQTT type hints part 4 (#80971)
* Improve typing humidifier * Improve typing lock * Improve typing number * Set humidifier type hints at class level * Set lock type hints at class level * Set number type hints at class level * Some small updates * Follow up comment * Remove assertpull/81706/head
parent
499839c596
commit
ff4456cb29
|
@ -1,6 +1,7 @@
|
||||||
"""Support for MQTT humidifiers."""
|
"""Support for MQTT humidifiers."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections.abc import Callable
|
||||||
import functools
|
import functools
|
||||||
import logging
|
import logging
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
@ -28,6 +29,7 @@ from homeassistant.const import (
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
from homeassistant.helpers.template import Template
|
||||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||||
|
|
||||||
from . import subscription
|
from . import subscription
|
||||||
|
@ -50,7 +52,13 @@ from .mixins import (
|
||||||
async_setup_platform_helper,
|
async_setup_platform_helper,
|
||||||
warn_for_legacy_schema,
|
warn_for_legacy_schema,
|
||||||
)
|
)
|
||||||
from .models import MqttCommandTemplate, MqttValueTemplate
|
from .models import (
|
||||||
|
MqttCommandTemplate,
|
||||||
|
MqttValueTemplate,
|
||||||
|
PublishPayloadType,
|
||||||
|
ReceiveMessage,
|
||||||
|
ReceivePayloadType,
|
||||||
|
)
|
||||||
from .util import get_mqtt_data, valid_publish_topic, valid_subscribe_topic
|
from .util import get_mqtt_data, valid_publish_topic, valid_subscribe_topic
|
||||||
|
|
||||||
CONF_AVAILABLE_MODES_LIST = "modes"
|
CONF_AVAILABLE_MODES_LIST = "modes"
|
||||||
|
@ -87,18 +95,18 @@ MQTT_HUMIDIFIER_ATTRIBUTES_BLOCKED = frozenset(
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def valid_mode_configuration(config):
|
def valid_mode_configuration(config: ConfigType) -> ConfigType:
|
||||||
"""Validate that the mode reset payload is not one of the available modes."""
|
"""Validate that the mode reset payload is not one of the available modes."""
|
||||||
if config.get(CONF_PAYLOAD_RESET_MODE) in config.get(CONF_AVAILABLE_MODES_LIST):
|
if config[CONF_PAYLOAD_RESET_MODE] in config[CONF_AVAILABLE_MODES_LIST]:
|
||||||
raise ValueError("modes must not contain payload_reset_mode")
|
raise ValueError("modes must not contain payload_reset_mode")
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|
||||||
def valid_humidity_range_configuration(config):
|
def valid_humidity_range_configuration(config: ConfigType) -> ConfigType:
|
||||||
"""Validate that the target_humidity range configuration is valid, throws if it isn't."""
|
"""Validate that the target_humidity range configuration is valid, throws if it isn't."""
|
||||||
if config.get(CONF_TARGET_HUMIDITY_MIN) >= config.get(CONF_TARGET_HUMIDITY_MAX):
|
if config[CONF_TARGET_HUMIDITY_MIN] >= config[CONF_TARGET_HUMIDITY_MAX]:
|
||||||
raise ValueError("target_humidity_max must be > target_humidity_min")
|
raise ValueError("target_humidity_max must be > target_humidity_min")
|
||||||
if config.get(CONF_TARGET_HUMIDITY_MAX) > 100:
|
if config[CONF_TARGET_HUMIDITY_MAX] > 100:
|
||||||
raise ValueError("max_humidity must be <= 100")
|
raise ValueError("max_humidity must be <= 100")
|
||||||
|
|
||||||
return config
|
return config
|
||||||
|
@ -196,8 +204,8 @@ async def _async_setup_entity(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
config: ConfigType,
|
config: ConfigType,
|
||||||
config_entry: ConfigEntry | None = None,
|
config_entry: ConfigEntry,
|
||||||
discovery_data: dict | None = None,
|
discovery_data: DiscoveryInfoType | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the MQTT humidifier."""
|
"""Set up the MQTT humidifier."""
|
||||||
async_add_entities([MqttHumidifier(hass, config, config_entry, discovery_data)])
|
async_add_entities([MqttHumidifier(hass, config, config_entry, discovery_data)])
|
||||||
|
@ -209,30 +217,36 @@ class MqttHumidifier(MqttEntity, HumidifierEntity):
|
||||||
_entity_id_format = humidifier.ENTITY_ID_FORMAT
|
_entity_id_format = humidifier.ENTITY_ID_FORMAT
|
||||||
_attributes_extra_blocked = MQTT_HUMIDIFIER_ATTRIBUTES_BLOCKED
|
_attributes_extra_blocked = MQTT_HUMIDIFIER_ATTRIBUTES_BLOCKED
|
||||||
|
|
||||||
def __init__(self, hass, config, config_entry, discovery_data):
|
_command_templates: dict[str, Callable[[PublishPayloadType], PublishPayloadType]]
|
||||||
|
_value_templates: dict[str, Callable[[ReceivePayloadType], ReceivePayloadType]]
|
||||||
|
_optimistic: bool
|
||||||
|
_optimistic_target_humidity: bool
|
||||||
|
_optimistic_mode: bool
|
||||||
|
_payload: dict[str, str]
|
||||||
|
_topic: dict[str, Any]
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
hass: HomeAssistant,
|
||||||
|
config: ConfigType,
|
||||||
|
config_entry: ConfigEntry,
|
||||||
|
discovery_data: DiscoveryInfoType | None,
|
||||||
|
) -> None:
|
||||||
"""Initialize the MQTT humidifier."""
|
"""Initialize the MQTT humidifier."""
|
||||||
self._attr_mode = None
|
self._attr_mode = None
|
||||||
|
|
||||||
self._topic = None
|
|
||||||
self._payload = None
|
|
||||||
self._value_templates = None
|
|
||||||
self._command_templates = None
|
|
||||||
self._optimistic = None
|
|
||||||
self._optimistic_target_humidity = None
|
|
||||||
self._optimistic_mode = None
|
|
||||||
|
|
||||||
MqttEntity.__init__(self, hass, config, config_entry, discovery_data)
|
MqttEntity.__init__(self, hass, config, config_entry, discovery_data)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def config_schema():
|
def config_schema() -> vol.Schema:
|
||||||
"""Return the config schema."""
|
"""Return the config schema."""
|
||||||
return DISCOVERY_SCHEMA
|
return DISCOVERY_SCHEMA
|
||||||
|
|
||||||
def _setup_from_config(self, config):
|
def _setup_from_config(self, config: ConfigType) -> None:
|
||||||
"""(Re)Setup the entity."""
|
"""(Re)Setup the entity."""
|
||||||
self._attr_device_class = config.get(CONF_DEVICE_CLASS)
|
self._attr_device_class = config.get(CONF_DEVICE_CLASS)
|
||||||
self._attr_min_humidity = config.get(CONF_TARGET_HUMIDITY_MIN)
|
self._attr_min_humidity = config[CONF_TARGET_HUMIDITY_MIN]
|
||||||
self._attr_max_humidity = config.get(CONF_TARGET_HUMIDITY_MAX)
|
self._attr_max_humidity = config[CONF_TARGET_HUMIDITY_MAX]
|
||||||
|
|
||||||
self._topic = {
|
self._topic = {
|
||||||
key: config.get(key)
|
key: config.get(key)
|
||||||
|
@ -245,16 +259,6 @@ class MqttHumidifier(MqttEntity, HumidifierEntity):
|
||||||
CONF_MODE_COMMAND_TOPIC,
|
CONF_MODE_COMMAND_TOPIC,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
self._value_templates = {
|
|
||||||
CONF_STATE: config.get(CONF_STATE_VALUE_TEMPLATE),
|
|
||||||
ATTR_HUMIDITY: config.get(CONF_TARGET_HUMIDITY_STATE_TEMPLATE),
|
|
||||||
ATTR_MODE: config.get(CONF_MODE_STATE_TEMPLATE),
|
|
||||||
}
|
|
||||||
self._command_templates = {
|
|
||||||
CONF_STATE: config.get(CONF_COMMAND_TEMPLATE),
|
|
||||||
ATTR_HUMIDITY: config.get(CONF_TARGET_HUMIDITY_COMMAND_TEMPLATE),
|
|
||||||
ATTR_MODE: config.get(CONF_MODE_COMMAND_TEMPLATE),
|
|
||||||
}
|
|
||||||
self._payload = {
|
self._payload = {
|
||||||
"STATE_ON": config[CONF_PAYLOAD_ON],
|
"STATE_ON": config[CONF_PAYLOAD_ON],
|
||||||
"STATE_OFF": config[CONF_PAYLOAD_OFF],
|
"STATE_OFF": config[CONF_PAYLOAD_OFF],
|
||||||
|
@ -270,31 +274,43 @@ class MqttHumidifier(MqttEntity, HumidifierEntity):
|
||||||
else:
|
else:
|
||||||
self._attr_supported_features = 0
|
self._attr_supported_features = 0
|
||||||
|
|
||||||
optimistic = config[CONF_OPTIMISTIC]
|
optimistic: bool = config[CONF_OPTIMISTIC]
|
||||||
self._optimistic = optimistic or self._topic[CONF_STATE_TOPIC] is None
|
self._optimistic = optimistic or self._topic[CONF_STATE_TOPIC] is None
|
||||||
self._optimistic_target_humidity = (
|
self._optimistic_target_humidity = (
|
||||||
optimistic or self._topic[CONF_TARGET_HUMIDITY_STATE_TOPIC] is None
|
optimistic or self._topic[CONF_TARGET_HUMIDITY_STATE_TOPIC] is None
|
||||||
)
|
)
|
||||||
self._optimistic_mode = optimistic or self._topic[CONF_MODE_STATE_TOPIC] is None
|
self._optimistic_mode = optimistic or self._topic[CONF_MODE_STATE_TOPIC] is None
|
||||||
|
|
||||||
for key, tpl in self._command_templates.items():
|
self._command_templates = {}
|
||||||
|
command_templates: dict[str, Template | None] = {
|
||||||
|
CONF_STATE: config.get(CONF_COMMAND_TEMPLATE),
|
||||||
|
ATTR_HUMIDITY: config.get(CONF_TARGET_HUMIDITY_COMMAND_TEMPLATE),
|
||||||
|
ATTR_MODE: config.get(CONF_MODE_COMMAND_TEMPLATE),
|
||||||
|
}
|
||||||
|
for key, tpl in command_templates.items():
|
||||||
self._command_templates[key] = MqttCommandTemplate(
|
self._command_templates[key] = MqttCommandTemplate(
|
||||||
tpl, entity=self
|
tpl, entity=self
|
||||||
).async_render
|
).async_render
|
||||||
|
|
||||||
for key, tpl in self._value_templates.items():
|
self._value_templates = {}
|
||||||
|
value_templates: dict[str, Template | None] = {
|
||||||
|
CONF_STATE: config.get(CONF_STATE_VALUE_TEMPLATE),
|
||||||
|
ATTR_HUMIDITY: config.get(CONF_TARGET_HUMIDITY_STATE_TEMPLATE),
|
||||||
|
ATTR_MODE: config.get(CONF_MODE_STATE_TEMPLATE),
|
||||||
|
}
|
||||||
|
for key, tpl in value_templates.items():
|
||||||
self._value_templates[key] = MqttValueTemplate(
|
self._value_templates[key] = MqttValueTemplate(
|
||||||
tpl,
|
tpl,
|
||||||
entity=self,
|
entity=self,
|
||||||
).async_render_with_possible_json_value
|
).async_render_with_possible_json_value
|
||||||
|
|
||||||
def _prepare_subscribe_topics(self):
|
def _prepare_subscribe_topics(self) -> None:
|
||||||
"""(Re)Subscribe to topics."""
|
"""(Re)Subscribe to topics."""
|
||||||
topics = {}
|
topics: dict[str, Any] = {}
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
@log_messages(self.hass, self.entity_id)
|
@log_messages(self.hass, self.entity_id)
|
||||||
def state_received(msg):
|
def state_received(msg: ReceiveMessage) -> None:
|
||||||
"""Handle new received MQTT message."""
|
"""Handle new received MQTT message."""
|
||||||
payload = self._value_templates[CONF_STATE](msg.payload)
|
payload = self._value_templates[CONF_STATE](msg.payload)
|
||||||
if not payload:
|
if not payload:
|
||||||
|
@ -318,7 +334,7 @@ class MqttHumidifier(MqttEntity, HumidifierEntity):
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
@log_messages(self.hass, self.entity_id)
|
@log_messages(self.hass, self.entity_id)
|
||||||
def target_humidity_received(msg):
|
def target_humidity_received(msg: ReceiveMessage) -> None:
|
||||||
"""Handle new received MQTT message for the target humidity."""
|
"""Handle new received MQTT message for the target humidity."""
|
||||||
rendered_target_humidity_payload = self._value_templates[ATTR_HUMIDITY](
|
rendered_target_humidity_payload = self._value_templates[ATTR_HUMIDITY](
|
||||||
msg.payload
|
msg.payload
|
||||||
|
@ -365,9 +381,9 @@ class MqttHumidifier(MqttEntity, HumidifierEntity):
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
@log_messages(self.hass, self.entity_id)
|
@log_messages(self.hass, self.entity_id)
|
||||||
def mode_received(msg):
|
def mode_received(msg: ReceiveMessage) -> None:
|
||||||
"""Handle new received MQTT message for mode."""
|
"""Handle new received MQTT message for mode."""
|
||||||
mode = self._value_templates[ATTR_MODE](msg.payload)
|
mode = str(self._value_templates[ATTR_MODE](msg.payload))
|
||||||
if mode == self._payload["MODE_RESET"]:
|
if mode == self._payload["MODE_RESET"]:
|
||||||
self._attr_mode = None
|
self._attr_mode = None
|
||||||
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
||||||
|
@ -375,7 +391,7 @@ class MqttHumidifier(MqttEntity, HumidifierEntity):
|
||||||
if not mode:
|
if not mode:
|
||||||
_LOGGER.debug("Ignoring empty mode from '%s'", msg.topic)
|
_LOGGER.debug("Ignoring empty mode from '%s'", msg.topic)
|
||||||
return
|
return
|
||||||
if mode not in self.available_modes:
|
if not self.available_modes or mode not in self.available_modes:
|
||||||
_LOGGER.warning(
|
_LOGGER.warning(
|
||||||
"'%s' received on topic %s. '%s' is not a valid mode",
|
"'%s' received on topic %s. '%s' is not a valid mode",
|
||||||
msg.payload,
|
msg.payload,
|
||||||
|
@ -400,7 +416,7 @@ class MqttHumidifier(MqttEntity, HumidifierEntity):
|
||||||
self.hass, self._sub_state, topics
|
self.hass, self._sub_state, topics
|
||||||
)
|
)
|
||||||
|
|
||||||
async def _subscribe_topics(self):
|
async def _subscribe_topics(self) -> None:
|
||||||
"""(Re)Subscribe to topics."""
|
"""(Re)Subscribe to topics."""
|
||||||
await subscription.async_subscribe_topics(self.hass, self._sub_state)
|
await subscription.async_subscribe_topics(self.hass, self._sub_state)
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
"""Support for MQTT locks."""
|
"""Support for MQTT locks."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections.abc import Callable
|
||||||
import functools
|
import functools
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
@ -32,7 +33,7 @@ from .mixins import (
|
||||||
async_setup_platform_helper,
|
async_setup_platform_helper,
|
||||||
warn_for_legacy_schema,
|
warn_for_legacy_schema,
|
||||||
)
|
)
|
||||||
from .models import MqttValueTemplate
|
from .models import MqttValueTemplate, ReceiveMessage, ReceivePayloadType
|
||||||
from .util import get_mqtt_data
|
from .util import get_mqtt_data
|
||||||
|
|
||||||
CONF_PAYLOAD_LOCK = "payload_lock"
|
CONF_PAYLOAD_LOCK = "payload_lock"
|
||||||
|
@ -112,8 +113,8 @@ async def _async_setup_entity(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
config: ConfigType,
|
config: ConfigType,
|
||||||
config_entry: ConfigEntry | None = None,
|
config_entry: ConfigEntry,
|
||||||
discovery_data: dict | None = None,
|
discovery_data: DiscoveryInfoType | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the MQTT Lock platform."""
|
"""Set up the MQTT Lock platform."""
|
||||||
async_add_entities([MqttLock(hass, config, config_entry, discovery_data)])
|
async_add_entities([MqttLock(hass, config, config_entry, discovery_data)])
|
||||||
|
@ -125,39 +126,50 @@ class MqttLock(MqttEntity, LockEntity):
|
||||||
_entity_id_format = lock.ENTITY_ID_FORMAT
|
_entity_id_format = lock.ENTITY_ID_FORMAT
|
||||||
_attributes_extra_blocked = MQTT_LOCK_ATTRIBUTES_BLOCKED
|
_attributes_extra_blocked = MQTT_LOCK_ATTRIBUTES_BLOCKED
|
||||||
|
|
||||||
def __init__(self, hass, config, config_entry, discovery_data):
|
_optimistic: bool
|
||||||
"""Initialize the lock."""
|
_value_template: Callable[[ReceivePayloadType], ReceivePayloadType]
|
||||||
self._state = False
|
|
||||||
self._optimistic = False
|
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
hass: HomeAssistant,
|
||||||
|
config: ConfigType,
|
||||||
|
config_entry: ConfigEntry,
|
||||||
|
discovery_data: DiscoveryInfoType | None,
|
||||||
|
) -> None:
|
||||||
|
"""Initialize the lock."""
|
||||||
|
self._attr_is_locked = False
|
||||||
MqttEntity.__init__(self, hass, config, config_entry, discovery_data)
|
MqttEntity.__init__(self, hass, config, config_entry, discovery_data)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def config_schema():
|
def config_schema() -> vol.Schema:
|
||||||
"""Return the config schema."""
|
"""Return the config schema."""
|
||||||
return DISCOVERY_SCHEMA
|
return DISCOVERY_SCHEMA
|
||||||
|
|
||||||
def _setup_from_config(self, config):
|
def _setup_from_config(self, config: ConfigType) -> None:
|
||||||
"""(Re)Setup the entity."""
|
"""(Re)Setup the entity."""
|
||||||
self._optimistic = config[CONF_OPTIMISTIC]
|
self._optimistic = config[CONF_OPTIMISTIC]
|
||||||
|
|
||||||
self._value_template = MqttValueTemplate(
|
self._value_template = MqttValueTemplate(
|
||||||
self._config.get(CONF_VALUE_TEMPLATE),
|
config.get(CONF_VALUE_TEMPLATE),
|
||||||
entity=self,
|
entity=self,
|
||||||
).async_render_with_possible_json_value
|
).async_render_with_possible_json_value
|
||||||
|
|
||||||
def _prepare_subscribe_topics(self):
|
self._attr_supported_features = (
|
||||||
|
LockEntityFeature.OPEN if CONF_PAYLOAD_OPEN in config else 0
|
||||||
|
)
|
||||||
|
|
||||||
|
def _prepare_subscribe_topics(self) -> None:
|
||||||
"""(Re)Subscribe to topics."""
|
"""(Re)Subscribe to topics."""
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
@log_messages(self.hass, self.entity_id)
|
@log_messages(self.hass, self.entity_id)
|
||||||
def message_received(msg):
|
def message_received(msg: ReceiveMessage) -> None:
|
||||||
"""Handle new MQTT messages."""
|
"""Handle new MQTT messages."""
|
||||||
payload = self._value_template(msg.payload)
|
payload = self._value_template(msg.payload)
|
||||||
if payload == self._config[CONF_STATE_LOCKED]:
|
if payload == self._config[CONF_STATE_LOCKED]:
|
||||||
self._state = True
|
self._attr_is_locked = True
|
||||||
elif payload == self._config[CONF_STATE_UNLOCKED]:
|
elif payload == self._config[CONF_STATE_UNLOCKED]:
|
||||||
self._state = False
|
self._attr_is_locked = False
|
||||||
|
|
||||||
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
||||||
|
|
||||||
|
@ -178,25 +190,15 @@ class MqttLock(MqttEntity, LockEntity):
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
async def _subscribe_topics(self):
|
async def _subscribe_topics(self) -> None:
|
||||||
"""(Re)Subscribe to topics."""
|
"""(Re)Subscribe to topics."""
|
||||||
await subscription.async_subscribe_topics(self.hass, self._sub_state)
|
await subscription.async_subscribe_topics(self.hass, self._sub_state)
|
||||||
|
|
||||||
@property
|
|
||||||
def is_locked(self) -> bool:
|
|
||||||
"""Return true if lock is locked."""
|
|
||||||
return self._state
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def assumed_state(self) -> bool:
|
def assumed_state(self) -> bool:
|
||||||
"""Return true if we do optimistic updates."""
|
"""Return true if we do optimistic updates."""
|
||||||
return self._optimistic
|
return self._optimistic
|
||||||
|
|
||||||
@property
|
|
||||||
def supported_features(self) -> int:
|
|
||||||
"""Flag supported features."""
|
|
||||||
return LockEntityFeature.OPEN if CONF_PAYLOAD_OPEN in self._config else 0
|
|
||||||
|
|
||||||
async def async_lock(self, **kwargs: Any) -> None:
|
async def async_lock(self, **kwargs: Any) -> None:
|
||||||
"""Lock the device.
|
"""Lock the device.
|
||||||
|
|
||||||
|
@ -211,7 +213,7 @@ class MqttLock(MqttEntity, LockEntity):
|
||||||
)
|
)
|
||||||
if self._optimistic:
|
if self._optimistic:
|
||||||
# Optimistically assume that the lock has changed state.
|
# Optimistically assume that the lock has changed state.
|
||||||
self._state = True
|
self._attr_is_locked = True
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
async def async_unlock(self, **kwargs: Any) -> None:
|
async def async_unlock(self, **kwargs: Any) -> None:
|
||||||
|
@ -228,7 +230,7 @@ class MqttLock(MqttEntity, LockEntity):
|
||||||
)
|
)
|
||||||
if self._optimistic:
|
if self._optimistic:
|
||||||
# Optimistically assume that the lock has changed state.
|
# Optimistically assume that the lock has changed state.
|
||||||
self._state = False
|
self._attr_is_locked = False
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
async def async_open(self, **kwargs: Any) -> None:
|
async def async_open(self, **kwargs: Any) -> None:
|
||||||
|
@ -245,5 +247,5 @@ class MqttLock(MqttEntity, LockEntity):
|
||||||
)
|
)
|
||||||
if self._optimistic:
|
if self._optimistic:
|
||||||
# Optimistically assume that the lock unlocks when opened.
|
# Optimistically assume that the lock unlocks when opened.
|
||||||
self._state = False
|
self._attr_is_locked = False
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
"""Configure number in a device through MQTT topic."""
|
"""Configure number in a device through MQTT topic."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections.abc import Callable
|
||||||
import functools
|
import functools
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
@ -47,7 +48,13 @@ from .mixins import (
|
||||||
async_setup_platform_helper,
|
async_setup_platform_helper,
|
||||||
warn_for_legacy_schema,
|
warn_for_legacy_schema,
|
||||||
)
|
)
|
||||||
from .models import MqttCommandTemplate, MqttValueTemplate
|
from .models import (
|
||||||
|
MqttCommandTemplate,
|
||||||
|
MqttValueTemplate,
|
||||||
|
PublishPayloadType,
|
||||||
|
ReceiveMessage,
|
||||||
|
ReceivePayloadType,
|
||||||
|
)
|
||||||
from .util import get_mqtt_data
|
from .util import get_mqtt_data
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
@ -70,9 +77,9 @@ MQTT_NUMBER_ATTRIBUTES_BLOCKED = frozenset(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def validate_config(config):
|
def validate_config(config: ConfigType) -> ConfigType:
|
||||||
"""Validate that the configuration is valid, throws if it isn't."""
|
"""Validate that the configuration is valid, throws if it isn't."""
|
||||||
if config.get(CONF_MIN) >= config.get(CONF_MAX):
|
if config[CONF_MIN] >= config[CONF_MAX]:
|
||||||
raise vol.Invalid(f"'{CONF_MAX}' must be > '{CONF_MIN}'")
|
raise vol.Invalid(f"'{CONF_MAX}' must be > '{CONF_MIN}'")
|
||||||
|
|
||||||
return config
|
return config
|
||||||
|
@ -147,8 +154,8 @@ async def _async_setup_entity(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
config: ConfigType,
|
config: ConfigType,
|
||||||
config_entry: ConfigEntry | None = None,
|
config_entry: ConfigEntry,
|
||||||
discovery_data: dict | None = None,
|
discovery_data: DiscoveryInfoType | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the MQTT number."""
|
"""Set up the MQTT number."""
|
||||||
async_add_entities([MqttNumber(hass, config, config_entry, discovery_data)])
|
async_add_entities([MqttNumber(hass, config, config_entry, discovery_data)])
|
||||||
|
@ -160,33 +167,39 @@ class MqttNumber(MqttEntity, RestoreNumber):
|
||||||
_entity_id_format = number.ENTITY_ID_FORMAT
|
_entity_id_format = number.ENTITY_ID_FORMAT
|
||||||
_attributes_extra_blocked = MQTT_NUMBER_ATTRIBUTES_BLOCKED
|
_attributes_extra_blocked = MQTT_NUMBER_ATTRIBUTES_BLOCKED
|
||||||
|
|
||||||
def __init__(self, hass, config, config_entry, discovery_data):
|
_optimistic: bool
|
||||||
"""Initialize the MQTT Number."""
|
_command_template: Callable[[PublishPayloadType], PublishPayloadType]
|
||||||
self._config = config
|
_value_template: Callable[[ReceivePayloadType], ReceivePayloadType]
|
||||||
self._optimistic = False
|
|
||||||
self._sub_state = None
|
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
hass: HomeAssistant,
|
||||||
|
config: ConfigType,
|
||||||
|
config_entry: ConfigEntry,
|
||||||
|
discovery_data: DiscoveryInfoType | None,
|
||||||
|
) -> None:
|
||||||
|
"""Initialize the MQTT Number."""
|
||||||
RestoreNumber.__init__(self)
|
RestoreNumber.__init__(self)
|
||||||
MqttEntity.__init__(self, hass, config, config_entry, discovery_data)
|
MqttEntity.__init__(self, hass, config, config_entry, discovery_data)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def config_schema():
|
def config_schema() -> vol.Schema:
|
||||||
"""Return the config schema."""
|
"""Return the config schema."""
|
||||||
return DISCOVERY_SCHEMA
|
return DISCOVERY_SCHEMA
|
||||||
|
|
||||||
def _setup_from_config(self, config):
|
def _setup_from_config(self, config: ConfigType) -> None:
|
||||||
"""(Re)Setup the entity."""
|
"""(Re)Setup the entity."""
|
||||||
|
self._config = config
|
||||||
self._optimistic = config[CONF_OPTIMISTIC]
|
self._optimistic = config[CONF_OPTIMISTIC]
|
||||||
|
|
||||||
self._templates = {
|
self._command_template = MqttCommandTemplate(
|
||||||
CONF_COMMAND_TEMPLATE: MqttCommandTemplate(
|
|
||||||
config.get(CONF_COMMAND_TEMPLATE), entity=self
|
config.get(CONF_COMMAND_TEMPLATE), entity=self
|
||||||
).async_render,
|
).async_render
|
||||||
CONF_VALUE_TEMPLATE: MqttValueTemplate(
|
self._value_template = MqttValueTemplate(
|
||||||
config.get(CONF_VALUE_TEMPLATE),
|
config.get(CONF_VALUE_TEMPLATE),
|
||||||
entity=self,
|
entity=self,
|
||||||
).async_render_with_possible_json_value,
|
).async_render_with_possible_json_value
|
||||||
}
|
|
||||||
self._attr_device_class = config.get(CONF_DEVICE_CLASS)
|
self._attr_device_class = config.get(CONF_DEVICE_CLASS)
|
||||||
self._attr_mode = config[CONF_MODE]
|
self._attr_mode = config[CONF_MODE]
|
||||||
self._attr_native_max_value = config[CONF_MAX]
|
self._attr_native_max_value = config[CONF_MAX]
|
||||||
|
@ -194,14 +207,15 @@ class MqttNumber(MqttEntity, RestoreNumber):
|
||||||
self._attr_native_step = config[CONF_STEP]
|
self._attr_native_step = config[CONF_STEP]
|
||||||
self._attr_native_unit_of_measurement = config.get(CONF_UNIT_OF_MEASUREMENT)
|
self._attr_native_unit_of_measurement = config.get(CONF_UNIT_OF_MEASUREMENT)
|
||||||
|
|
||||||
def _prepare_subscribe_topics(self):
|
def _prepare_subscribe_topics(self) -> None:
|
||||||
"""(Re)Subscribe to topics."""
|
"""(Re)Subscribe to topics."""
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
@log_messages(self.hass, self.entity_id)
|
@log_messages(self.hass, self.entity_id)
|
||||||
def message_received(msg):
|
def message_received(msg: ReceiveMessage) -> None:
|
||||||
"""Handle new MQTT messages."""
|
"""Handle new MQTT messages."""
|
||||||
payload = self._templates[CONF_VALUE_TEMPLATE](msg.payload)
|
num_value: int | float | None
|
||||||
|
payload = str(self._value_template(msg.payload))
|
||||||
try:
|
try:
|
||||||
if payload == self._config[CONF_PAYLOAD_RESET]:
|
if payload == self._config[CONF_PAYLOAD_RESET]:
|
||||||
num_value = None
|
num_value = None
|
||||||
|
@ -245,7 +259,7 @@ class MqttNumber(MqttEntity, RestoreNumber):
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
async def _subscribe_topics(self):
|
async def _subscribe_topics(self) -> None:
|
||||||
"""(Re)Subscribe to topics."""
|
"""(Re)Subscribe to topics."""
|
||||||
await subscription.async_subscribe_topics(self.hass, self._sub_state)
|
await subscription.async_subscribe_topics(self.hass, self._sub_state)
|
||||||
|
|
||||||
|
@ -260,7 +274,7 @@ class MqttNumber(MqttEntity, RestoreNumber):
|
||||||
|
|
||||||
if value.is_integer():
|
if value.is_integer():
|
||||||
current_number = int(value)
|
current_number = int(value)
|
||||||
payload = self._templates[CONF_COMMAND_TEMPLATE](current_number)
|
payload = self._command_template(current_number)
|
||||||
|
|
||||||
if self._optimistic:
|
if self._optimistic:
|
||||||
self._attr_native_value = current_number
|
self._attr_native_value = current_number
|
||||||
|
|
Loading…
Reference in New Issue