mirror of
https://github.com/home-assistant/core.git
synced 2025-07-24 21:57:51 +00:00
2022.10.4 (#80343)
This commit is contained in:
commit
68907b4de9
@ -370,11 +370,12 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
event_codes = {
|
event_codes = {
|
||||||
sensor.event_code
|
event_code
|
||||||
for sensor in BINARY_SENSORS
|
for sensor in BINARY_SENSORS
|
||||||
if sensor.key in binary_sensors
|
if sensor.key in binary_sensors
|
||||||
and not sensor.should_poll
|
and not sensor.should_poll
|
||||||
and sensor.event_code is not None
|
and sensor.event_codes is not None
|
||||||
|
for event_code in sensor.event_codes
|
||||||
}
|
}
|
||||||
|
|
||||||
_start_event_monitor(hass, name, api, event_codes)
|
_start_event_monitor(hass, name, api, event_codes)
|
||||||
|
@ -39,7 +39,7 @@ if TYPE_CHECKING:
|
|||||||
class AmcrestSensorEntityDescription(BinarySensorEntityDescription):
|
class AmcrestSensorEntityDescription(BinarySensorEntityDescription):
|
||||||
"""Describe Amcrest sensor entity."""
|
"""Describe Amcrest sensor entity."""
|
||||||
|
|
||||||
event_code: str | None = None
|
event_codes: set[str] | None = None
|
||||||
should_poll: bool = False
|
should_poll: bool = False
|
||||||
|
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ _ONLINE_SCAN_INTERVAL = timedelta(seconds=60 - BINARY_SENSOR_SCAN_INTERVAL_SECS)
|
|||||||
_AUDIO_DETECTED_KEY = "audio_detected"
|
_AUDIO_DETECTED_KEY = "audio_detected"
|
||||||
_AUDIO_DETECTED_POLLED_KEY = "audio_detected_polled"
|
_AUDIO_DETECTED_POLLED_KEY = "audio_detected_polled"
|
||||||
_AUDIO_DETECTED_NAME = "Audio Detected"
|
_AUDIO_DETECTED_NAME = "Audio Detected"
|
||||||
_AUDIO_DETECTED_EVENT_CODE = "AudioMutation"
|
_AUDIO_DETECTED_EVENT_CODES = {"AudioMutation", "AudioIntensity"}
|
||||||
|
|
||||||
_CROSSLINE_DETECTED_KEY = "crossline_detected"
|
_CROSSLINE_DETECTED_KEY = "crossline_detected"
|
||||||
_CROSSLINE_DETECTED_POLLED_KEY = "crossline_detected_polled"
|
_CROSSLINE_DETECTED_POLLED_KEY = "crossline_detected_polled"
|
||||||
@ -70,39 +70,39 @@ BINARY_SENSORS: tuple[AmcrestSensorEntityDescription, ...] = (
|
|||||||
key=_AUDIO_DETECTED_KEY,
|
key=_AUDIO_DETECTED_KEY,
|
||||||
name=_AUDIO_DETECTED_NAME,
|
name=_AUDIO_DETECTED_NAME,
|
||||||
device_class=BinarySensorDeviceClass.SOUND,
|
device_class=BinarySensorDeviceClass.SOUND,
|
||||||
event_code=_AUDIO_DETECTED_EVENT_CODE,
|
event_codes=_AUDIO_DETECTED_EVENT_CODES,
|
||||||
),
|
),
|
||||||
AmcrestSensorEntityDescription(
|
AmcrestSensorEntityDescription(
|
||||||
key=_AUDIO_DETECTED_POLLED_KEY,
|
key=_AUDIO_DETECTED_POLLED_KEY,
|
||||||
name=_AUDIO_DETECTED_NAME,
|
name=_AUDIO_DETECTED_NAME,
|
||||||
device_class=BinarySensorDeviceClass.SOUND,
|
device_class=BinarySensorDeviceClass.SOUND,
|
||||||
event_code=_AUDIO_DETECTED_EVENT_CODE,
|
event_codes=_AUDIO_DETECTED_EVENT_CODES,
|
||||||
should_poll=True,
|
should_poll=True,
|
||||||
),
|
),
|
||||||
AmcrestSensorEntityDescription(
|
AmcrestSensorEntityDescription(
|
||||||
key=_CROSSLINE_DETECTED_KEY,
|
key=_CROSSLINE_DETECTED_KEY,
|
||||||
name=_CROSSLINE_DETECTED_NAME,
|
name=_CROSSLINE_DETECTED_NAME,
|
||||||
device_class=BinarySensorDeviceClass.MOTION,
|
device_class=BinarySensorDeviceClass.MOTION,
|
||||||
event_code=_CROSSLINE_DETECTED_EVENT_CODE,
|
event_codes={_CROSSLINE_DETECTED_EVENT_CODE},
|
||||||
),
|
),
|
||||||
AmcrestSensorEntityDescription(
|
AmcrestSensorEntityDescription(
|
||||||
key=_CROSSLINE_DETECTED_POLLED_KEY,
|
key=_CROSSLINE_DETECTED_POLLED_KEY,
|
||||||
name=_CROSSLINE_DETECTED_NAME,
|
name=_CROSSLINE_DETECTED_NAME,
|
||||||
device_class=BinarySensorDeviceClass.MOTION,
|
device_class=BinarySensorDeviceClass.MOTION,
|
||||||
event_code=_CROSSLINE_DETECTED_EVENT_CODE,
|
event_codes={_CROSSLINE_DETECTED_EVENT_CODE},
|
||||||
should_poll=True,
|
should_poll=True,
|
||||||
),
|
),
|
||||||
AmcrestSensorEntityDescription(
|
AmcrestSensorEntityDescription(
|
||||||
key=_MOTION_DETECTED_KEY,
|
key=_MOTION_DETECTED_KEY,
|
||||||
name=_MOTION_DETECTED_NAME,
|
name=_MOTION_DETECTED_NAME,
|
||||||
device_class=BinarySensorDeviceClass.MOTION,
|
device_class=BinarySensorDeviceClass.MOTION,
|
||||||
event_code=_MOTION_DETECTED_EVENT_CODE,
|
event_codes={_MOTION_DETECTED_EVENT_CODE},
|
||||||
),
|
),
|
||||||
AmcrestSensorEntityDescription(
|
AmcrestSensorEntityDescription(
|
||||||
key=_MOTION_DETECTED_POLLED_KEY,
|
key=_MOTION_DETECTED_POLLED_KEY,
|
||||||
name=_MOTION_DETECTED_NAME,
|
name=_MOTION_DETECTED_NAME,
|
||||||
device_class=BinarySensorDeviceClass.MOTION,
|
device_class=BinarySensorDeviceClass.MOTION,
|
||||||
event_code=_MOTION_DETECTED_EVENT_CODE,
|
event_codes={_MOTION_DETECTED_EVENT_CODE},
|
||||||
should_poll=True,
|
should_poll=True,
|
||||||
),
|
),
|
||||||
AmcrestSensorEntityDescription(
|
AmcrestSensorEntityDescription(
|
||||||
@ -211,13 +211,13 @@ class AmcrestBinarySensor(BinarySensorEntity):
|
|||||||
log_update_error(_LOGGER, "update", self.name, "binary sensor", error)
|
log_update_error(_LOGGER, "update", self.name, "binary sensor", error)
|
||||||
return
|
return
|
||||||
|
|
||||||
if (event_code := self.entity_description.event_code) is None:
|
if not (event_codes := self.entity_description.event_codes):
|
||||||
_LOGGER.error("Binary sensor %s event code not set", self.name)
|
raise ValueError(f"Binary sensor {self.name} event codes not set")
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self._attr_is_on = (
|
self._attr_is_on = any( # type: ignore[arg-type]
|
||||||
len(await self._api.async_event_channels_happened(event_code)) > 0
|
len(await self._api.async_event_channels_happened(event_code)) > 0
|
||||||
|
for event_code in event_codes
|
||||||
)
|
)
|
||||||
except AmcrestError as error:
|
except AmcrestError as error:
|
||||||
log_update_error(_LOGGER, "update", self.name, "binary sensor", error)
|
log_update_error(_LOGGER, "update", self.name, "binary sensor", error)
|
||||||
@ -266,17 +266,17 @@ class AmcrestBinarySensor(BinarySensorEntity):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
self.entity_description.event_code
|
event_codes := self.entity_description.event_codes
|
||||||
and not self.entity_description.should_poll
|
) and not self.entity_description.should_poll:
|
||||||
):
|
for event_code in event_codes:
|
||||||
self.async_on_remove(
|
self.async_on_remove(
|
||||||
async_dispatcher_connect(
|
async_dispatcher_connect(
|
||||||
self.hass,
|
self.hass,
|
||||||
service_signal(
|
service_signal(
|
||||||
SERVICE_EVENT,
|
SERVICE_EVENT,
|
||||||
self._signal_name,
|
self._signal_name,
|
||||||
self.entity_description.event_code,
|
event_code,
|
||||||
),
|
),
|
||||||
self.async_event_received,
|
self.async_event_received,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
"domain": "august",
|
"domain": "august",
|
||||||
"name": "August",
|
"name": "August",
|
||||||
"documentation": "https://www.home-assistant.io/integrations/august",
|
"documentation": "https://www.home-assistant.io/integrations/august",
|
||||||
"requirements": ["yalexs==1.2.4"],
|
"requirements": ["yalexs==1.2.6"],
|
||||||
"codeowners": ["@bdraco"],
|
"codeowners": ["@bdraco"],
|
||||||
"dhcp": [
|
"dhcp": [
|
||||||
{
|
{
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"name": "ESPHome",
|
"name": "ESPHome",
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/esphome",
|
"documentation": "https://www.home-assistant.io/integrations/esphome",
|
||||||
"requirements": ["aioesphomeapi==11.1.0"],
|
"requirements": ["aioesphomeapi==11.1.1"],
|
||||||
"zeroconf": ["_esphomelib._tcp.local."],
|
"zeroconf": ["_esphomelib._tcp.local."],
|
||||||
"dhcp": [{ "registered_devices": true }],
|
"dhcp": [{ "registered_devices": true }],
|
||||||
"codeowners": ["@OttoWinter", "@jesserockz"],
|
"codeowners": ["@OttoWinter", "@jesserockz"],
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"name": "HomeKit",
|
"name": "HomeKit",
|
||||||
"documentation": "https://www.home-assistant.io/integrations/homekit",
|
"documentation": "https://www.home-assistant.io/integrations/homekit",
|
||||||
"requirements": [
|
"requirements": [
|
||||||
"HAP-python==4.5.0",
|
"ha-HAP-python==4.5.2",
|
||||||
"fnvhash==0.1.0",
|
"fnvhash==0.1.0",
|
||||||
"PyQRCode==1.2.1",
|
"PyQRCode==1.2.1",
|
||||||
"base36==0.1.1"
|
"base36==0.1.1"
|
||||||
|
@ -49,7 +49,7 @@ from .mixins import (
|
|||||||
warn_for_legacy_schema,
|
warn_for_legacy_schema,
|
||||||
)
|
)
|
||||||
from .models import MqttCommandTemplate, MqttValueTemplate
|
from .models import MqttCommandTemplate, MqttValueTemplate
|
||||||
from .util import valid_publish_topic, valid_subscribe_topic
|
from .util import get_mqtt_data, valid_publish_topic, valid_subscribe_topic
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -211,7 +211,7 @@ class MqttAlarm(MqttEntity, alarm.AlarmControlPanelEntity):
|
|||||||
_LOGGER.warning("Received unexpected payload: %s", msg.payload)
|
_LOGGER.warning("Received unexpected payload: %s", msg.payload)
|
||||||
return
|
return
|
||||||
self._state = payload
|
self._state = payload
|
||||||
self.async_write_ha_state()
|
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
||||||
|
|
||||||
self._sub_state = subscription.async_prepare_subscribe_topics(
|
self._sub_state = subscription.async_prepare_subscribe_topics(
|
||||||
self.hass,
|
self.hass,
|
||||||
|
@ -47,6 +47,7 @@ from .mixins import (
|
|||||||
warn_for_legacy_schema,
|
warn_for_legacy_schema,
|
||||||
)
|
)
|
||||||
from .models import MqttValueTemplate
|
from .models import MqttValueTemplate
|
||||||
|
from .util import get_mqtt_data
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -260,7 +261,7 @@ class MqttBinarySensor(MqttEntity, BinarySensorEntity, RestoreEntity):
|
|||||||
self.hass, off_delay, off_delay_listener
|
self.hass, off_delay, off_delay_listener
|
||||||
)
|
)
|
||||||
|
|
||||||
self.async_write_ha_state()
|
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
||||||
|
|
||||||
self._sub_state = subscription.async_prepare_subscribe_topics(
|
self._sub_state = subscription.async_prepare_subscribe_topics(
|
||||||
self.hass,
|
self.hass,
|
||||||
|
@ -658,6 +658,7 @@ class MQTT:
|
|||||||
timestamp,
|
timestamp,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
self._mqtt_data.state_write_requests.process_write_state_requests()
|
||||||
|
|
||||||
def _mqtt_on_callback(self, _mqttc, _userdata, mid, _granted_qos=None) -> None:
|
def _mqtt_on_callback(self, _mqttc, _userdata, mid, _granted_qos=None) -> None:
|
||||||
"""Publish / Subscribe / Unsubscribe callback."""
|
"""Publish / Subscribe / Unsubscribe callback."""
|
||||||
|
@ -55,7 +55,7 @@ from .mixins import (
|
|||||||
warn_for_legacy_schema,
|
warn_for_legacy_schema,
|
||||||
)
|
)
|
||||||
from .models import MqttCommandTemplate, MqttValueTemplate
|
from .models import MqttCommandTemplate, MqttValueTemplate
|
||||||
from .util import valid_publish_topic, valid_subscribe_topic
|
from .util import get_mqtt_data, valid_publish_topic, valid_subscribe_topic
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -494,7 +494,7 @@ class MqttClimate(MqttEntity, ClimateEntity):
|
|||||||
payload,
|
payload,
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
self.async_write_ha_state()
|
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
||||||
|
|
||||||
add_subscription(topics, CONF_ACTION_TOPIC, handle_action_received)
|
add_subscription(topics, CONF_ACTION_TOPIC, handle_action_received)
|
||||||
|
|
||||||
@ -505,7 +505,7 @@ class MqttClimate(MqttEntity, ClimateEntity):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
setattr(self, attr, float(payload))
|
setattr(self, attr, float(payload))
|
||||||
self.async_write_ha_state()
|
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
_LOGGER.error("Could not parse temperature from %s", payload)
|
_LOGGER.error("Could not parse temperature from %s", payload)
|
||||||
|
|
||||||
@ -564,7 +564,7 @@ class MqttClimate(MqttEntity, ClimateEntity):
|
|||||||
_LOGGER.error("Invalid %s mode: %s", mode_list, payload)
|
_LOGGER.error("Invalid %s mode: %s", mode_list, payload)
|
||||||
else:
|
else:
|
||||||
setattr(self, attr, payload)
|
setattr(self, attr, payload)
|
||||||
self.async_write_ha_state()
|
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
@log_messages(self.hass, self.entity_id)
|
@log_messages(self.hass, self.entity_id)
|
||||||
@ -623,7 +623,7 @@ class MqttClimate(MqttEntity, ClimateEntity):
|
|||||||
else:
|
else:
|
||||||
_LOGGER.error("Invalid %s mode: %s", attr, payload)
|
_LOGGER.error("Invalid %s mode: %s", attr, payload)
|
||||||
|
|
||||||
self.async_write_ha_state()
|
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
@log_messages(self.hass, self.entity_id)
|
@log_messages(self.hass, self.entity_id)
|
||||||
@ -640,7 +640,7 @@ class MqttClimate(MqttEntity, ClimateEntity):
|
|||||||
preset_mode = render_template(msg, CONF_PRESET_MODE_VALUE_TEMPLATE)
|
preset_mode = render_template(msg, CONF_PRESET_MODE_VALUE_TEMPLATE)
|
||||||
if preset_mode in [PRESET_NONE, PAYLOAD_NONE]:
|
if preset_mode in [PRESET_NONE, PAYLOAD_NONE]:
|
||||||
self._preset_mode = None
|
self._preset_mode = None
|
||||||
self.async_write_ha_state()
|
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
||||||
return
|
return
|
||||||
if not preset_mode:
|
if not preset_mode:
|
||||||
_LOGGER.debug("Ignoring empty preset_mode from '%s'", msg.topic)
|
_LOGGER.debug("Ignoring empty preset_mode from '%s'", msg.topic)
|
||||||
@ -654,7 +654,7 @@ class MqttClimate(MqttEntity, ClimateEntity):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self._preset_mode = preset_mode
|
self._preset_mode = preset_mode
|
||||||
self.async_write_ha_state()
|
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
||||||
|
|
||||||
add_subscription(
|
add_subscription(
|
||||||
topics, CONF_PRESET_MODE_STATE_TOPIC, handle_preset_mode_received
|
topics, CONF_PRESET_MODE_STATE_TOPIC, handle_preset_mode_received
|
||||||
|
@ -51,7 +51,7 @@ from .mixins import (
|
|||||||
warn_for_legacy_schema,
|
warn_for_legacy_schema,
|
||||||
)
|
)
|
||||||
from .models import MqttCommandTemplate, MqttValueTemplate
|
from .models import MqttCommandTemplate, MqttValueTemplate
|
||||||
from .util import valid_publish_topic, valid_subscribe_topic
|
from .util import get_mqtt_data, valid_publish_topic, valid_subscribe_topic
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -405,7 +405,7 @@ class MqttCover(MqttEntity, CoverEntity):
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
self.async_write_ha_state()
|
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
@log_messages(self.hass, self.entity_id)
|
@log_messages(self.hass, self.entity_id)
|
||||||
@ -451,7 +451,7 @@ class MqttCover(MqttEntity, CoverEntity):
|
|||||||
else STATE_OPEN
|
else STATE_OPEN
|
||||||
)
|
)
|
||||||
|
|
||||||
self.async_write_ha_state()
|
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
||||||
|
|
||||||
if self._config.get(CONF_GET_POSITION_TOPIC):
|
if self._config.get(CONF_GET_POSITION_TOPIC):
|
||||||
topics["get_position_topic"] = {
|
topics["get_position_topic"] = {
|
||||||
|
@ -29,6 +29,7 @@ from ..const import CONF_QOS, CONF_STATE_TOPIC
|
|||||||
from ..debug_info import log_messages
|
from ..debug_info import log_messages
|
||||||
from ..mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, async_setup_entry_helper
|
from ..mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, async_setup_entry_helper
|
||||||
from ..models import MqttValueTemplate
|
from ..models import MqttValueTemplate
|
||||||
|
from ..util import get_mqtt_data
|
||||||
|
|
||||||
CONF_PAYLOAD_HOME = "payload_home"
|
CONF_PAYLOAD_HOME = "payload_home"
|
||||||
CONF_PAYLOAD_NOT_HOME = "payload_not_home"
|
CONF_PAYLOAD_NOT_HOME = "payload_not_home"
|
||||||
@ -106,7 +107,7 @@ class MqttDeviceTracker(MqttEntity, TrackerEntity):
|
|||||||
else:
|
else:
|
||||||
self._location_name = msg.payload
|
self._location_name = msg.payload
|
||||||
|
|
||||||
self.async_write_ha_state()
|
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
||||||
|
|
||||||
self._sub_state = subscription.async_prepare_subscribe_topics(
|
self._sub_state = subscription.async_prepare_subscribe_topics(
|
||||||
self.hass,
|
self.hass,
|
||||||
|
@ -55,7 +55,7 @@ from .mixins import (
|
|||||||
warn_for_legacy_schema,
|
warn_for_legacy_schema,
|
||||||
)
|
)
|
||||||
from .models import MqttCommandTemplate, MqttValueTemplate
|
from .models import MqttCommandTemplate, MqttValueTemplate
|
||||||
from .util import valid_publish_topic, valid_subscribe_topic
|
from .util import get_mqtt_data, valid_publish_topic, valid_subscribe_topic
|
||||||
|
|
||||||
CONF_PERCENTAGE_STATE_TOPIC = "percentage_state_topic"
|
CONF_PERCENTAGE_STATE_TOPIC = "percentage_state_topic"
|
||||||
CONF_PERCENTAGE_COMMAND_TOPIC = "percentage_command_topic"
|
CONF_PERCENTAGE_COMMAND_TOPIC = "percentage_command_topic"
|
||||||
@ -391,7 +391,7 @@ class MqttFan(MqttEntity, FanEntity):
|
|||||||
self._state = False
|
self._state = False
|
||||||
elif payload == PAYLOAD_NONE:
|
elif payload == PAYLOAD_NONE:
|
||||||
self._state = None
|
self._state = None
|
||||||
self.async_write_ha_state()
|
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
||||||
|
|
||||||
if self._topic[CONF_STATE_TOPIC] is not None:
|
if self._topic[CONF_STATE_TOPIC] is not None:
|
||||||
topics[CONF_STATE_TOPIC] = {
|
topics[CONF_STATE_TOPIC] = {
|
||||||
@ -413,7 +413,7 @@ class MqttFan(MqttEntity, FanEntity):
|
|||||||
return
|
return
|
||||||
if rendered_percentage_payload == self._payload["PERCENTAGE_RESET"]:
|
if rendered_percentage_payload == self._payload["PERCENTAGE_RESET"]:
|
||||||
self._percentage = None
|
self._percentage = None
|
||||||
self.async_write_ha_state()
|
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
percentage = ranged_value_to_percentage(
|
percentage = ranged_value_to_percentage(
|
||||||
@ -436,7 +436,7 @@ class MqttFan(MqttEntity, FanEntity):
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
self._percentage = percentage
|
self._percentage = percentage
|
||||||
self.async_write_ha_state()
|
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
||||||
|
|
||||||
if self._topic[CONF_PERCENTAGE_STATE_TOPIC] is not None:
|
if self._topic[CONF_PERCENTAGE_STATE_TOPIC] is not None:
|
||||||
topics[CONF_PERCENTAGE_STATE_TOPIC] = {
|
topics[CONF_PERCENTAGE_STATE_TOPIC] = {
|
||||||
@ -469,7 +469,7 @@ class MqttFan(MqttEntity, FanEntity):
|
|||||||
return
|
return
|
||||||
|
|
||||||
self._preset_mode = preset_mode
|
self._preset_mode = preset_mode
|
||||||
self.async_write_ha_state()
|
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
||||||
|
|
||||||
if self._topic[CONF_PRESET_MODE_STATE_TOPIC] is not None:
|
if self._topic[CONF_PRESET_MODE_STATE_TOPIC] is not None:
|
||||||
topics[CONF_PRESET_MODE_STATE_TOPIC] = {
|
topics[CONF_PRESET_MODE_STATE_TOPIC] = {
|
||||||
@ -492,7 +492,7 @@ class MqttFan(MqttEntity, FanEntity):
|
|||||||
self._oscillation = True
|
self._oscillation = True
|
||||||
elif payload == self._payload["OSCILLATE_OFF_PAYLOAD"]:
|
elif payload == self._payload["OSCILLATE_OFF_PAYLOAD"]:
|
||||||
self._oscillation = False
|
self._oscillation = False
|
||||||
self.async_write_ha_state()
|
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
||||||
|
|
||||||
if self._topic[CONF_OSCILLATION_STATE_TOPIC] is not None:
|
if self._topic[CONF_OSCILLATION_STATE_TOPIC] is not None:
|
||||||
topics[CONF_OSCILLATION_STATE_TOPIC] = {
|
topics[CONF_OSCILLATION_STATE_TOPIC] = {
|
||||||
|
@ -51,7 +51,7 @@ from .mixins import (
|
|||||||
warn_for_legacy_schema,
|
warn_for_legacy_schema,
|
||||||
)
|
)
|
||||||
from .models import MqttCommandTemplate, MqttValueTemplate
|
from .models import MqttCommandTemplate, MqttValueTemplate
|
||||||
from .util import 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"
|
||||||
CONF_DEVICE_CLASS = "device_class"
|
CONF_DEVICE_CLASS = "device_class"
|
||||||
@ -309,7 +309,7 @@ class MqttHumidifier(MqttEntity, HumidifierEntity):
|
|||||||
self._state = False
|
self._state = False
|
||||||
elif payload == PAYLOAD_NONE:
|
elif payload == PAYLOAD_NONE:
|
||||||
self._state = None
|
self._state = None
|
||||||
self.async_write_ha_state()
|
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
||||||
|
|
||||||
if self._topic[CONF_STATE_TOPIC] is not None:
|
if self._topic[CONF_STATE_TOPIC] is not None:
|
||||||
topics[CONF_STATE_TOPIC] = {
|
topics[CONF_STATE_TOPIC] = {
|
||||||
@ -331,7 +331,7 @@ class MqttHumidifier(MqttEntity, HumidifierEntity):
|
|||||||
return
|
return
|
||||||
if rendered_target_humidity_payload == self._payload["HUMIDITY_RESET"]:
|
if rendered_target_humidity_payload == self._payload["HUMIDITY_RESET"]:
|
||||||
self._target_humidity = None
|
self._target_humidity = None
|
||||||
self.async_write_ha_state()
|
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
target_humidity = round(float(rendered_target_humidity_payload))
|
target_humidity = round(float(rendered_target_humidity_payload))
|
||||||
@ -355,7 +355,7 @@ class MqttHumidifier(MqttEntity, HumidifierEntity):
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
self._target_humidity = target_humidity
|
self._target_humidity = target_humidity
|
||||||
self.async_write_ha_state()
|
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
||||||
|
|
||||||
if self._topic[CONF_TARGET_HUMIDITY_STATE_TOPIC] is not None:
|
if self._topic[CONF_TARGET_HUMIDITY_STATE_TOPIC] is not None:
|
||||||
topics[CONF_TARGET_HUMIDITY_STATE_TOPIC] = {
|
topics[CONF_TARGET_HUMIDITY_STATE_TOPIC] = {
|
||||||
@ -373,7 +373,7 @@ class MqttHumidifier(MqttEntity, HumidifierEntity):
|
|||||||
mode = self._value_templates[ATTR_MODE](msg.payload)
|
mode = self._value_templates[ATTR_MODE](msg.payload)
|
||||||
if mode == self._payload["MODE_RESET"]:
|
if mode == self._payload["MODE_RESET"]:
|
||||||
self._mode = None
|
self._mode = None
|
||||||
self.async_write_ha_state()
|
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
||||||
return
|
return
|
||||||
if not mode:
|
if not mode:
|
||||||
_LOGGER.debug("Ignoring empty mode from '%s'", msg.topic)
|
_LOGGER.debug("Ignoring empty mode from '%s'", msg.topic)
|
||||||
@ -388,7 +388,7 @@ class MqttHumidifier(MqttEntity, HumidifierEntity):
|
|||||||
return
|
return
|
||||||
|
|
||||||
self._mode = mode
|
self._mode = mode
|
||||||
self.async_write_ha_state()
|
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
||||||
|
|
||||||
if self._topic[CONF_MODE_STATE_TOPIC] is not None:
|
if self._topic[CONF_MODE_STATE_TOPIC] is not None:
|
||||||
topics[CONF_MODE_STATE_TOPIC] = {
|
topics[CONF_MODE_STATE_TOPIC] = {
|
||||||
|
@ -51,7 +51,7 @@ from ..const import (
|
|||||||
from ..debug_info import log_messages
|
from ..debug_info import log_messages
|
||||||
from ..mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity
|
from ..mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity
|
||||||
from ..models import MqttCommandTemplate, MqttValueTemplate
|
from ..models import MqttCommandTemplate, MqttValueTemplate
|
||||||
from ..util import valid_publish_topic, valid_subscribe_topic
|
from ..util import get_mqtt_data, valid_publish_topic, valid_subscribe_topic
|
||||||
from .schema import MQTT_LIGHT_SCHEMA_SCHEMA
|
from .schema import MQTT_LIGHT_SCHEMA_SCHEMA
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
@ -438,7 +438,7 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity):
|
|||||||
self._state = False
|
self._state = False
|
||||||
elif payload == PAYLOAD_NONE:
|
elif payload == PAYLOAD_NONE:
|
||||||
self._state = None
|
self._state = None
|
||||||
self.async_write_ha_state()
|
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
||||||
|
|
||||||
if self._topic[CONF_STATE_TOPIC] is not None:
|
if self._topic[CONF_STATE_TOPIC] is not None:
|
||||||
topics[CONF_STATE_TOPIC] = {
|
topics[CONF_STATE_TOPIC] = {
|
||||||
@ -462,7 +462,7 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity):
|
|||||||
device_value = float(payload)
|
device_value = float(payload)
|
||||||
percent_bright = device_value / self._config[CONF_BRIGHTNESS_SCALE]
|
percent_bright = device_value / self._config[CONF_BRIGHTNESS_SCALE]
|
||||||
self._brightness = percent_bright * 255
|
self._brightness = percent_bright * 255
|
||||||
self.async_write_ha_state()
|
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
||||||
|
|
||||||
add_topic(CONF_BRIGHTNESS_STATE_TOPIC, brightness_received)
|
add_topic(CONF_BRIGHTNESS_STATE_TOPIC, brightness_received)
|
||||||
|
|
||||||
@ -493,7 +493,7 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity):
|
|||||||
if not rgb:
|
if not rgb:
|
||||||
return
|
return
|
||||||
self._rgb_color = rgb
|
self._rgb_color = rgb
|
||||||
self.async_write_ha_state()
|
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
||||||
|
|
||||||
add_topic(CONF_RGB_STATE_TOPIC, rgb_received)
|
add_topic(CONF_RGB_STATE_TOPIC, rgb_received)
|
||||||
|
|
||||||
@ -510,7 +510,7 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity):
|
|||||||
if not rgbw:
|
if not rgbw:
|
||||||
return
|
return
|
||||||
self._rgbw_color = rgbw
|
self._rgbw_color = rgbw
|
||||||
self.async_write_ha_state()
|
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
||||||
|
|
||||||
add_topic(CONF_RGBW_STATE_TOPIC, rgbw_received)
|
add_topic(CONF_RGBW_STATE_TOPIC, rgbw_received)
|
||||||
|
|
||||||
@ -527,7 +527,7 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity):
|
|||||||
if not rgbww:
|
if not rgbww:
|
||||||
return
|
return
|
||||||
self._rgbww_color = rgbww
|
self._rgbww_color = rgbww
|
||||||
self.async_write_ha_state()
|
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
||||||
|
|
||||||
add_topic(CONF_RGBWW_STATE_TOPIC, rgbww_received)
|
add_topic(CONF_RGBWW_STATE_TOPIC, rgbww_received)
|
||||||
|
|
||||||
@ -543,7 +543,7 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity):
|
|||||||
return
|
return
|
||||||
|
|
||||||
self._color_mode = payload
|
self._color_mode = payload
|
||||||
self.async_write_ha_state()
|
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
||||||
|
|
||||||
add_topic(CONF_COLOR_MODE_STATE_TOPIC, color_mode_received)
|
add_topic(CONF_COLOR_MODE_STATE_TOPIC, color_mode_received)
|
||||||
|
|
||||||
@ -561,7 +561,7 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity):
|
|||||||
if self._optimistic_color_mode:
|
if self._optimistic_color_mode:
|
||||||
self._color_mode = ColorMode.COLOR_TEMP
|
self._color_mode = ColorMode.COLOR_TEMP
|
||||||
self._color_temp = int(payload)
|
self._color_temp = int(payload)
|
||||||
self.async_write_ha_state()
|
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
||||||
|
|
||||||
add_topic(CONF_COLOR_TEMP_STATE_TOPIC, color_temp_received)
|
add_topic(CONF_COLOR_TEMP_STATE_TOPIC, color_temp_received)
|
||||||
|
|
||||||
@ -577,7 +577,7 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity):
|
|||||||
return
|
return
|
||||||
|
|
||||||
self._effect = payload
|
self._effect = payload
|
||||||
self.async_write_ha_state()
|
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
||||||
|
|
||||||
add_topic(CONF_EFFECT_STATE_TOPIC, effect_received)
|
add_topic(CONF_EFFECT_STATE_TOPIC, effect_received)
|
||||||
|
|
||||||
@ -594,7 +594,7 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity):
|
|||||||
if self._optimistic_color_mode:
|
if self._optimistic_color_mode:
|
||||||
self._color_mode = ColorMode.HS
|
self._color_mode = ColorMode.HS
|
||||||
self._hs_color = hs_color
|
self._hs_color = hs_color
|
||||||
self.async_write_ha_state()
|
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
_LOGGER.debug("Failed to parse hs state update: '%s'", payload)
|
_LOGGER.debug("Failed to parse hs state update: '%s'", payload)
|
||||||
|
|
||||||
@ -613,7 +613,7 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity):
|
|||||||
if self._optimistic_color_mode:
|
if self._optimistic_color_mode:
|
||||||
self._color_mode = ColorMode.XY
|
self._color_mode = ColorMode.XY
|
||||||
self._xy_color = xy_color
|
self._xy_color = xy_color
|
||||||
self.async_write_ha_state()
|
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
||||||
|
|
||||||
add_topic(CONF_XY_STATE_TOPIC, xy_received)
|
add_topic(CONF_XY_STATE_TOPIC, xy_received)
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ from ..const import (
|
|||||||
)
|
)
|
||||||
from ..debug_info import log_messages
|
from ..debug_info import log_messages
|
||||||
from ..mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity
|
from ..mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity
|
||||||
from ..util import valid_subscribe_topic
|
from ..util import get_mqtt_data, valid_subscribe_topic
|
||||||
from .schema import MQTT_LIGHT_SCHEMA_SCHEMA
|
from .schema import MQTT_LIGHT_SCHEMA_SCHEMA
|
||||||
from .schema_basic import (
|
from .schema_basic import (
|
||||||
CONF_BRIGHTNESS_SCALE,
|
CONF_BRIGHTNESS_SCALE,
|
||||||
@ -401,7 +401,7 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity):
|
|||||||
with suppress(KeyError):
|
with suppress(KeyError):
|
||||||
self._effect = values["effect"]
|
self._effect = values["effect"]
|
||||||
|
|
||||||
self.async_write_ha_state()
|
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
||||||
|
|
||||||
if self._topic[CONF_STATE_TOPIC] is not None:
|
if self._topic[CONF_STATE_TOPIC] is not None:
|
||||||
self._sub_state = subscription.async_prepare_subscribe_topics(
|
self._sub_state = subscription.async_prepare_subscribe_topics(
|
||||||
|
@ -41,6 +41,7 @@ from ..const import (
|
|||||||
from ..debug_info import log_messages
|
from ..debug_info import log_messages
|
||||||
from ..mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity
|
from ..mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity
|
||||||
from ..models import MqttValueTemplate
|
from ..models import MqttValueTemplate
|
||||||
|
from ..util import get_mqtt_data
|
||||||
from .schema import MQTT_LIGHT_SCHEMA_SCHEMA
|
from .schema import MQTT_LIGHT_SCHEMA_SCHEMA
|
||||||
from .schema_basic import MQTT_LIGHT_ATTRIBUTES_BLOCKED
|
from .schema_basic import MQTT_LIGHT_ATTRIBUTES_BLOCKED
|
||||||
|
|
||||||
@ -256,7 +257,7 @@ class MqttLightTemplate(MqttEntity, LightEntity, RestoreEntity):
|
|||||||
else:
|
else:
|
||||||
_LOGGER.warning("Unsupported effect value received")
|
_LOGGER.warning("Unsupported effect value received")
|
||||||
|
|
||||||
self.async_write_ha_state()
|
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
||||||
|
|
||||||
if self._topics[CONF_STATE_TOPIC] is not None:
|
if self._topics[CONF_STATE_TOPIC] is not None:
|
||||||
self._sub_state = subscription.async_prepare_subscribe_topics(
|
self._sub_state = subscription.async_prepare_subscribe_topics(
|
||||||
|
@ -33,6 +33,7 @@ from .mixins import (
|
|||||||
warn_for_legacy_schema,
|
warn_for_legacy_schema,
|
||||||
)
|
)
|
||||||
from .models import MqttValueTemplate
|
from .models import MqttValueTemplate
|
||||||
|
from .util import get_mqtt_data
|
||||||
|
|
||||||
CONF_PAYLOAD_LOCK = "payload_lock"
|
CONF_PAYLOAD_LOCK = "payload_lock"
|
||||||
CONF_PAYLOAD_UNLOCK = "payload_unlock"
|
CONF_PAYLOAD_UNLOCK = "payload_unlock"
|
||||||
@ -158,7 +159,7 @@ class MqttLock(MqttEntity, LockEntity):
|
|||||||
elif payload == self._config[CONF_STATE_UNLOCKED]:
|
elif payload == self._config[CONF_STATE_UNLOCKED]:
|
||||||
self._state = False
|
self._state = False
|
||||||
|
|
||||||
self.async_write_ha_state()
|
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
||||||
|
|
||||||
if self._config.get(CONF_STATE_TOPIC) is None:
|
if self._config.get(CONF_STATE_TOPIC) is None:
|
||||||
# Force into optimistic mode.
|
# Force into optimistic mode.
|
||||||
|
@ -435,7 +435,9 @@ class MqttAttributes(Entity):
|
|||||||
and k not in self._attributes_extra_blocked
|
and k not in self._attributes_extra_blocked
|
||||||
}
|
}
|
||||||
self._attributes = filtered_dict
|
self._attributes = filtered_dict
|
||||||
self.async_write_ha_state()
|
get_mqtt_data(self.hass).state_write_requests.write_state_request(
|
||||||
|
self
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
_LOGGER.warning("JSON result was not a dictionary")
|
_LOGGER.warning("JSON result was not a dictionary")
|
||||||
self._attributes = None
|
self._attributes = None
|
||||||
@ -547,7 +549,7 @@ class MqttAvailability(Entity):
|
|||||||
self._available[topic] = False
|
self._available[topic] = False
|
||||||
self._available_latest = False
|
self._available_latest = False
|
||||||
|
|
||||||
self.async_write_ha_state()
|
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
||||||
|
|
||||||
self._available = {
|
self._available = {
|
||||||
topic: (self._available[topic] if topic in self._available else False)
|
topic: (self._available[topic] if topic in self._available else False)
|
||||||
|
@ -236,6 +236,26 @@ class MqttValueTemplate:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class EntityTopicState:
|
||||||
|
"""Manage entity state write requests for subscribed topics."""
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
"""Register topic."""
|
||||||
|
self.subscribe_calls: dict[str, Entity] = {}
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def process_write_state_requests(self) -> None:
|
||||||
|
"""Process the write state requests."""
|
||||||
|
while self.subscribe_calls:
|
||||||
|
_, entity = self.subscribe_calls.popitem()
|
||||||
|
entity.async_write_ha_state()
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def write_state_request(self, entity: Entity) -> None:
|
||||||
|
"""Register write state request."""
|
||||||
|
self.subscribe_calls[entity.entity_id] = entity
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class MqttData:
|
class MqttData:
|
||||||
"""Keep the MQTT entry data."""
|
"""Keep the MQTT entry data."""
|
||||||
@ -264,6 +284,7 @@ class MqttData:
|
|||||||
default_factory=dict
|
default_factory=dict
|
||||||
)
|
)
|
||||||
reload_needed: bool = False
|
reload_needed: bool = False
|
||||||
|
state_write_requests: EntityTopicState = field(default_factory=EntityTopicState)
|
||||||
subscriptions_to_restore: list[Subscription] = field(default_factory=list)
|
subscriptions_to_restore: list[Subscription] = field(default_factory=list)
|
||||||
tags: dict[str, dict[str, MQTTTagScanner]] = field(default_factory=dict)
|
tags: dict[str, dict[str, MQTTTagScanner]] = field(default_factory=dict)
|
||||||
updated_config: ConfigType = field(default_factory=dict)
|
updated_config: ConfigType = field(default_factory=dict)
|
||||||
|
@ -49,6 +49,7 @@ from .mixins import (
|
|||||||
warn_for_legacy_schema,
|
warn_for_legacy_schema,
|
||||||
)
|
)
|
||||||
from .models import MqttCommandTemplate, MqttValueTemplate
|
from .models import MqttCommandTemplate, MqttValueTemplate
|
||||||
|
from .util import get_mqtt_data
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -222,7 +223,7 @@ class MqttNumber(MqttEntity, RestoreNumber):
|
|||||||
return
|
return
|
||||||
|
|
||||||
self._current_number = num_value
|
self._current_number = num_value
|
||||||
self.async_write_ha_state()
|
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
||||||
|
|
||||||
if self._config.get(CONF_STATE_TOPIC) is None:
|
if self._config.get(CONF_STATE_TOPIC) is None:
|
||||||
# Force into optimistic mode.
|
# Force into optimistic mode.
|
||||||
|
@ -35,6 +35,7 @@ from .mixins import (
|
|||||||
warn_for_legacy_schema,
|
warn_for_legacy_schema,
|
||||||
)
|
)
|
||||||
from .models import MqttCommandTemplate, MqttValueTemplate
|
from .models import MqttCommandTemplate, MqttValueTemplate
|
||||||
|
from .util import get_mqtt_data
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -169,7 +170,7 @@ class MqttSelect(MqttEntity, SelectEntity, RestoreEntity):
|
|||||||
return
|
return
|
||||||
|
|
||||||
self._attr_current_option = payload
|
self._attr_current_option = payload
|
||||||
self.async_write_ha_state()
|
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
||||||
|
|
||||||
if self._config.get(CONF_STATE_TOPIC) is None:
|
if self._config.get(CONF_STATE_TOPIC) is None:
|
||||||
# Force into optimistic mode.
|
# Force into optimistic mode.
|
||||||
|
@ -46,7 +46,7 @@ from .mixins import (
|
|||||||
warn_for_legacy_schema,
|
warn_for_legacy_schema,
|
||||||
)
|
)
|
||||||
from .models import MqttValueTemplate
|
from .models import MqttValueTemplate
|
||||||
from .util import valid_subscribe_topic
|
from .util import get_mqtt_data, valid_subscribe_topic
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -300,7 +300,7 @@ class MqttSensor(MqttEntity, RestoreSensor):
|
|||||||
or self._config[CONF_LAST_RESET_TOPIC] == self._config[CONF_STATE_TOPIC]
|
or self._config[CONF_LAST_RESET_TOPIC] == self._config[CONF_STATE_TOPIC]
|
||||||
):
|
):
|
||||||
_update_last_reset(msg)
|
_update_last_reset(msg)
|
||||||
self.async_write_ha_state()
|
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
||||||
|
|
||||||
topics["state_topic"] = {
|
topics["state_topic"] = {
|
||||||
"topic": self._config[CONF_STATE_TOPIC],
|
"topic": self._config[CONF_STATE_TOPIC],
|
||||||
@ -314,7 +314,7 @@ class MqttSensor(MqttEntity, RestoreSensor):
|
|||||||
def last_reset_message_received(msg):
|
def last_reset_message_received(msg):
|
||||||
"""Handle new last_reset messages."""
|
"""Handle new last_reset messages."""
|
||||||
_update_last_reset(msg)
|
_update_last_reset(msg)
|
||||||
self.async_write_ha_state()
|
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
CONF_LAST_RESET_TOPIC in self._config
|
CONF_LAST_RESET_TOPIC in self._config
|
||||||
|
@ -54,6 +54,7 @@ from .mixins import (
|
|||||||
warn_for_legacy_schema,
|
warn_for_legacy_schema,
|
||||||
)
|
)
|
||||||
from .models import MqttCommandTemplate, MqttValueTemplate
|
from .models import MqttCommandTemplate, MqttValueTemplate
|
||||||
|
from .util import get_mqtt_data
|
||||||
|
|
||||||
DEFAULT_NAME = "MQTT Siren"
|
DEFAULT_NAME = "MQTT Siren"
|
||||||
DEFAULT_PAYLOAD_ON = "ON"
|
DEFAULT_PAYLOAD_ON = "ON"
|
||||||
@ -283,7 +284,7 @@ class MqttSiren(MqttEntity, SirenEntity):
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
self._update(process_turn_on_params(self, json_payload))
|
self._update(process_turn_on_params(self, json_payload))
|
||||||
self.async_write_ha_state()
|
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
||||||
|
|
||||||
if self._config.get(CONF_STATE_TOPIC) is None:
|
if self._config.get(CONF_STATE_TOPIC) is None:
|
||||||
# Force into optimistic mode.
|
# Force into optimistic mode.
|
||||||
|
@ -26,9 +26,12 @@ class EntitySubscription:
|
|||||||
qos: int = attr.ib(default=0)
|
qos: int = attr.ib(default=0)
|
||||||
encoding: str = attr.ib(default="utf-8")
|
encoding: str = attr.ib(default="utf-8")
|
||||||
|
|
||||||
def resubscribe_if_necessary(self, hass, other):
|
def resubscribe_if_necessary(
|
||||||
|
self, hass: HomeAssistant, other: EntitySubscription | None
|
||||||
|
) -> None:
|
||||||
"""Re-subscribe to the new topic if necessary."""
|
"""Re-subscribe to the new topic if necessary."""
|
||||||
if not self._should_resubscribe(other):
|
if not self._should_resubscribe(other):
|
||||||
|
assert other
|
||||||
self.unsubscribe_callback = other.unsubscribe_callback
|
self.unsubscribe_callback = other.unsubscribe_callback
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -56,7 +59,7 @@ class EntitySubscription:
|
|||||||
return
|
return
|
||||||
self.unsubscribe_callback = await self.subscribe_task
|
self.unsubscribe_callback = await self.subscribe_task
|
||||||
|
|
||||||
def _should_resubscribe(self, other):
|
def _should_resubscribe(self, other: EntitySubscription | None) -> bool:
|
||||||
"""Check if we should re-subscribe to the topic using the old state."""
|
"""Check if we should re-subscribe to the topic using the old state."""
|
||||||
if other is None:
|
if other is None:
|
||||||
return True
|
return True
|
||||||
|
@ -47,6 +47,7 @@ from .mixins import (
|
|||||||
warn_for_legacy_schema,
|
warn_for_legacy_schema,
|
||||||
)
|
)
|
||||||
from .models import MqttValueTemplate
|
from .models import MqttValueTemplate
|
||||||
|
from .util import get_mqtt_data
|
||||||
|
|
||||||
DEFAULT_NAME = "MQTT Switch"
|
DEFAULT_NAME = "MQTT Switch"
|
||||||
DEFAULT_PAYLOAD_ON = "ON"
|
DEFAULT_PAYLOAD_ON = "ON"
|
||||||
@ -168,7 +169,7 @@ class MqttSwitch(MqttEntity, SwitchEntity, RestoreEntity):
|
|||||||
elif payload == PAYLOAD_NONE:
|
elif payload == PAYLOAD_NONE:
|
||||||
self._state = None
|
self._state = None
|
||||||
|
|
||||||
self.async_write_ha_state()
|
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
||||||
|
|
||||||
if self._config.get(CONF_STATE_TOPIC) is None:
|
if self._config.get(CONF_STATE_TOPIC) is None:
|
||||||
# Force into optimistic mode.
|
# Force into optimistic mode.
|
||||||
|
@ -20,7 +20,7 @@ from ..const import CONF_COMMAND_TOPIC, CONF_ENCODING, CONF_QOS, CONF_RETAIN
|
|||||||
from ..debug_info import log_messages
|
from ..debug_info import log_messages
|
||||||
from ..mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, warn_for_legacy_schema
|
from ..mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, warn_for_legacy_schema
|
||||||
from ..models import MqttValueTemplate
|
from ..models import MqttValueTemplate
|
||||||
from ..util import valid_publish_topic
|
from ..util import get_mqtt_data, valid_publish_topic
|
||||||
from .const import MQTT_VACUUM_ATTRIBUTES_BLOCKED
|
from .const import MQTT_VACUUM_ATTRIBUTES_BLOCKED
|
||||||
from .schema import MQTT_VACUUM_SCHEMA, services_to_strings, strings_to_services
|
from .schema import MQTT_VACUUM_SCHEMA, services_to_strings, strings_to_services
|
||||||
|
|
||||||
@ -320,7 +320,7 @@ class MqttVacuum(MqttEntity, VacuumEntity):
|
|||||||
if fan_speed:
|
if fan_speed:
|
||||||
self._fan_speed = fan_speed
|
self._fan_speed = fan_speed
|
||||||
|
|
||||||
self.async_write_ha_state()
|
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
||||||
|
|
||||||
topics_list = {topic for topic in self._state_topics.values() if topic}
|
topics_list = {topic for topic in self._state_topics.values() if topic}
|
||||||
self._sub_state = subscription.async_prepare_subscribe_topics(
|
self._sub_state = subscription.async_prepare_subscribe_topics(
|
||||||
|
@ -32,7 +32,7 @@ from ..const import (
|
|||||||
)
|
)
|
||||||
from ..debug_info import log_messages
|
from ..debug_info import log_messages
|
||||||
from ..mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, warn_for_legacy_schema
|
from ..mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, warn_for_legacy_schema
|
||||||
from ..util import valid_publish_topic
|
from ..util import get_mqtt_data, valid_publish_topic
|
||||||
from .const import MQTT_VACUUM_ATTRIBUTES_BLOCKED
|
from .const import MQTT_VACUUM_ATTRIBUTES_BLOCKED
|
||||||
from .schema import MQTT_VACUUM_SCHEMA, services_to_strings, strings_to_services
|
from .schema import MQTT_VACUUM_SCHEMA, services_to_strings, strings_to_services
|
||||||
|
|
||||||
@ -211,7 +211,7 @@ class MqttStateVacuum(MqttEntity, StateVacuumEntity):
|
|||||||
)
|
)
|
||||||
del payload[STATE]
|
del payload[STATE]
|
||||||
self._state_attrs.update(payload)
|
self._state_attrs.update(payload)
|
||||||
self.async_write_ha_state()
|
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
||||||
|
|
||||||
if self._config.get(CONF_STATE_TOPIC):
|
if self._config.get(CONF_STATE_TOPIC):
|
||||||
topics["state_position_topic"] = {
|
topics["state_position_topic"] = {
|
||||||
|
@ -206,7 +206,7 @@ class NexiaZone(NexiaThermostatZoneEntity, ClimateEntity):
|
|||||||
"""Set the hvac run mode."""
|
"""Set the hvac run mode."""
|
||||||
if run_mode is not None:
|
if run_mode is not None:
|
||||||
if run_mode == HOLD_PERMANENT:
|
if run_mode == HOLD_PERMANENT:
|
||||||
await self._zone.call_permanent_hold()
|
await self._zone.set_permanent_hold()
|
||||||
else:
|
else:
|
||||||
await self._zone.call_return_to_schedule()
|
await self._zone.call_return_to_schedule()
|
||||||
if hvac_mode is not None:
|
if hvac_mode is not None:
|
||||||
@ -399,7 +399,7 @@ class NexiaZone(NexiaThermostatZoneEntity, ClimateEntity):
|
|||||||
await self._zone.call_return_to_schedule()
|
await self._zone.call_return_to_schedule()
|
||||||
await self._zone.set_mode(mode=OPERATION_MODE_AUTO)
|
await self._zone.set_mode(mode=OPERATION_MODE_AUTO)
|
||||||
else:
|
else:
|
||||||
await self._zone.call_permanent_hold()
|
await self._zone.set_permanent_hold()
|
||||||
await self._zone.set_mode(mode=HA_TO_NEXIA_HVAC_MODE_MAP[hvac_mode])
|
await self._zone.set_mode(mode=HA_TO_NEXIA_HVAC_MODE_MAP[hvac_mode])
|
||||||
|
|
||||||
self._signal_zone_update()
|
self._signal_zone_update()
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"domain": "nexia",
|
"domain": "nexia",
|
||||||
"name": "Nexia/American Standard/Trane",
|
"name": "Nexia/American Standard/Trane",
|
||||||
"requirements": ["nexia==2.0.2"],
|
"requirements": ["nexia==2.0.4"],
|
||||||
"codeowners": ["@bdraco"],
|
"codeowners": ["@bdraco"],
|
||||||
"documentation": "https://www.home-assistant.io/integrations/nexia",
|
"documentation": "https://www.home-assistant.io/integrations/nexia",
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
|
@ -62,7 +62,7 @@ class NexiaHoldSwitch(NexiaThermostatZoneEntity, SwitchEntity):
|
|||||||
if self._zone.get_current_mode() == OPERATION_MODE_OFF:
|
if self._zone.get_current_mode() == OPERATION_MODE_OFF:
|
||||||
await self._zone.call_permanent_off()
|
await self._zone.call_permanent_off()
|
||||||
else:
|
else:
|
||||||
await self._zone.call_permanent_hold()
|
await self._zone.set_permanent_hold()
|
||||||
self._signal_zone_update()
|
self._signal_zone_update()
|
||||||
|
|
||||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||||
|
@ -19,6 +19,7 @@ from homeassistant.const import (
|
|||||||
ENERGY_KILO_WATT_HOUR,
|
ENERGY_KILO_WATT_HOUR,
|
||||||
ENERGY_MEGA_WATT_HOUR,
|
ENERGY_MEGA_WATT_HOUR,
|
||||||
ENERGY_WATT_HOUR,
|
ENERGY_WATT_HOUR,
|
||||||
|
POWER_WATT,
|
||||||
TEMP_CELSIUS,
|
TEMP_CELSIUS,
|
||||||
TEMP_FAHRENHEIT,
|
TEMP_FAHRENHEIT,
|
||||||
TIME_HOURS,
|
TIME_HOURS,
|
||||||
@ -72,24 +73,31 @@ UNIT_DESCRIPTIONS = {
|
|||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
native_unit_of_measurement=ELECTRIC_POTENTIAL_MILLIVOLT,
|
native_unit_of_measurement=ELECTRIC_POTENTIAL_MILLIVOLT,
|
||||||
),
|
),
|
||||||
|
"W": SensorEntityDescription(
|
||||||
|
key="W",
|
||||||
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
|
device_class=SensorDeviceClass.POWER,
|
||||||
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
|
native_unit_of_measurement=POWER_WATT,
|
||||||
|
),
|
||||||
"Wh": SensorEntityDescription(
|
"Wh": SensorEntityDescription(
|
||||||
key="Wh",
|
key="Wh",
|
||||||
entity_category=EntityCategory.DIAGNOSTIC,
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
device_class=SensorDeviceClass.POWER,
|
device_class=SensorDeviceClass.ENERGY,
|
||||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||||
native_unit_of_measurement=ENERGY_WATT_HOUR,
|
native_unit_of_measurement=ENERGY_WATT_HOUR,
|
||||||
),
|
),
|
||||||
"kWh": SensorEntityDescription(
|
"kWh": SensorEntityDescription(
|
||||||
key="kWh",
|
key="kWh",
|
||||||
entity_category=EntityCategory.DIAGNOSTIC,
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
device_class=SensorDeviceClass.POWER,
|
device_class=SensorDeviceClass.ENERGY,
|
||||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||||
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
|
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
|
||||||
),
|
),
|
||||||
"MWh": SensorEntityDescription(
|
"MWh": SensorEntityDescription(
|
||||||
key="MWh",
|
key="MWh",
|
||||||
entity_category=EntityCategory.DIAGNOSTIC,
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
device_class=SensorDeviceClass.POWER,
|
device_class=SensorDeviceClass.ENERGY,
|
||||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||||
native_unit_of_measurement=ENERGY_MEGA_WATT_HOUR,
|
native_unit_of_measurement=ENERGY_MEGA_WATT_HOUR,
|
||||||
),
|
),
|
||||||
@ -97,6 +105,7 @@ UNIT_DESCRIPTIONS = {
|
|||||||
key="h",
|
key="h",
|
||||||
entity_category=EntityCategory.DIAGNOSTIC,
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
device_class=SensorDeviceClass.DURATION,
|
device_class=SensorDeviceClass.DURATION,
|
||||||
|
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||||
native_unit_of_measurement=TIME_HOURS,
|
native_unit_of_measurement=TIME_HOURS,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
@ -133,6 +142,7 @@ class Sensor(CoilEntity, SensorEntity):
|
|||||||
self.entity_description = entity_description
|
self.entity_description = entity_description
|
||||||
else:
|
else:
|
||||||
self._attr_native_unit_of_measurement = coil.unit
|
self._attr_native_unit_of_measurement = coil.unit
|
||||||
|
self._attr_entity_category = EntityCategory.DIAGNOSTIC
|
||||||
|
|
||||||
def _async_read_coil(self, coil: Coil):
|
def _async_read_coil(self, coil: Coil):
|
||||||
self._attr_native_value = coil.value
|
self._attr_native_value = coil.value
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
"""Python Control of Nobø Hub - Nobø Energy Control."""
|
"""Python Control of Nobø Hub - Nobø Energy Control."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import logging
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from pynobo import nobo
|
from pynobo import nobo
|
||||||
@ -24,7 +23,7 @@ from homeassistant.const import (
|
|||||||
ATTR_NAME,
|
ATTR_NAME,
|
||||||
ATTR_SUGGESTED_AREA,
|
ATTR_SUGGESTED_AREA,
|
||||||
ATTR_VIA_DEVICE,
|
ATTR_VIA_DEVICE,
|
||||||
PRECISION_WHOLE,
|
PRECISION_TENTHS,
|
||||||
TEMP_CELSIUS,
|
TEMP_CELSIUS,
|
||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
@ -52,8 +51,6 @@ PRESET_MODES = [PRESET_NONE, PRESET_COMFORT, PRESET_ECO, PRESET_AWAY]
|
|||||||
MIN_TEMPERATURE = 7
|
MIN_TEMPERATURE = 7
|
||||||
MAX_TEMPERATURE = 40
|
MAX_TEMPERATURE = 40
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
@ -87,11 +84,12 @@ class NoboZone(ClimateEntity):
|
|||||||
|
|
||||||
_attr_max_temp = MAX_TEMPERATURE
|
_attr_max_temp = MAX_TEMPERATURE
|
||||||
_attr_min_temp = MIN_TEMPERATURE
|
_attr_min_temp = MIN_TEMPERATURE
|
||||||
_attr_precision = PRECISION_WHOLE
|
_attr_precision = PRECISION_TENTHS
|
||||||
_attr_preset_modes = PRESET_MODES
|
_attr_preset_modes = PRESET_MODES
|
||||||
# Need to poll to get preset change when in HVACMode.AUTO.
|
|
||||||
_attr_supported_features = SUPPORT_FLAGS
|
_attr_supported_features = SUPPORT_FLAGS
|
||||||
_attr_temperature_unit = TEMP_CELSIUS
|
_attr_temperature_unit = TEMP_CELSIUS
|
||||||
|
_attr_target_temperature_step = 1
|
||||||
|
# Need to poll to get preset change when in HVACMode.AUTO, so can't set _attr_should_poll = False
|
||||||
|
|
||||||
def __init__(self, zone_id, hub: nobo, override_type):
|
def __init__(self, zone_id, hub: nobo, override_type):
|
||||||
"""Initialize the climate device."""
|
"""Initialize the climate device."""
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
"domain": "totalconnect",
|
"domain": "totalconnect",
|
||||||
"name": "Total Connect",
|
"name": "Total Connect",
|
||||||
"documentation": "https://www.home-assistant.io/integrations/totalconnect",
|
"documentation": "https://www.home-assistant.io/integrations/totalconnect",
|
||||||
"requirements": ["total_connect_client==2022.5"],
|
"requirements": ["total_connect_client==2022.10"],
|
||||||
"dependencies": [],
|
"dependencies": [],
|
||||||
"codeowners": ["@austinmroczek"],
|
"codeowners": ["@austinmroczek"],
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
|
@ -104,7 +104,7 @@ class TuyaHumidifierEntity(TuyaEntity, HumidifierEntity):
|
|||||||
if int_type := self.find_dpcode(
|
if int_type := self.find_dpcode(
|
||||||
description.humidity, dptype=DPType.INTEGER, prefer_function=True
|
description.humidity, dptype=DPType.INTEGER, prefer_function=True
|
||||||
):
|
):
|
||||||
self._set_humiditye = int_type
|
self._set_humidity = int_type
|
||||||
self._attr_min_humidity = int(int_type.min_scaled)
|
self._attr_min_humidity = int(int_type.min_scaled)
|
||||||
self._attr_max_humidity = int(int_type.max_scaled)
|
self._attr_max_humidity = int(int_type.max_scaled)
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ from homeassistant.helpers.entity import DeviceInfo
|
|||||||
from homeassistant.helpers.update_coordinator import (
|
from homeassistant.helpers.update_coordinator import (
|
||||||
CoordinatorEntity,
|
CoordinatorEntity,
|
||||||
DataUpdateCoordinator,
|
DataUpdateCoordinator,
|
||||||
|
UpdateFailed,
|
||||||
)
|
)
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
@ -93,6 +94,7 @@ class WallboxCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
|||||||
"""Authenticate using Wallbox API."""
|
"""Authenticate using Wallbox API."""
|
||||||
try:
|
try:
|
||||||
self._wallbox.authenticate()
|
self._wallbox.authenticate()
|
||||||
|
|
||||||
except requests.exceptions.HTTPError as wallbox_connection_error:
|
except requests.exceptions.HTTPError as wallbox_connection_error:
|
||||||
if wallbox_connection_error.response.status_code == HTTPStatus.FORBIDDEN:
|
if wallbox_connection_error.response.status_code == HTTPStatus.FORBIDDEN:
|
||||||
raise ConfigEntryAuthFailed from wallbox_connection_error
|
raise ConfigEntryAuthFailed from wallbox_connection_error
|
||||||
@ -125,11 +127,12 @@ class WallboxCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
|||||||
data[CHARGER_STATUS_DESCRIPTION_KEY] = CHARGER_STATUS.get(
|
data[CHARGER_STATUS_DESCRIPTION_KEY] = CHARGER_STATUS.get(
|
||||||
data[CHARGER_STATUS_ID_KEY], ChargerStatus.UNKNOWN
|
data[CHARGER_STATUS_ID_KEY], ChargerStatus.UNKNOWN
|
||||||
)
|
)
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
except (
|
||||||
except requests.exceptions.HTTPError as wallbox_connection_error:
|
ConnectionError,
|
||||||
raise ConnectionError from wallbox_connection_error
|
requests.exceptions.HTTPError,
|
||||||
|
) as wallbox_connection_error:
|
||||||
|
raise UpdateFailed from wallbox_connection_error
|
||||||
|
|
||||||
async def _async_update_data(self) -> dict[str, Any]:
|
async def _async_update_data(self) -> dict[str, Any]:
|
||||||
"""Get new sensor data for Wallbox component."""
|
"""Get new sensor data for Wallbox component."""
|
||||||
|
@ -6,6 +6,7 @@ from typing import Any
|
|||||||
from homeassistant.components.lock import LockEntity, LockEntityDescription
|
from homeassistant.components.lock import LockEntity, LockEntityDescription
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.exceptions import PlatformNotReady
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
from . import InvalidAuth, WallboxCoordinator, WallboxEntity
|
from . import InvalidAuth, WallboxCoordinator, WallboxEntity
|
||||||
@ -36,6 +37,8 @@ async def async_setup_entry(
|
|||||||
)
|
)
|
||||||
except InvalidAuth:
|
except InvalidAuth:
|
||||||
return
|
return
|
||||||
|
except ConnectionError as exc:
|
||||||
|
raise PlatformNotReady from exc
|
||||||
|
|
||||||
async_add_entities(
|
async_add_entities(
|
||||||
[
|
[
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"name": "Wallbox",
|
"name": "Wallbox",
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/wallbox",
|
"documentation": "https://www.home-assistant.io/integrations/wallbox",
|
||||||
"requirements": ["wallbox==0.4.9"],
|
"requirements": ["wallbox==0.4.10"],
|
||||||
"codeowners": ["@hesselonline"],
|
"codeowners": ["@hesselonline"],
|
||||||
"iot_class": "cloud_polling",
|
"iot_class": "cloud_polling",
|
||||||
"loggers": ["wallbox"]
|
"loggers": ["wallbox"]
|
||||||
|
@ -7,6 +7,7 @@ from typing import Optional, cast
|
|||||||
from homeassistant.components.number import NumberEntity, NumberEntityDescription
|
from homeassistant.components.number import NumberEntity, NumberEntityDescription
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.exceptions import PlatformNotReady
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
from . import InvalidAuth, WallboxCoordinator, WallboxEntity
|
from . import InvalidAuth, WallboxCoordinator, WallboxEntity
|
||||||
@ -46,6 +47,8 @@ async def async_setup_entry(
|
|||||||
)
|
)
|
||||||
except InvalidAuth:
|
except InvalidAuth:
|
||||||
return
|
return
|
||||||
|
except ConnectionError as exc:
|
||||||
|
raise PlatformNotReady from exc
|
||||||
|
|
||||||
async_add_entities(
|
async_add_entities(
|
||||||
[
|
[
|
||||||
|
@ -8,7 +8,7 @@ from .backports.enum import StrEnum
|
|||||||
APPLICATION_NAME: Final = "HomeAssistant"
|
APPLICATION_NAME: Final = "HomeAssistant"
|
||||||
MAJOR_VERSION: Final = 2022
|
MAJOR_VERSION: Final = 2022
|
||||||
MINOR_VERSION: Final = 10
|
MINOR_VERSION: Final = 10
|
||||||
PATCH_VERSION: Final = "3"
|
PATCH_VERSION: Final = "4"
|
||||||
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
|
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
|
||||||
__version__: Final = f"{__short_version__}.{PATCH_VERSION}"
|
__version__: Final = f"{__short_version__}.{PATCH_VERSION}"
|
||||||
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 9, 0)
|
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 9, 0)
|
||||||
|
@ -48,7 +48,7 @@ def convert(value: float, from_unit: str, to_unit: str) -> float:
|
|||||||
"""Convert one unit of measurement to another."""
|
"""Convert one unit of measurement to another."""
|
||||||
report(
|
report(
|
||||||
"uses distance utility. This is deprecated since 2022.10 and will "
|
"uses distance utility. This is deprecated since 2022.10 and will "
|
||||||
"stop working in Home Assistant 2022.4, it should be updated to use "
|
"stop working in Home Assistant 2023.4, it should be updated to use "
|
||||||
"unit_conversion.DistanceConverter instead",
|
"unit_conversion.DistanceConverter instead",
|
||||||
error_if_core=False,
|
error_if_core=False,
|
||||||
)
|
)
|
||||||
|
@ -27,7 +27,7 @@ def convert(value: float, from_unit: str, to_unit: str) -> float:
|
|||||||
"""Convert one unit of measurement to another."""
|
"""Convert one unit of measurement to another."""
|
||||||
report(
|
report(
|
||||||
"uses pressure utility. This is deprecated since 2022.10 and will "
|
"uses pressure utility. This is deprecated since 2022.10 and will "
|
||||||
"stop working in Home Assistant 2022.4, it should be updated to use "
|
"stop working in Home Assistant 2023.4, it should be updated to use "
|
||||||
"unit_conversion.PressureConverter instead",
|
"unit_conversion.PressureConverter instead",
|
||||||
error_if_core=False,
|
error_if_core=False,
|
||||||
)
|
)
|
||||||
|
@ -34,7 +34,7 @@ def convert(value: float, from_unit: str, to_unit: str) -> float:
|
|||||||
"""Convert one unit of measurement to another."""
|
"""Convert one unit of measurement to another."""
|
||||||
report(
|
report(
|
||||||
"uses speed utility. This is deprecated since 2022.10 and will "
|
"uses speed utility. This is deprecated since 2022.10 and will "
|
||||||
"stop working in Home Assistant 2022.4, it should be updated to use "
|
"stop working in Home Assistant 2023.4, it should be updated to use "
|
||||||
"unit_conversion.SpeedConverter instead",
|
"unit_conversion.SpeedConverter instead",
|
||||||
error_if_core=False,
|
error_if_core=False,
|
||||||
)
|
)
|
||||||
|
@ -39,7 +39,7 @@ def convert(
|
|||||||
"""Convert a temperature from one unit to another."""
|
"""Convert a temperature from one unit to another."""
|
||||||
report(
|
report(
|
||||||
"uses temperature utility. This is deprecated since 2022.10 and will "
|
"uses temperature utility. This is deprecated since 2022.10 and will "
|
||||||
"stop working in Home Assistant 2022.4, it should be updated to use "
|
"stop working in Home Assistant 2023.4, it should be updated to use "
|
||||||
"unit_conversion.TemperatureConverter instead",
|
"unit_conversion.TemperatureConverter instead",
|
||||||
error_if_core=False,
|
error_if_core=False,
|
||||||
)
|
)
|
||||||
|
@ -42,7 +42,7 @@ def convert(volume: float, from_unit: str, to_unit: str) -> float:
|
|||||||
"""Convert a volume from one unit to another."""
|
"""Convert a volume from one unit to another."""
|
||||||
report(
|
report(
|
||||||
"uses volume utility. This is deprecated since 2022.10 and will "
|
"uses volume utility. This is deprecated since 2022.10 and will "
|
||||||
"stop working in Home Assistant 2022.4, it should be updated to use "
|
"stop working in Home Assistant 2023.4, it should be updated to use "
|
||||||
"unit_conversion.VolumeConverter instead",
|
"unit_conversion.VolumeConverter instead",
|
||||||
error_if_core=False,
|
error_if_core=False,
|
||||||
)
|
)
|
||||||
|
@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "homeassistant"
|
name = "homeassistant"
|
||||||
version = "2022.10.3"
|
version = "2022.10.4"
|
||||||
license = {text = "Apache-2.0"}
|
license = {text = "Apache-2.0"}
|
||||||
description = "Open-source home automation platform running on Python 3."
|
description = "Open-source home automation platform running on Python 3."
|
||||||
readme = "README.rst"
|
readme = "README.rst"
|
||||||
|
@ -10,9 +10,6 @@ AIOAladdinConnect==0.1.46
|
|||||||
# homeassistant.components.adax
|
# homeassistant.components.adax
|
||||||
Adax-local==0.1.4
|
Adax-local==0.1.4
|
||||||
|
|
||||||
# homeassistant.components.homekit
|
|
||||||
HAP-python==4.5.0
|
|
||||||
|
|
||||||
# homeassistant.components.mastodon
|
# homeassistant.components.mastodon
|
||||||
Mastodon.py==1.5.1
|
Mastodon.py==1.5.1
|
||||||
|
|
||||||
@ -156,7 +153,7 @@ aioecowitt==2022.09.3
|
|||||||
aioemonitor==1.0.5
|
aioemonitor==1.0.5
|
||||||
|
|
||||||
# homeassistant.components.esphome
|
# homeassistant.components.esphome
|
||||||
aioesphomeapi==11.1.0
|
aioesphomeapi==11.1.1
|
||||||
|
|
||||||
# homeassistant.components.flo
|
# homeassistant.components.flo
|
||||||
aioflo==2021.11.0
|
aioflo==2021.11.0
|
||||||
@ -815,6 +812,9 @@ gstreamer-player==1.1.2
|
|||||||
# homeassistant.components.profiler
|
# homeassistant.components.profiler
|
||||||
guppy3==3.1.2
|
guppy3==3.1.2
|
||||||
|
|
||||||
|
# homeassistant.components.homekit
|
||||||
|
ha-HAP-python==4.5.2
|
||||||
|
|
||||||
# homeassistant.components.generic
|
# homeassistant.components.generic
|
||||||
# homeassistant.components.stream
|
# homeassistant.components.stream
|
||||||
ha-av==10.0.0b5
|
ha-av==10.0.0b5
|
||||||
@ -1132,7 +1132,7 @@ nettigo-air-monitor==1.4.2
|
|||||||
neurio==0.3.1
|
neurio==0.3.1
|
||||||
|
|
||||||
# homeassistant.components.nexia
|
# homeassistant.components.nexia
|
||||||
nexia==2.0.2
|
nexia==2.0.4
|
||||||
|
|
||||||
# homeassistant.components.nextcloud
|
# homeassistant.components.nextcloud
|
||||||
nextcloudmonitor==1.1.0
|
nextcloudmonitor==1.1.0
|
||||||
@ -2416,7 +2416,7 @@ tololib==0.1.0b3
|
|||||||
toonapi==0.2.1
|
toonapi==0.2.1
|
||||||
|
|
||||||
# homeassistant.components.totalconnect
|
# homeassistant.components.totalconnect
|
||||||
total_connect_client==2022.5
|
total_connect_client==2022.10
|
||||||
|
|
||||||
# homeassistant.components.tplink_lte
|
# homeassistant.components.tplink_lte
|
||||||
tp-connected==0.0.4
|
tp-connected==0.0.4
|
||||||
@ -2503,7 +2503,7 @@ vultr==0.1.2
|
|||||||
wakeonlan==2.1.0
|
wakeonlan==2.1.0
|
||||||
|
|
||||||
# homeassistant.components.wallbox
|
# homeassistant.components.wallbox
|
||||||
wallbox==0.4.9
|
wallbox==0.4.10
|
||||||
|
|
||||||
# homeassistant.components.waqi
|
# homeassistant.components.waqi
|
||||||
waqiasync==1.0.0
|
waqiasync==1.0.0
|
||||||
@ -2568,7 +2568,7 @@ yalesmartalarmclient==0.3.9
|
|||||||
yalexs-ble==1.9.2
|
yalexs-ble==1.9.2
|
||||||
|
|
||||||
# homeassistant.components.august
|
# homeassistant.components.august
|
||||||
yalexs==1.2.4
|
yalexs==1.2.6
|
||||||
|
|
||||||
# homeassistant.components.yeelight
|
# homeassistant.components.yeelight
|
||||||
yeelight==0.7.10
|
yeelight==0.7.10
|
||||||
|
@ -12,9 +12,6 @@ AIOAladdinConnect==0.1.46
|
|||||||
# homeassistant.components.adax
|
# homeassistant.components.adax
|
||||||
Adax-local==0.1.4
|
Adax-local==0.1.4
|
||||||
|
|
||||||
# homeassistant.components.homekit
|
|
||||||
HAP-python==4.5.0
|
|
||||||
|
|
||||||
# homeassistant.components.flick_electric
|
# homeassistant.components.flick_electric
|
||||||
PyFlick==0.0.2
|
PyFlick==0.0.2
|
||||||
|
|
||||||
@ -143,7 +140,7 @@ aioecowitt==2022.09.3
|
|||||||
aioemonitor==1.0.5
|
aioemonitor==1.0.5
|
||||||
|
|
||||||
# homeassistant.components.esphome
|
# homeassistant.components.esphome
|
||||||
aioesphomeapi==11.1.0
|
aioesphomeapi==11.1.1
|
||||||
|
|
||||||
# homeassistant.components.flo
|
# homeassistant.components.flo
|
||||||
aioflo==2021.11.0
|
aioflo==2021.11.0
|
||||||
@ -607,6 +604,9 @@ gspread==5.5.0
|
|||||||
# homeassistant.components.profiler
|
# homeassistant.components.profiler
|
||||||
guppy3==3.1.2
|
guppy3==3.1.2
|
||||||
|
|
||||||
|
# homeassistant.components.homekit
|
||||||
|
ha-HAP-python==4.5.2
|
||||||
|
|
||||||
# homeassistant.components.generic
|
# homeassistant.components.generic
|
||||||
# homeassistant.components.stream
|
# homeassistant.components.stream
|
||||||
ha-av==10.0.0b5
|
ha-av==10.0.0b5
|
||||||
@ -822,7 +822,7 @@ netmap==0.7.0.2
|
|||||||
nettigo-air-monitor==1.4.2
|
nettigo-air-monitor==1.4.2
|
||||||
|
|
||||||
# homeassistant.components.nexia
|
# homeassistant.components.nexia
|
||||||
nexia==2.0.2
|
nexia==2.0.4
|
||||||
|
|
||||||
# homeassistant.components.discord
|
# homeassistant.components.discord
|
||||||
nextcord==2.0.0a8
|
nextcord==2.0.0a8
|
||||||
@ -1656,7 +1656,7 @@ tololib==0.1.0b3
|
|||||||
toonapi==0.2.1
|
toonapi==0.2.1
|
||||||
|
|
||||||
# homeassistant.components.totalconnect
|
# homeassistant.components.totalconnect
|
||||||
total_connect_client==2022.5
|
total_connect_client==2022.10
|
||||||
|
|
||||||
# homeassistant.components.transmission
|
# homeassistant.components.transmission
|
||||||
transmissionrpc==0.11
|
transmissionrpc==0.11
|
||||||
@ -1731,7 +1731,7 @@ vultr==0.1.2
|
|||||||
wakeonlan==2.1.0
|
wakeonlan==2.1.0
|
||||||
|
|
||||||
# homeassistant.components.wallbox
|
# homeassistant.components.wallbox
|
||||||
wallbox==0.4.9
|
wallbox==0.4.10
|
||||||
|
|
||||||
# homeassistant.components.folder_watcher
|
# homeassistant.components.folder_watcher
|
||||||
watchdog==2.1.9
|
watchdog==2.1.9
|
||||||
@ -1778,7 +1778,7 @@ yalesmartalarmclient==0.3.9
|
|||||||
yalexs-ble==1.9.2
|
yalexs-ble==1.9.2
|
||||||
|
|
||||||
# homeassistant.components.august
|
# homeassistant.components.august
|
||||||
yalexs==1.2.4
|
yalexs==1.2.6
|
||||||
|
|
||||||
# homeassistant.components.yeelight
|
# homeassistant.components.yeelight
|
||||||
yeelight==0.7.10
|
yeelight==0.7.10
|
||||||
|
73
tests/components/mqtt/test_mixins.py
Normal file
73
tests/components/mqtt/test_mixins.py
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
"""The tests for shared code of the MQTT platform."""
|
||||||
|
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from homeassistant.components import mqtt, sensor
|
||||||
|
from homeassistant.const import EVENT_STATE_CHANGED, Platform
|
||||||
|
import homeassistant.core as ha
|
||||||
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
|
from tests.common import async_fire_mqtt_message
|
||||||
|
|
||||||
|
|
||||||
|
@patch("homeassistant.components.mqtt.PLATFORMS", [Platform.SENSOR])
|
||||||
|
async def test_availability_with_shared_state_topic(
|
||||||
|
hass,
|
||||||
|
mqtt_mock_entry_with_yaml_config,
|
||||||
|
):
|
||||||
|
"""Test the state is not changed twice.
|
||||||
|
|
||||||
|
When an entity with a shared state_topic and availability_topic becomes available
|
||||||
|
The state should only change once.
|
||||||
|
"""
|
||||||
|
assert await async_setup_component(
|
||||||
|
hass,
|
||||||
|
mqtt.DOMAIN,
|
||||||
|
{
|
||||||
|
mqtt.DOMAIN: {
|
||||||
|
sensor.DOMAIN: {
|
||||||
|
"name": "test",
|
||||||
|
"state_topic": "test-topic",
|
||||||
|
"availability_topic": "test-topic",
|
||||||
|
"payload_available": True,
|
||||||
|
"payload_not_available": False,
|
||||||
|
"value_template": "{{ int(value) or '' }}",
|
||||||
|
"availability_template": "{{ value != '0' }}",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
await mqtt_mock_entry_with_yaml_config()
|
||||||
|
|
||||||
|
events = []
|
||||||
|
|
||||||
|
@ha.callback
|
||||||
|
def callback(event):
|
||||||
|
events.append(event)
|
||||||
|
|
||||||
|
hass.bus.async_listen(EVENT_STATE_CHANGED, callback)
|
||||||
|
|
||||||
|
async_fire_mqtt_message(hass, "test-topic", "100")
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
# Initially the state and the availability change
|
||||||
|
assert len(events) == 1
|
||||||
|
|
||||||
|
events.clear()
|
||||||
|
async_fire_mqtt_message(hass, "test-topic", "50")
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert len(events) == 1
|
||||||
|
|
||||||
|
events.clear()
|
||||||
|
async_fire_mqtt_message(hass, "test-topic", "0")
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
# Only the availability is changed since the template resukts in an empty payload
|
||||||
|
# This does not change the state
|
||||||
|
assert len(events) == 1
|
||||||
|
|
||||||
|
events.clear()
|
||||||
|
async_fire_mqtt_message(hass, "test-topic", "10")
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
# The availability is changed but the topic is shared,
|
||||||
|
# hence there the state will be written when the value is updated
|
||||||
|
assert len(events) == 1
|
@ -173,3 +173,29 @@ async def setup_integration_read_only(hass: HomeAssistant) -> None:
|
|||||||
|
|
||||||
await hass.config_entries.async_setup(entry.entry_id)
|
await hass.config_entries.async_setup(entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
|
||||||
|
async def setup_integration_platform_not_ready(hass: HomeAssistant) -> None:
|
||||||
|
"""Test wallbox sensor class setup for read only."""
|
||||||
|
|
||||||
|
with requests_mock.Mocker() as mock_request:
|
||||||
|
mock_request.get(
|
||||||
|
"https://user-api.wall-box.com/users/signin",
|
||||||
|
json=authorisation_response,
|
||||||
|
status_code=HTTPStatus.OK,
|
||||||
|
)
|
||||||
|
mock_request.get(
|
||||||
|
"https://api.wall-box.com/chargers/status/12345",
|
||||||
|
json=test_response,
|
||||||
|
status_code=HTTPStatus.OK,
|
||||||
|
)
|
||||||
|
mock_request.put(
|
||||||
|
"https://api.wall-box.com/v2/charger/12345",
|
||||||
|
json=test_response,
|
||||||
|
status_code=HTTPStatus.NOT_FOUND,
|
||||||
|
)
|
||||||
|
|
||||||
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
await hass.config_entries.async_setup(entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
@ -13,6 +13,7 @@ from . import (
|
|||||||
authorisation_response,
|
authorisation_response,
|
||||||
entry,
|
entry,
|
||||||
setup_integration,
|
setup_integration,
|
||||||
|
setup_integration_platform_not_ready,
|
||||||
setup_integration_read_only,
|
setup_integration_read_only,
|
||||||
)
|
)
|
||||||
from .const import MOCK_LOCK_ENTITY_ID
|
from .const import MOCK_LOCK_ENTITY_ID
|
||||||
@ -109,3 +110,15 @@ async def test_wallbox_lock_class_authentication_error(hass: HomeAssistant) -> N
|
|||||||
assert state is None
|
assert state is None
|
||||||
|
|
||||||
await hass.config_entries.async_unload(entry.entry_id)
|
await hass.config_entries.async_unload(entry.entry_id)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_wallbox_lock_class_platform_not_ready(hass: HomeAssistant) -> None:
|
||||||
|
"""Test wallbox lock not loaded on authentication error."""
|
||||||
|
|
||||||
|
await setup_integration_platform_not_ready(hass)
|
||||||
|
|
||||||
|
state = hass.states.get(MOCK_LOCK_ENTITY_ID)
|
||||||
|
|
||||||
|
assert state is None
|
||||||
|
|
||||||
|
await hass.config_entries.async_unload(entry.entry_id)
|
||||||
|
@ -9,7 +9,12 @@ from homeassistant.components.wallbox import CHARGER_MAX_CHARGING_CURRENT_KEY
|
|||||||
from homeassistant.const import ATTR_ENTITY_ID
|
from homeassistant.const import ATTR_ENTITY_ID
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
from . import authorisation_response, entry, setup_integration
|
from . import (
|
||||||
|
authorisation_response,
|
||||||
|
entry,
|
||||||
|
setup_integration,
|
||||||
|
setup_integration_platform_not_ready,
|
||||||
|
)
|
||||||
from .const import MOCK_NUMBER_ENTITY_ID
|
from .const import MOCK_NUMBER_ENTITY_ID
|
||||||
|
|
||||||
|
|
||||||
@ -71,3 +76,15 @@ async def test_wallbox_number_class_connection_error(hass: HomeAssistant) -> Non
|
|||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
await hass.config_entries.async_unload(entry.entry_id)
|
await hass.config_entries.async_unload(entry.entry_id)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_wallbox_number_class_platform_not_ready(hass: HomeAssistant) -> None:
|
||||||
|
"""Test wallbox lock not loaded on authentication error."""
|
||||||
|
|
||||||
|
await setup_integration_platform_not_ready(hass)
|
||||||
|
|
||||||
|
state = hass.states.get(MOCK_NUMBER_ENTITY_ID)
|
||||||
|
|
||||||
|
assert state is None
|
||||||
|
|
||||||
|
await hass.config_entries.async_unload(entry.entry_id)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user