From ce548efd807f76d43916374fcbfa3fdce8f7282e Mon Sep 17 00:00:00 2001 From: Manu <4445816+tr4nt0r@users.noreply.github.com> Date: Fri, 3 Oct 2025 21:18:39 +0200 Subject: [PATCH] Remove IBM Watson IoT Platform integration (#153567) --- homeassistant/brands/ibm.json | 5 - .../components/watson_iot/__init__.py | 227 ------------------ .../components/watson_iot/manifest.json | 10 - homeassistant/generated/integrations.json | 23 +- requirements_all.txt | 3 - script/hassfest/quality_scale.py | 2 - 6 files changed, 6 insertions(+), 264 deletions(-) delete mode 100644 homeassistant/brands/ibm.json delete mode 100644 homeassistant/components/watson_iot/__init__.py delete mode 100644 homeassistant/components/watson_iot/manifest.json diff --git a/homeassistant/brands/ibm.json b/homeassistant/brands/ibm.json deleted file mode 100644 index 42367e899e7..00000000000 --- a/homeassistant/brands/ibm.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "domain": "ibm", - "name": "IBM", - "integrations": ["watson_iot", "watson_tts"] -} diff --git a/homeassistant/components/watson_iot/__init__.py b/homeassistant/components/watson_iot/__init__.py deleted file mode 100644 index 0130b53930b..00000000000 --- a/homeassistant/components/watson_iot/__init__.py +++ /dev/null @@ -1,227 +0,0 @@ -"""Support for the IBM Watson IoT Platform.""" - -import logging -import queue -import threading -import time - -from ibmiotf import MissingMessageEncoderException -from ibmiotf.gateway import Client -import voluptuous as vol - -from homeassistant.const import ( - CONF_DOMAINS, - CONF_ENTITIES, - CONF_EXCLUDE, - CONF_ID, - CONF_INCLUDE, - CONF_TOKEN, - CONF_TYPE, - EVENT_HOMEASSISTANT_STOP, - EVENT_STATE_CHANGED, - STATE_UNAVAILABLE, - STATE_UNKNOWN, -) -from homeassistant.core import HomeAssistant, callback -from homeassistant.helpers import config_validation as cv, state as state_helper -from homeassistant.helpers.typing import ConfigType - -_LOGGER = logging.getLogger(__name__) - -CONF_ORG = "organization" - -DOMAIN = "watson_iot" - -MAX_TRIES = 3 - -RETRY_DELAY = 20 - -CONFIG_SCHEMA = vol.Schema( - { - DOMAIN: vol.All( - vol.Schema( - { - vol.Required(CONF_ORG): cv.string, - vol.Required(CONF_TYPE): cv.string, - vol.Required(CONF_ID): cv.string, - vol.Required(CONF_TOKEN): cv.string, - vol.Optional(CONF_EXCLUDE, default={}): vol.Schema( - { - vol.Optional(CONF_ENTITIES, default=[]): cv.entity_ids, - vol.Optional(CONF_DOMAINS, default=[]): vol.All( - cv.ensure_list, [cv.string] - ), - } - ), - vol.Optional(CONF_INCLUDE, default={}): vol.Schema( - { - vol.Optional(CONF_ENTITIES, default=[]): cv.entity_ids, - vol.Optional(CONF_DOMAINS, default=[]): vol.All( - cv.ensure_list, [cv.string] - ), - } - ), - } - ) - ) - }, - extra=vol.ALLOW_EXTRA, -) - - -def setup(hass: HomeAssistant, config: ConfigType) -> bool: - """Set up the Watson IoT Platform component.""" - - conf = config[DOMAIN] - - include = conf[CONF_INCLUDE] - exclude = conf[CONF_EXCLUDE] - include_e = set(include[CONF_ENTITIES]) - include_d = set(include[CONF_DOMAINS]) - exclude_e = set(exclude[CONF_ENTITIES]) - exclude_d = set(exclude[CONF_DOMAINS]) - - client_args = { - "org": conf[CONF_ORG], - "type": conf[CONF_TYPE], - "id": conf[CONF_ID], - "auth-method": "token", - "auth-token": conf[CONF_TOKEN], - } - watson_gateway = Client(client_args) - - def event_to_json(event): - """Add an event to the outgoing list.""" - state = event.data.get("new_state") - if ( - state is None - or state.state in (STATE_UNKNOWN, "", STATE_UNAVAILABLE) - or state.entity_id in exclude_e - or state.domain in exclude_d - ): - return None - - if (include_e and state.entity_id not in include_e) or ( - include_d and state.domain not in include_d - ): - return None - - try: - _state_as_value = float(state.state) - except ValueError: - _state_as_value = None - - if _state_as_value is None: - try: - _state_as_value = float(state_helper.state_as_number(state)) - except ValueError: - _state_as_value = None - - out_event = { - "tags": {"domain": state.domain, "entity_id": state.object_id}, - "time": event.time_fired.isoformat(), - "fields": {"state": state.state}, - } - if _state_as_value is not None: - out_event["fields"]["state_value"] = _state_as_value - - for key, value in state.attributes.items(): - if key != "unit_of_measurement": - # If the key is already in fields - if key in out_event["fields"]: - key = f"{key}_" - # For each value we try to cast it as float - # But if we cannot do it we store the value - # as string - try: - out_event["fields"][key] = float(value) - except (ValueError, TypeError): - out_event["fields"][key] = str(value) - - return out_event - - instance = hass.data[DOMAIN] = WatsonIOTThread(hass, watson_gateway, event_to_json) - instance.start() - - def shutdown(event): - """Shut down the thread.""" - instance.queue.put(None) - instance.join() - - hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, shutdown) - - return True - - -class WatsonIOTThread(threading.Thread): - """A threaded event handler class.""" - - def __init__(self, hass, gateway, event_to_json): - """Initialize the listener.""" - threading.Thread.__init__(self, name="WatsonIOT") - self.queue = queue.Queue() - self.gateway = gateway - self.gateway.connect() - self.event_to_json = event_to_json - self.write_errors = 0 - self.shutdown = False - hass.bus.listen(EVENT_STATE_CHANGED, self._event_listener) - - @callback - def _event_listener(self, event): - """Listen for new messages on the bus and queue them for Watson IoT.""" - item = (time.monotonic(), event) - self.queue.put(item) - - def get_events_json(self): - """Return an event formatted for writing.""" - events = [] - - try: - if (item := self.queue.get()) is None: - self.shutdown = True - else: - event_json = self.event_to_json(item[1]) - if event_json: - events.append(event_json) - - except queue.Empty: - pass - - return events - - def write_to_watson(self, events): - """Write preprocessed events to watson.""" - - for event in events: - for retry in range(MAX_TRIES + 1): - try: - for field in event["fields"]: - value = event["fields"][field] - device_success = self.gateway.publishDeviceEvent( - event["tags"]["domain"], - event["tags"]["entity_id"], - field, - "json", - value, - ) - if not device_success: - _LOGGER.error("Failed to publish message to Watson IoT") - continue - break - except (MissingMessageEncoderException, OSError): - if retry < MAX_TRIES: - time.sleep(RETRY_DELAY) - else: - _LOGGER.exception("Failed to publish message to Watson IoT") - - def run(self): - """Process incoming events.""" - while not self.shutdown: - if event := self.get_events_json(): - self.write_to_watson(event) - self.queue.task_done() - - def block_till_done(self): - """Block till all events processed.""" - self.queue.join() diff --git a/homeassistant/components/watson_iot/manifest.json b/homeassistant/components/watson_iot/manifest.json deleted file mode 100644 index a457dcc44b1..00000000000 --- a/homeassistant/components/watson_iot/manifest.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "domain": "watson_iot", - "name": "IBM Watson IoT Platform", - "codeowners": [], - "documentation": "https://www.home-assistant.io/integrations/watson_iot", - "iot_class": "cloud_push", - "loggers": ["ibmiotf", "paho_mqtt"], - "quality_scale": "legacy", - "requirements": ["ibmiotf==0.3.4"] -} diff --git a/homeassistant/generated/integrations.json b/homeassistant/generated/integrations.json index ef6dfdfc823..bd3cd7692c9 100644 --- a/homeassistant/generated/integrations.json +++ b/homeassistant/generated/integrations.json @@ -2923,23 +2923,6 @@ "iot_class": "cloud_polling", "single_config_entry": true }, - "ibm": { - "name": "IBM", - "integrations": { - "watson_iot": { - "integration_type": "hub", - "config_flow": false, - "iot_class": "cloud_push", - "name": "IBM Watson IoT Platform" - }, - "watson_tts": { - "integration_type": "hub", - "config_flow": false, - "iot_class": "cloud_push", - "name": "IBM Watson TTS" - } - } - }, "idteck_prox": { "name": "IDTECK Proximity Reader", "integration_type": "hub", @@ -7447,6 +7430,12 @@ "config_flow": true, "iot_class": "local_push" }, + "watson_tts": { + "name": "IBM Watson TTS", + "integration_type": "hub", + "config_flow": false, + "iot_class": "cloud_push" + }, "watttime": { "name": "WattTime", "integration_type": "service", diff --git a/requirements_all.txt b/requirements_all.txt index ad0cd2f3a90..7c69d19303a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1218,9 +1218,6 @@ iaqualink==0.6.0 # homeassistant.components.ibeacon ibeacon-ble==1.2.0 -# homeassistant.components.watson_iot -ibmiotf==0.3.4 - # homeassistant.components.google # homeassistant.components.local_calendar # homeassistant.components.local_todo diff --git a/script/hassfest/quality_scale.py b/script/hassfest/quality_scale.py index 97c8a63a1f6..7468afab890 100644 --- a/script/hassfest/quality_scale.py +++ b/script/hassfest/quality_scale.py @@ -1067,7 +1067,6 @@ INTEGRATIONS_WITHOUT_QUALITY_SCALE_FILE = [ "wallbox", "waqi", "waterfurnace", - "watson_iot", "watson_tts", "watttime", "waze_travel_time", @@ -2116,7 +2115,6 @@ INTEGRATIONS_WITHOUT_SCALE = [ "wallbox", "waqi", "waterfurnace", - "watson_iot", "watson_tts", "watttime", "waze_travel_time",