From 87cfe215674a9eb18910f24bab3cd757d119825f Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Wed, 20 Jul 2022 18:55:33 +0200 Subject: [PATCH] Remove XBee integration (#75502) --- .coveragerc | 1 - homeassistant/components/xbee/__init__.py | 441 ------------------ .../components/xbee/binary_sensor.py | 29 -- homeassistant/components/xbee/const.py | 5 - homeassistant/components/xbee/light.py | 34 -- homeassistant/components/xbee/manifest.json | 10 - homeassistant/components/xbee/sensor.py | 97 ---- homeassistant/components/xbee/switch.py | 29 -- 8 files changed, 646 deletions(-) delete mode 100644 homeassistant/components/xbee/__init__.py delete mode 100644 homeassistant/components/xbee/binary_sensor.py delete mode 100644 homeassistant/components/xbee/const.py delete mode 100644 homeassistant/components/xbee/light.py delete mode 100644 homeassistant/components/xbee/manifest.json delete mode 100644 homeassistant/components/xbee/sensor.py delete mode 100644 homeassistant/components/xbee/switch.py diff --git a/.coveragerc b/.coveragerc index 3645286980b..0dd9f18af34 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1523,7 +1523,6 @@ omit = homeassistant/components/zha/light.py homeassistant/components/zha/sensor.py homeassistant/components/zhong_hong/climate.py - homeassistant/components/xbee/* homeassistant/components/ziggo_mediabox_xl/media_player.py homeassistant/components/zoneminder/* homeassistant/components/supla/* diff --git a/homeassistant/components/xbee/__init__.py b/homeassistant/components/xbee/__init__.py deleted file mode 100644 index 6a7aba16b95..00000000000 --- a/homeassistant/components/xbee/__init__.py +++ /dev/null @@ -1,441 +0,0 @@ -"""Support for XBee Zigbee devices.""" -# pylint: disable=import-error -from binascii import hexlify, unhexlify -import logging - -from serial import Serial, SerialException -import voluptuous as vol -from xbee_helper import ZigBee -import xbee_helper.const as xb_const -from xbee_helper.device import convert_adc -from xbee_helper.exceptions import ZigBeeException, ZigBeeTxFailure - -from homeassistant.components.sensor import SensorEntity -from homeassistant.const import ( - CONF_ADDRESS, - CONF_DEVICE, - CONF_NAME, - CONF_PIN, - EVENT_HOMEASSISTANT_STOP, - PERCENTAGE, -) -from homeassistant.core import HomeAssistant -from homeassistant.helpers import config_validation as cv -from homeassistant.helpers.dispatcher import async_dispatcher_connect, dispatcher_send -from homeassistant.helpers.entity import Entity -from homeassistant.helpers.typing import ConfigType - -from .const import DOMAIN - -_LOGGER = logging.getLogger(__name__) - -SIGNAL_XBEE_FRAME_RECEIVED = "xbee_frame_received" - -CONF_BAUD = "baud" - -DEFAULT_DEVICE = "/dev/ttyUSB0" -DEFAULT_BAUD = 9600 -DEFAULT_ADC_MAX_VOLTS = 1.2 - -ATTR_FRAME = "frame" - -CONFIG_SCHEMA = vol.Schema( - { - DOMAIN: vol.Schema( - { - vol.Optional(CONF_BAUD, default=DEFAULT_BAUD): cv.string, - vol.Optional(CONF_DEVICE, default=DEFAULT_DEVICE): cv.string, - } - ) - }, - extra=vol.ALLOW_EXTRA, -) - -PLATFORM_SCHEMA = vol.Schema( - { - vol.Required(CONF_NAME): cv.string, - vol.Optional(CONF_PIN): cv.positive_int, - vol.Optional(CONF_ADDRESS): cv.string, - }, - extra=vol.ALLOW_EXTRA, -) - - -def setup(hass: HomeAssistant, config: ConfigType) -> bool: - """Set up the connection to the XBee Zigbee device.""" - usb_device = config[DOMAIN].get(CONF_DEVICE, DEFAULT_DEVICE) - baud = int(config[DOMAIN].get(CONF_BAUD, DEFAULT_BAUD)) - try: - ser = Serial(usb_device, baud) - except SerialException as exc: - _LOGGER.exception("Unable to open serial port for XBee: %s", exc) - return False - zigbee_device = ZigBee(ser) - - def close_serial_port(*args): - """Close the serial port we're using to communicate with the XBee.""" - zigbee_device.zb.serial.close() - - def _frame_received(frame): - """Run when a XBee Zigbee frame is received. - - Pickles the frame, then encodes it into base64 since it contains - non JSON serializable binary. - """ - dispatcher_send(hass, SIGNAL_XBEE_FRAME_RECEIVED, frame) - - hass.data[DOMAIN] = zigbee_device - hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, close_serial_port) - zigbee_device.add_frame_rx_handler(_frame_received) - - return True - - -def frame_is_relevant(entity, frame): - """Test whether the frame is relevant to the entity.""" - if frame.get("source_addr_long") != entity.config.address: - return False - return "samples" in frame - - -class XBeeConfig: - """Handle the fetching of configuration from the config file.""" - - def __init__(self, config): - """Initialize the configuration.""" - self._config = config - self._should_poll = config.get("poll", True) - - @property - def name(self): - """Return the name given to the entity.""" - return self._config["name"] - - @property - def address(self): - """Return the address of the device. - - If an address has been provided, unhexlify it, otherwise return None - as we're talking to our local XBee device. - """ - if (address := self._config.get("address")) is not None: - return unhexlify(address) - return address - - @property - def should_poll(self): - """Return the polling state.""" - return self._should_poll - - -class XBeePinConfig(XBeeConfig): - """Handle the fetching of configuration from the configuration file.""" - - @property - def pin(self): - """Return the GPIO pin number.""" - return self._config["pin"] - - -class XBeeDigitalInConfig(XBeePinConfig): - """A subclass of XBeePinConfig.""" - - def __init__(self, config): - """Initialise the XBee Zigbee Digital input config.""" - super().__init__(config) - self._bool2state, self._state2bool = self.boolean_maps - - @property - def boolean_maps(self): - """Create mapping dictionaries for potential inversion of booleans. - - Create dicts to map the pin state (true/false) to potentially inverted - values depending on the on_state config value which should be set to - "low" or "high". - """ - if self._config.get("on_state", "").lower() == "low": - bool2state = {True: False, False: True} - else: - bool2state = {True: True, False: False} - state2bool = {v: k for k, v in bool2state.items()} - return bool2state, state2bool - - @property - def bool2state(self): - """Return a dictionary mapping the internal value to the Zigbee value. - - For the translation of on/off as being pin high or low. - """ - return self._bool2state - - @property - def state2bool(self): - """Return a dictionary mapping the Zigbee value to the internal value. - - For the translation of pin high/low as being on or off. - """ - return self._state2bool - - -class XBeeDigitalOutConfig(XBeePinConfig): - """A subclass of XBeePinConfig. - - Set _should_poll to default as False instead of True. The value will - still be overridden by the presence of a 'poll' config entry. - """ - - def __init__(self, config): - """Initialize the XBee Zigbee Digital out.""" - super().__init__(config) - self._bool2state, self._state2bool = self.boolean_maps - self._should_poll = config.get("poll", False) - - @property - def boolean_maps(self): - """Create dicts to map booleans to pin high/low and vice versa. - - Depends on the config item "on_state" which should be set to "low" - or "high". - """ - if self._config.get("on_state", "").lower() == "low": - bool2state = { - True: xb_const.GPIO_DIGITAL_OUTPUT_LOW, - False: xb_const.GPIO_DIGITAL_OUTPUT_HIGH, - } - else: - bool2state = { - True: xb_const.GPIO_DIGITAL_OUTPUT_HIGH, - False: xb_const.GPIO_DIGITAL_OUTPUT_LOW, - } - state2bool = {v: k for k, v in bool2state.items()} - return bool2state, state2bool - - @property - def bool2state(self): - """Return a dictionary mapping booleans to GPIOSetting objects. - - For the translation of on/off as being pin high or low. - """ - return self._bool2state - - @property - def state2bool(self): - """Return a dictionary mapping GPIOSetting objects to booleans. - - For the translation of pin high/low as being on or off. - """ - return self._state2bool - - -class XBeeAnalogInConfig(XBeePinConfig): - """Representation of a XBee Zigbee GPIO pin set to analog in.""" - - @property - def max_voltage(self): - """Return the voltage for ADC to report its highest value.""" - return float(self._config.get("max_volts", DEFAULT_ADC_MAX_VOLTS)) - - -class XBeeDigitalIn(Entity): - """Representation of a GPIO pin configured as a digital input.""" - - def __init__(self, config, device): - """Initialize the device.""" - self._config = config - self._device = device - self._state = False - - async def async_added_to_hass(self): - """Register callbacks.""" - - def handle_frame(frame): - """Handle an incoming frame. - - Handle an incoming frame and update our status if it contains - information relating to this device. - """ - if not frame_is_relevant(self, frame): - return - sample = next(iter(frame["samples"])) - pin_name = xb_const.DIGITAL_PINS[self._config.pin] - if pin_name not in sample: - # Doesn't contain information about our pin - return - # Set state to the value of sample, respecting any inversion - # logic from the on_state config variable. - self._state = self._config.state2bool[ - self._config.bool2state[sample[pin_name]] - ] - self.schedule_update_ha_state() - - async_dispatcher_connect(self.hass, SIGNAL_XBEE_FRAME_RECEIVED, handle_frame) - - @property - def name(self): - """Return the name of the input.""" - return self._config.name - - @property - def config(self): - """Return the entity's configuration.""" - return self._config - - @property - def should_poll(self): - """Return the state of the polling, if needed.""" - return self._config.should_poll - - @property - def is_on(self): - """Return True if the Entity is on, else False.""" - return self._state - - def update(self): - """Ask the Zigbee device what state its input pin is in.""" - try: - sample = self._device.get_sample(self._config.address) - except ZigBeeTxFailure: - _LOGGER.warning( - "Transmission failure when attempting to get sample from " - "Zigbee device at address: %s", - hexlify(self._config.address), - ) - return - except ZigBeeException as exc: - _LOGGER.exception("Unable to get sample from Zigbee device: %s", exc) - return - pin_name = xb_const.DIGITAL_PINS[self._config.pin] - if pin_name not in sample: - _LOGGER.warning( - "Pin %s (%s) was not in the sample provided by Zigbee device %s", - self._config.pin, - pin_name, - hexlify(self._config.address), - ) - return - self._state = self._config.state2bool[sample[pin_name]] - - -class XBeeDigitalOut(XBeeDigitalIn): - """Representation of a GPIO pin configured as a digital input.""" - - def _set_state(self, state): - """Initialize the XBee Zigbee digital out device.""" - try: - self._device.set_gpio_pin( - self._config.pin, self._config.bool2state[state], self._config.address - ) - except ZigBeeTxFailure: - _LOGGER.warning( - "Transmission failure when attempting to set output pin on " - "Zigbee device at address: %s", - hexlify(self._config.address), - ) - return - except ZigBeeException as exc: - _LOGGER.exception("Unable to set digital pin on XBee device: %s", exc) - return - self._state = state - if not self.should_poll: - self.schedule_update_ha_state() - - def turn_on(self, **kwargs): - """Set the digital output to its 'on' state.""" - self._set_state(True) - - def turn_off(self, **kwargs): - """Set the digital output to its 'off' state.""" - self._set_state(False) - - def update(self): - """Ask the XBee device what its output is set to.""" - try: - pin_state = self._device.get_gpio_pin( - self._config.pin, self._config.address - ) - except ZigBeeTxFailure: - _LOGGER.warning( - "Transmission failure when attempting to get output pin status" - " from Zigbee device at address: %s", - hexlify(self._config.address), - ) - return - except ZigBeeException as exc: - _LOGGER.exception( - "Unable to get output pin status from XBee device: %s", exc - ) - return - self._state = self._config.state2bool[pin_state] - - -class XBeeAnalogIn(SensorEntity): - """Representation of a GPIO pin configured as an analog input.""" - - _attr_native_unit_of_measurement = PERCENTAGE - - def __init__(self, config, device): - """Initialize the XBee analog in device.""" - self._config = config - self._device = device - self._value = None - - async def async_added_to_hass(self): - """Register callbacks.""" - - def handle_frame(frame): - """Handle an incoming frame. - - Handle an incoming frame and update our status if it contains - information relating to this device. - """ - if not frame_is_relevant(self, frame): - return - sample = frame["samples"].pop() - pin_name = xb_const.ANALOG_PINS[self._config.pin] - if pin_name not in sample: - # Doesn't contain information about our pin - return - self._value = convert_adc( - sample[pin_name], xb_const.ADC_PERCENTAGE, self._config.max_voltage - ) - self.schedule_update_ha_state() - - async_dispatcher_connect(self.hass, SIGNAL_XBEE_FRAME_RECEIVED, handle_frame) - - @property - def name(self): - """Return the name of the input.""" - return self._config.name - - @property - def config(self): - """Return the entity's configuration.""" - return self._config - - @property - def should_poll(self): - """Return the polling state, if needed.""" - return self._config.should_poll - - @property - def sensor_state(self): - """Return the state of the entity.""" - return self._value - - def update(self): - """Get the latest reading from the ADC.""" - try: - self._value = self._device.read_analog_pin( - self._config.pin, - self._config.max_voltage, - self._config.address, - xb_const.ADC_PERCENTAGE, - ) - except ZigBeeTxFailure: - _LOGGER.warning( - "Transmission failure when attempting to get sample from " - "Zigbee device at address: %s", - hexlify(self._config.address), - ) - except ZigBeeException as exc: - _LOGGER.exception("Unable to get sample from Zigbee device: %s", exc) diff --git a/homeassistant/components/xbee/binary_sensor.py b/homeassistant/components/xbee/binary_sensor.py deleted file mode 100644 index b1639085993..00000000000 --- a/homeassistant/components/xbee/binary_sensor.py +++ /dev/null @@ -1,29 +0,0 @@ -"""Support for Zigbee binary sensors.""" -from __future__ import annotations - -import voluptuous as vol - -from homeassistant.components.binary_sensor import BinarySensorEntity -from homeassistant.core import HomeAssistant -from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType - -from . import PLATFORM_SCHEMA, XBeeDigitalIn, XBeeDigitalInConfig -from .const import CONF_ON_STATE, DOMAIN, STATES - -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({vol.Optional(CONF_ON_STATE): vol.In(STATES)}) - - -def setup_platform( - hass: HomeAssistant, - config: ConfigType, - add_entities: AddEntitiesCallback, - discovery_info: DiscoveryInfoType | None = None, -) -> None: - """Set up the XBee Zigbee binary sensor platform.""" - zigbee_device = hass.data[DOMAIN] - add_entities([XBeeBinarySensor(XBeeDigitalInConfig(config), zigbee_device)], True) - - -class XBeeBinarySensor(XBeeDigitalIn, BinarySensorEntity): - """Use XBeeDigitalIn as binary sensor.""" diff --git a/homeassistant/components/xbee/const.py b/homeassistant/components/xbee/const.py deleted file mode 100644 index a77e71e92f5..00000000000 --- a/homeassistant/components/xbee/const.py +++ /dev/null @@ -1,5 +0,0 @@ -"""Constants for the xbee integration.""" -CONF_ON_STATE = "on_state" -DEFAULT_ON_STATE = "high" -DOMAIN = "xbee" -STATES = ["high", "low"] diff --git a/homeassistant/components/xbee/light.py b/homeassistant/components/xbee/light.py deleted file mode 100644 index 126eaf91f9d..00000000000 --- a/homeassistant/components/xbee/light.py +++ /dev/null @@ -1,34 +0,0 @@ -"""Support for XBee Zigbee lights.""" -from __future__ import annotations - -import voluptuous as vol - -from homeassistant.components.light import ColorMode, LightEntity -from homeassistant.core import HomeAssistant -from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType - -from . import PLATFORM_SCHEMA, XBeeDigitalOut, XBeeDigitalOutConfig -from .const import CONF_ON_STATE, DEFAULT_ON_STATE, DOMAIN, STATES - -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( - {vol.Optional(CONF_ON_STATE, default=DEFAULT_ON_STATE): vol.In(STATES)} -) - - -def setup_platform( - hass: HomeAssistant, - config: ConfigType, - add_entities: AddEntitiesCallback, - discovery_info: DiscoveryInfoType | None = None, -) -> None: - """Create and add an entity based on the configuration.""" - zigbee_device = hass.data[DOMAIN] - add_entities([XBeeLight(XBeeDigitalOutConfig(config), zigbee_device)]) - - -class XBeeLight(XBeeDigitalOut, LightEntity): - """Use XBeeDigitalOut as light.""" - - _attr_color_mode = ColorMode.ONOFF - _attr_supported_color_modes = {ColorMode.ONOFF} diff --git a/homeassistant/components/xbee/manifest.json b/homeassistant/components/xbee/manifest.json deleted file mode 100644 index 150036129d2..00000000000 --- a/homeassistant/components/xbee/manifest.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "disabled": "Integration library not compatible with Python 3.10", - "domain": "xbee", - "name": "XBee", - "documentation": "https://www.home-assistant.io/integrations/xbee", - "requirements": ["xbee-helper==0.0.7"], - "codeowners": [], - "iot_class": "local_polling", - "loggers": ["xbee_helper"] -} diff --git a/homeassistant/components/xbee/sensor.py b/homeassistant/components/xbee/sensor.py deleted file mode 100644 index 9cea60ade8c..00000000000 --- a/homeassistant/components/xbee/sensor.py +++ /dev/null @@ -1,97 +0,0 @@ -"""Support for XBee Zigbee sensors.""" -# pylint: disable=import-error -from __future__ import annotations - -from binascii import hexlify -import logging - -import voluptuous as vol -from xbee_helper.exceptions import ZigBeeException, ZigBeeTxFailure - -from homeassistant.components.sensor import SensorDeviceClass, SensorEntity -from homeassistant.const import CONF_TYPE, TEMP_CELSIUS -from homeassistant.core import HomeAssistant -from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType - -from . import DOMAIN, PLATFORM_SCHEMA, XBeeAnalogIn, XBeeAnalogInConfig, XBeeConfig - -_LOGGER = logging.getLogger(__name__) - -CONF_MAX_VOLTS = "max_volts" - -DEFAULT_VOLTS = 1.2 -TYPES = ["analog", "temperature"] - -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( - { - vol.Required(CONF_TYPE): vol.In(TYPES), - vol.Optional(CONF_MAX_VOLTS, default=DEFAULT_VOLTS): vol.Coerce(float), - } -) - - -def setup_platform( - hass: HomeAssistant, - config: ConfigType, - add_entities: AddEntitiesCallback, - discovery_info: DiscoveryInfoType | None = None, -) -> None: - """Set up the XBee Zigbee platform. - - Uses the 'type' config value to work out which type of Zigbee sensor we're - dealing with and instantiates the relevant classes to handle it. - """ - zigbee_device = hass.data[DOMAIN] - typ = config[CONF_TYPE] - - try: - sensor_class, config_class = TYPE_CLASSES[typ] - except KeyError: - _LOGGER.exception("Unknown XBee Zigbee sensor type: %s", typ) - return - - add_entities([sensor_class(config_class(config), zigbee_device)], True) - - -class XBeeTemperatureSensor(SensorEntity): - """Representation of XBee Pro temperature sensor.""" - - _attr_device_class = SensorDeviceClass.TEMPERATURE - _attr_native_unit_of_measurement = TEMP_CELSIUS - - def __init__(self, config, device): - """Initialize the sensor.""" - self._config = config - self._device = device - self._temp = None - - @property - def name(self): - """Return the name of the sensor.""" - return self._config.name - - @property - def native_value(self): - """Return the state of the sensor.""" - return self._temp - - def update(self): - """Get the latest data.""" - try: - self._temp = self._device.get_temperature(self._config.address) - except ZigBeeTxFailure: - _LOGGER.warning( - "Transmission failure when attempting to get sample from " - "Zigbee device at address: %s", - hexlify(self._config.address), - ) - except ZigBeeException as exc: - _LOGGER.exception("Unable to get sample from Zigbee device: %s", exc) - - -# This must be below the classes to which it refers. -TYPE_CLASSES = { - "temperature": (XBeeTemperatureSensor, XBeeConfig), - "analog": (XBeeAnalogIn, XBeeAnalogInConfig), -} diff --git a/homeassistant/components/xbee/switch.py b/homeassistant/components/xbee/switch.py deleted file mode 100644 index 9cc25fbf7d2..00000000000 --- a/homeassistant/components/xbee/switch.py +++ /dev/null @@ -1,29 +0,0 @@ -"""Support for XBee Zigbee switches.""" -from __future__ import annotations - -import voluptuous as vol - -from homeassistant.components.switch import SwitchEntity -from homeassistant.core import HomeAssistant -from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType - -from . import PLATFORM_SCHEMA, XBeeDigitalOut, XBeeDigitalOutConfig -from .const import CONF_ON_STATE, DOMAIN, STATES - -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({vol.Optional(CONF_ON_STATE): vol.In(STATES)}) - - -def setup_platform( - hass: HomeAssistant, - config: ConfigType, - add_entities: AddEntitiesCallback, - discovery_info: DiscoveryInfoType | None = None, -) -> None: - """Set up the XBee Zigbee switch platform.""" - zigbee_device = hass.data[DOMAIN] - add_entities([XBeeSwitch(XBeeDigitalOutConfig(config), zigbee_device)]) - - -class XBeeSwitch(XBeeDigitalOut, SwitchEntity): - """Representation of a XBee Zigbee Digital Out device."""