From d87e8c60087dea5964cc2376dd4b30d49c94c859 Mon Sep 17 00:00:00 2001 From: RogerSelwyn Date: Tue, 21 Jan 2025 18:07:06 +0000 Subject: [PATCH] maint: Code tidy-up --- .../mqtt_discoverystream_alt/__init__.py | 29 ++++++--------- .../classes/base_entity.py | 36 +++++++++++++------ .../classes/base_input_entity.py | 9 +---- .../classes/climate.py | 24 ++++++------- .../mqtt_discoverystream_alt/classes/event.py | 15 ++------ .../mqtt_discoverystream_alt/classes/fan.py | 9 +---- .../mqtt_discoverystream_alt/classes/image.py | 27 ++++---------- .../mqtt_discoverystream_alt/classes/light.py | 7 +--- .../classes/update.py | 13 +------ .../classes/vacuum.py | 11 +----- .../mqtt_discoverystream_alt/discovery.py | 4 ++- .../mqtt_discoverystream_alt/publisher.py | 29 ++++++--------- .../mqtt_discoverystream_alt/utils.py | 19 ---------- 13 files changed, 73 insertions(+), 159 deletions(-) diff --git a/custom_components/mqtt_discoverystream_alt/__init__.py b/custom_components/mqtt_discoverystream_alt/__init__.py index ba618ab..f3982d8 100644 --- a/custom_components/mqtt_discoverystream_alt/__init__.py +++ b/custom_components/mqtt_discoverystream_alt/__init__.py @@ -59,32 +59,20 @@ async def _state_publisher(evt: Event[EventStateChangedData]) -> None: await publisher.async_state_publish(entity_id, new_state, mybase) else: payload = new_state.state - await mqtt.async_publish(hass, f"{mybase}state", payload, 1, publish_retain) + await _async_mqtt_publish(f"{mybase}state", payload) if publish_timestamps: if new_state.last_updated: - await mqtt.async_publish( - hass, - f"{mybase}last_updated", - new_state.last_updated.isoformat(), - 1, - publish_retain, + await _async_mqtt_publish( + f"{mybase}last_updated", new_state.last_updated.isoformat() ) if new_state.last_changed: - await mqtt.async_publish( - hass, - f"{mybase}last_changed", - new_state.last_changed.isoformat(), - 1, - publish_retain, + await _async_mqtt_publish( + f"{mybase}last_changed", new_state.last_changed.isoformat() ) - if publish_attributes: for key, val in new_state.attributes.items(): - encoded_val = json.dumps(val, cls=JSONEncoder) - await mqtt.async_publish( - hass, mybase + key, encoded_val, 1, publish_retain - ) + await _async_mqtt_publish(f"{mybase}{key}", val, encoded=True) @callback def _ha_started(hass: HomeAssistant) -> None: @@ -108,6 +96,11 @@ def _ha_stopping(_: Event) -> None: hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _ha_stopping) + async def _async_mqtt_publish(mybase, value, encoded=False): + if encoded: + value = json.dumps(value, cls=JSONEncoder) + await mqtt.async_publish(hass, mybase, value, 1, publish_retain) + async_at_start(hass, _ha_started) return True diff --git a/custom_components/mqtt_discoverystream_alt/classes/base_entity.py b/custom_components/mqtt_discoverystream_alt/classes/base_entity.py index 8d102db..5986d38 100644 --- a/custom_components/mqtt_discoverystream_alt/classes/base_entity.py +++ b/custom_components/mqtt_discoverystream_alt/classes/base_entity.py @@ -42,8 +42,8 @@ async def async_publish_state(self, new_state, mybase): mybase, ) - async def async_subscribe(self, command_topic): - """Subscribe to messages for a cover.""" + async def async_subscribe_commands(self, command_topic): + """Subscribe to messages for an entity.""" if not self._commands: return @@ -62,16 +62,30 @@ async def _async_handle_message(self, msg): async def _async_publish_base_attributes(self, new_state, mybase): """Publish the basic attributes for the entity state.""" if self._publish_state: - await mqtt.async_publish( - self._hass, - f"{mybase}{ATTR_STATE}", - new_state.state, - 1, - self._publish_retain, - ) + await self._async_mqtt_publish(ATTR_STATE, new_state.state, mybase) attributes = dict(new_state.attributes.items()) - encoded = json.dumps(attributes, cls=JSONEncoder) + await self._async_mqtt_publish( + ATTR_ATTRIBUTES, attributes, mybase, encoded=True + ) + + async def _async_mqtt_publish(self, topic, value, mybase, encoded=False): + if encoded: + value = json.dumps(value, cls=JSONEncoder) await mqtt.async_publish( - self._hass, f"{mybase}{ATTR_ATTRIBUTES}", encoded, 1, self._publish_retain + self._hass, + f"{mybase}{topic}", + value, + 1, + self._publish_retain, ) + + async def async_publish_attribute_if_exists( + self, new_state, mybase, attribute_name, strip=False + ): + """Publish a specific attribute""" + if attribute_name in new_state.attributes: + value = new_state.attributes[attribute_name] + if value and strip: + value = value.strip('"') + await self._async_mqtt_publish(attribute_name, value, mybase) diff --git a/custom_components/mqtt_discoverystream_alt/classes/base_input_entity.py b/custom_components/mqtt_discoverystream_alt/classes/base_input_entity.py index c46a4c3..be9bbb0 100644 --- a/custom_components/mqtt_discoverystream_alt/classes/base_input_entity.py +++ b/custom_components/mqtt_discoverystream_alt/classes/base_input_entity.py @@ -2,7 +2,6 @@ import logging -from homeassistant.components import mqtt from homeassistant.components.button.const import SERVICE_PRESS from homeassistant.components.number import ( ATTR_MAX, @@ -167,13 +166,7 @@ async def async_publish_state(self, new_state, mybase): """Publish the state for a text.""" if new_state.state != STATE_UNKNOWN: - await mqtt.async_publish( - self._hass, - f"{mybase}{ATTR_STATE}", - new_state.state, - 1, - self._publish_retain, - ) + await self._async_mqtt_publish(ATTR_STATE, new_state.state, mybase) await super().async_publish_state(new_state, mybase) diff --git a/custom_components/mqtt_discoverystream_alt/classes/climate.py b/custom_components/mqtt_discoverystream_alt/classes/climate.py index 2eace54..375ee01 100644 --- a/custom_components/mqtt_discoverystream_alt/classes/climate.py +++ b/custom_components/mqtt_discoverystream_alt/classes/climate.py @@ -2,7 +2,6 @@ import logging -from homeassistant.components import mqtt from homeassistant.components.climate import ( ATTR_CURRENT_TEMPERATURE, ATTR_HVAC_ACTION, @@ -50,7 +49,6 @@ from ..utils import ( EntityInfo, add_config_command, - async_publish_attribute, build_topic, validate_message, ) @@ -100,21 +98,21 @@ def build_config(self, config, entity_info: EntityInfo): async def async_publish_state(self, new_state, mybase): """Publish the state for a climate.""" - await async_publish_attribute( - self._hass, new_state, mybase, ATTR_HVAC_ACTION, self._publish_retain + await self.async_publish_attribute_if_exists( + new_state, + mybase, + ATTR_HVAC_ACTION, ) - await async_publish_attribute( - self._hass, + await self.async_publish_attribute_if_exists( new_state, mybase, ATTR_CURRENT_TEMPERATURE, - self._publish_retain, ) - await async_publish_attribute( - self._hass, new_state, mybase, ATTR_PRESET_MODE, self._publish_retain + await self.async_publish_attribute_if_exists( + new_state, mybase, ATTR_PRESET_MODE ) - await async_publish_attribute( - self._hass, new_state, mybase, ATTR_TEMPERATURE, self._publish_retain + await self.async_publish_attribute_if_exists( + new_state, mybase, ATTR_TEMPERATURE ) await super().async_publish_state(new_state, mybase) @@ -122,9 +120,7 @@ async def async_publish_state(self, new_state, mybase): payload = new_state.state if payload == STATE_UNAVAILABLE: payload = STATE_OFF - await mqtt.async_publish( - self._hass, f"{mybase}{ATTR_HVAC_MODE}", payload, 1, self._publish_retain - ) + await self._async_mqtt_publish(ATTR_HVAC_MODE, payload, mybase) async def _async_handle_message(self, msg): """Handle a message for a switch.""" diff --git a/custom_components/mqtt_discoverystream_alt/classes/event.py b/custom_components/mqtt_discoverystream_alt/classes/event.py index 1c8cc0c..cb68e18 100644 --- a/custom_components/mqtt_discoverystream_alt/classes/event.py +++ b/custom_components/mqtt_discoverystream_alt/classes/event.py @@ -1,12 +1,9 @@ """event methods for MQTT Discovery Statestream.""" -import json import logging -from homeassistant.components import mqtt from homeassistant.components.event import ATTR_EVENT_TYPE, ATTR_EVENT_TYPES from homeassistant.const import ATTR_STATE, Platform -from homeassistant.helpers.json import JSONEncoder from ..const import ( CONF_EVT_TYP, @@ -30,15 +27,7 @@ def build_config(self, config, entity_info: EntityInfo): async def async_publish_state(self, new_state, mybase): """Publish the state for a text.""" - payload = json.dumps( - {ATTR_EVENT_TYPE: new_state.attributes[ATTR_EVENT_TYPE]}, cls=JSONEncoder - ) - await mqtt.async_publish( - self._hass, - f"{mybase}{ATTR_STATE}", - payload, - 1, - self._publish_retain, - ) + payload = {ATTR_EVENT_TYPE: new_state.attributes[ATTR_EVENT_TYPE]} + await self._async_mqtt_publish(ATTR_STATE, payload, mybase, encoded=True) await super().async_publish_state(new_state, mybase) diff --git a/custom_components/mqtt_discoverystream_alt/classes/fan.py b/custom_components/mqtt_discoverystream_alt/classes/fan.py index 4df35e5..fbea630 100644 --- a/custom_components/mqtt_discoverystream_alt/classes/fan.py +++ b/custom_components/mqtt_discoverystream_alt/classes/fan.py @@ -2,7 +2,6 @@ import logging -from homeassistant.components import mqtt from homeassistant.components.fan import ( ATTR_DIRECTION, ATTR_OSCILLATING, @@ -155,13 +154,7 @@ async def async_publish_state(self, new_state, mybase): new_state.attributes[ATTR_PERCENTAGE] / new_state.attributes[ATTR_PERCENTAGE_STEP] ) - await mqtt.async_publish( - self._hass, - f"{mybase}{ATTR_PERCENTAGE}", - int(percentage), - 1, - self._publish_retain, - ) + await self._async_mqtt_publish(ATTR_PERCENTAGE, int(percentage), mybase) def _add_attribute(self, payload, new_state, attribute): if attribute in new_state.attributes and new_state.attributes[attribute]: diff --git a/custom_components/mqtt_discoverystream_alt/classes/image.py b/custom_components/mqtt_discoverystream_alt/classes/image.py index a323d26..015aa89 100644 --- a/custom_components/mqtt_discoverystream_alt/classes/image.py +++ b/custom_components/mqtt_discoverystream_alt/classes/image.py @@ -1,12 +1,9 @@ """Image methods for MQTT Discovery Statestream.""" -import json import logging -from homeassistant.components import mqtt from homeassistant.components.mqtt.image import CONF_URL_TOPIC from homeassistant.const import ATTR_ENTITY_PICTURE, ATTR_STATE, Platform -from homeassistant.helpers.json import JSONEncoder from homeassistant.helpers.network import get_url from ..const import ATTR_ATTRIBUTES, CONF_ENT_PIC @@ -30,14 +27,8 @@ def build_config(self, config, entity_info: EntityInfo): async def async_publish_state(self, new_state, mybase): """Publish the state for a image.""" - if self._publish_state: - await mqtt.async_publish( - self._hass, - f"{mybase}{ATTR_STATE}", - new_state.state, - 1, - self._publish_retain, - ) + + await self._async_mqtt_publish(ATTR_STATE, new_state.state, mybase) attributes = dict(new_state.attributes.items()) if ATTR_ENTITY_PICTURE in attributes: @@ -45,16 +36,10 @@ async def async_publish_state(self, new_state, mybase): if picture.startswith(f"/api/image_proxy/{new_state.entity_id}"): url = get_url(self._hass, prefer_external=True) picture = url + picture - await mqtt.async_publish( - self._hass, - f"{mybase}{ATTR_ENTITY_PICTURE}", - picture, - 1, - self._publish_retain, - ) + await self._async_mqtt_publish(ATTR_ENTITY_PICTURE, picture, mybase) del attributes[ATTR_ENTITY_PICTURE] del attributes["access_token"] - encoded = json.dumps(attributes, cls=JSONEncoder) - await mqtt.async_publish( - self._hass, f"{mybase}{ATTR_ATTRIBUTES}", encoded, 1, self._publish_retain + + await self._async_mqtt_publish( + ATTR_ATTRIBUTES, attributes, mybase, encoded=True ) diff --git a/custom_components/mqtt_discoverystream_alt/classes/light.py b/custom_components/mqtt_discoverystream_alt/classes/light.py index 942c31a..9929c22 100644 --- a/custom_components/mqtt_discoverystream_alt/classes/light.py +++ b/custom_components/mqtt_discoverystream_alt/classes/light.py @@ -3,7 +3,6 @@ import json import logging -from homeassistant.components import mqtt from homeassistant.components.light import ( ATTR_BRIGHTNESS, ATTR_COLOR_MODE, @@ -32,7 +31,6 @@ Platform, ) from homeassistant.helpers.entity import get_supported_features -from homeassistant.helpers.json import JSONEncoder from ..const import ( ATTR_B, @@ -102,10 +100,7 @@ async def async_publish_state(self, new_state, mybase): if color := self._add_colors(new_state): payload[ATTR_COLOR] = color - payload = json.dumps(payload, cls=JSONEncoder) - await mqtt.async_publish( - self._hass, f"{mybase}{ATTR_STATE}", payload, 1, self._publish_retain - ) + await self._async_mqtt_publish(ATTR_STATE, payload, mybase, encoded=True) def _add_attribute(self, payload, new_state, attribute): if attribute in new_state.attributes and new_state.attributes[attribute]: diff --git a/custom_components/mqtt_discoverystream_alt/classes/update.py b/custom_components/mqtt_discoverystream_alt/classes/update.py index 100610b..af8d888 100644 --- a/custom_components/mqtt_discoverystream_alt/classes/update.py +++ b/custom_components/mqtt_discoverystream_alt/classes/update.py @@ -1,8 +1,5 @@ """Update methods for MQTT Discovery Statestream.""" -import json - -from homeassistant.components import mqtt from homeassistant.components.mqtt.update import ( CONF_DISPLAY_PRECISION, CONF_LATEST_VERSION_TEMPLATE, @@ -30,7 +27,6 @@ CONF_VALUE_TEMPLATE, Platform, ) -from homeassistant.helpers.json import JSONEncoder from ..const import ATTR_INSTALL, COMMAND_INSTALL, CONF_CMD_T from ..utils import ( @@ -88,14 +84,7 @@ async def async_publish_state(self, new_state, mybase): simple_attribute_add(state, attributes, ATTR_RELEASE_SUMMARY) simple_attribute_add(state, attributes, ATTR_RELEASE_URL) simple_attribute_add(state, attributes, ATTR_ENTITY_PICTURE) - encoded = json.dumps(state, cls=JSONEncoder) - await mqtt.async_publish( - self._hass, - f"{mybase}{ATTR_STATE}", - encoded, - 1, - self._publish_retain, - ) + await self._async_mqtt_publish(ATTR_STATE, state, mybase, encoded=True) async def _async_handle_message(self, msg): """Handle a message for a fan.""" diff --git a/custom_components/mqtt_discoverystream_alt/classes/vacuum.py b/custom_components/mqtt_discoverystream_alt/classes/vacuum.py index b8df379..cf586b4 100644 --- a/custom_components/mqtt_discoverystream_alt/classes/vacuum.py +++ b/custom_components/mqtt_discoverystream_alt/classes/vacuum.py @@ -3,7 +3,6 @@ import json import logging -from homeassistant.components import mqtt from homeassistant.components.mqtt.vacuum import ( CONF_FAN_SPEED_LIST, CONF_SEND_COMMAND_TOPIC, @@ -28,7 +27,6 @@ ATTR_SUPPORTED_FEATURES, Platform, ) -from homeassistant.helpers.json import JSONEncoder from ..const import ( COMMAND_SEND, @@ -92,14 +90,7 @@ async def async_publish_state(self, new_state, mybase): state = {ATTR_STATE: new_state.state} simple_attribute_add(state, attributes, ATTR_BATTERY_LEVEL) simple_attribute_add(state, attributes, ATTR_FAN_SPEED) - encoded = json.dumps(state, cls=JSONEncoder) - await mqtt.async_publish( - self._hass, - f"{mybase}{ATTR_STATE}", - encoded, - 1, - self._publish_retain, - ) + await self._async_mqtt_publish(ATTR_STATE, state, mybase, encoded=True) def _build_supported_features(self, feat_list): sup_feat = [] diff --git a/custom_components/mqtt_discoverystream_alt/discovery.py b/custom_components/mqtt_discoverystream_alt/discovery.py index 88fe3ee..1bdf5e5 100644 --- a/custom_components/mqtt_discoverystream_alt/discovery.py +++ b/custom_components/mqtt_discoverystream_alt/discovery.py @@ -126,7 +126,9 @@ async def async_discovery_publish(self, entity_id, attributes, mybase): ) entityclass = self.discovery_classes[ent_domain] if ent_domain not in self._subscribed and self.subscribe_possible: - await entityclass.async_subscribe(set_topic(self._conf, CONF_COMMAND_TOPIC)) + await entityclass.async_subscribe_commands( + set_topic(self._conf, CONF_COMMAND_TOPIC) + ) self._subscribed.append(ent_domain) entityclass.build_config(config, entity_info) diff --git a/custom_components/mqtt_discoverystream_alt/publisher.py b/custom_components/mqtt_discoverystream_alt/publisher.py index 0b62944..58e20dc 100644 --- a/custom_components/mqtt_discoverystream_alt/publisher.py +++ b/custom_components/mqtt_discoverystream_alt/publisher.py @@ -69,24 +69,16 @@ async def async_state_publish(self, entity_id, new_state, mybase): return if new_state.state in (STATE_UNAVAILABLE, None): - await mqtt.async_publish( - self._hass, - f"{mybase}{CONF_AVAILABILITY}", - DEFAULT_PAYLOAD_NOT_AVAILABLE, - 1, - self._publish_retain, + await self._async_mqtt_publish( + mybase, CONF_AVAILABILITY, DEFAULT_PAYLOAD_NOT_AVAILABLE ) return entityclass = self._discovery.discovery_classes[ent_domain] await entityclass.async_publish_state(new_state, mybase) - await mqtt.async_publish( - self._hass, - f"{mybase}{CONF_AVAILABILITY}", - DEFAULT_PAYLOAD_AVAILABLE, - 1, - self._publish_retain, + await self._async_mqtt_publish( + mybase, CONF_AVAILABILITY, DEFAULT_PAYLOAD_AVAILABLE ) async def _async_birth_subscribe(self, hass, component): # pylint: disable=unused-argument @@ -165,12 +157,8 @@ async def _async_mark_unavailable(self, call): # pylint: disable=unused-argumen async def _async_mark_entity_unavailable(self, entity_id): mybase = f"{self._base_topic}{entity_id.replace('.', '/')}/" _LOGGER.info(entity_id) - await mqtt.async_publish( - self._hass, - f"{mybase}{CONF_AVAILABILITY}", - DEFAULT_PAYLOAD_NOT_AVAILABLE, - 0, - self._publish_retain, + await self._async_mqtt_publish( + mybase, CONF_AVAILABILITY, DEFAULT_PAYLOAD_NOT_AVAILABLE, qos=0 ) async def _async_schedule_publish(self, recalltime): # pylint: disable=unused-argument @@ -192,3 +180,8 @@ def _set_remote_status(self): if not remote_status_topic.endswith("/status"): remote_status_topic = f"{remote_status_topic}/status" return remote_status, remote_status_topic + + async def _async_mqtt_publish(self, mybase, topic, value, qos=1): + await mqtt.async_publish( + self._hass, f"{mybase}{topic}", value, qos, self._publish_retain + ) diff --git a/custom_components/mqtt_discoverystream_alt/utils.py b/custom_components/mqtt_discoverystream_alt/utils.py index 73d0a99..c90dd9a 100644 --- a/custom_components/mqtt_discoverystream_alt/utils.py +++ b/custom_components/mqtt_discoverystream_alt/utils.py @@ -3,8 +3,6 @@ import logging from dataclasses import dataclass, field -from homeassistant.components import mqtt - from .const import ( CONF_BASE_TOPIC, CONF_PUBLISHED, @@ -16,23 +14,6 @@ _LOGGER = logging.getLogger(__name__) -async def async_publish_attribute( - hass, new_state, mybase, attribute_name, publish_retain, strip=False -): - """Publish a specific attribute""" - if attribute_name in new_state.attributes: - value = new_state.attributes[attribute_name] - if value and strip: - value = value.strip('"') - await mqtt.async_publish( - hass, - f"{mybase}{attribute_name}", - value, - 1, - publish_retain, - ) - - def set_topic(conf, topic): """Create the topic string.""" response_topic = conf.get(topic) or conf.get(CONF_BASE_TOPIC)