diff --git a/CODEOWNERS b/CODEOWNERS index b60dad20232d..1e779bf69635 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1539,7 +1539,6 @@ CLAUDE.md @home-assistant/core /tests/components/radiotherm/ @vinnyfuria /homeassistant/components/rainbird/ @konikvranik @allenporter /tests/components/rainbird/ @konikvranik @allenporter -/homeassistant/components/raincloud/ @vanstinator /homeassistant/components/rainforest_eagle/ @gtdiehl @jcalbert @hastarin /tests/components/rainforest_eagle/ @gtdiehl @jcalbert @hastarin /homeassistant/components/rainforest_raven/ @cottsay diff --git a/homeassistant/brands/melnor.json b/homeassistant/brands/melnor.json deleted file mode 100644 index c04db5c4e7c3..000000000000 --- a/homeassistant/brands/melnor.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "domain": "melnor", - "name": "Melnor", - "integrations": ["melnor", "raincloud"] -} diff --git a/homeassistant/components/raincloud/__init__.py b/homeassistant/components/raincloud/__init__.py deleted file mode 100644 index 7a119d4109d0..000000000000 --- a/homeassistant/components/raincloud/__init__.py +++ /dev/null @@ -1,84 +0,0 @@ -"""Support for Melnor RainCloud sprinkler water timer.""" - -from datetime import timedelta -import logging - -import probatio -from raincloudy.core import RainCloudy -from requests.exceptions import ConnectTimeout, HTTPError - -from homeassistant.components import persistent_notification -from homeassistant.const import CONF_PASSWORD, CONF_SCAN_INTERVAL, CONF_USERNAME -from homeassistant.core import HomeAssistant -from homeassistant.helpers import config_validation as cv -from homeassistant.helpers.dispatcher import dispatcher_send -from homeassistant.helpers.event import track_time_interval -from homeassistant.helpers.typing import ConfigType - -from .const import DATA_RAINCLOUD, SIGNAL_UPDATE_RAINCLOUD - -_LOGGER = logging.getLogger(__name__) - -NOTIFICATION_ID = "raincloud_notification" -NOTIFICATION_TITLE = "Rain Cloud Setup" - -DOMAIN = "raincloud" - -SCAN_INTERVAL = timedelta(seconds=20) - -CONFIG_SCHEMA = probatio.Schema( - { - DOMAIN: probatio.Schema( - { - probatio.Required(CONF_USERNAME): cv.string, - probatio.Required(CONF_PASSWORD): cv.string, - probatio.Optional( - CONF_SCAN_INTERVAL, default=SCAN_INTERVAL - ): cv.time_period, - } - ) - }, - extra=probatio.ALLOW_EXTRA, -) - - -def setup(hass: HomeAssistant, config: ConfigType) -> bool: - """Set up the Melnor RainCloud component.""" - conf = config[DOMAIN] - username = conf.get(CONF_USERNAME) - password = conf.get(CONF_PASSWORD) - scan_interval = conf.get(CONF_SCAN_INTERVAL) - - try: - raincloud = RainCloudy(username=username, password=password) - if not raincloud.is_connected: - raise HTTPError # noqa: TRY301 - hass.data[DATA_RAINCLOUD] = RainCloudHub(raincloud) - except (ConnectTimeout, HTTPError) as ex: - _LOGGER.error("Unable to connect to Rain Cloud service: %s", str(ex)) - persistent_notification.create( - hass, - f"Error: {ex}
You will need to restart hass after fixing.", - title=NOTIFICATION_TITLE, - notification_id=NOTIFICATION_ID, - ) - return False - - def hub_refresh(event_time): - """Call Raincloud hub to refresh information.""" - _LOGGER.debug("Updating RainCloud Hub component") - hass.data[DATA_RAINCLOUD].data.update() - dispatcher_send(hass, SIGNAL_UPDATE_RAINCLOUD) - - # Call the Raincloud API to refresh updates - track_time_interval(hass, hub_refresh, scan_interval) - - return True - - -class RainCloudHub: - """Representation of a base RainCloud device.""" - - def __init__(self, data): - """Initialize the entity.""" - self.data = data diff --git a/homeassistant/components/raincloud/binary_sensor.py b/homeassistant/components/raincloud/binary_sensor.py deleted file mode 100644 index 3dbe0aa9a489..000000000000 --- a/homeassistant/components/raincloud/binary_sensor.py +++ /dev/null @@ -1,81 +0,0 @@ -"""Support for Melnor RainCloud sprinkler water timer.""" - -import logging -from typing import override - -import probatio - -from homeassistant.components.binary_sensor import ( - PLATFORM_SCHEMA as BINARY_SENSOR_PLATFORM_SCHEMA, - BinarySensorEntity, -) -from homeassistant.const import CONF_MONITORED_CONDITIONS -from homeassistant.core import HomeAssistant -from homeassistant.helpers import config_validation as cv -from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType - -from .const import DATA_RAINCLOUD -from .entity import RainCloudEntity - -_LOGGER = logging.getLogger(__name__) - -BINARY_SENSORS = ["is_watering", "status"] - -PLATFORM_SCHEMA = BINARY_SENSOR_PLATFORM_SCHEMA.extend( - { - probatio.Optional( - CONF_MONITORED_CONDITIONS, default=list(BINARY_SENSORS) - ): probatio.All(cv.ensure_list, [probatio.In(BINARY_SENSORS)]) - } -) - - -def setup_platform( - hass: HomeAssistant, - config: ConfigType, - add_entities: AddEntitiesCallback, - discovery_info: DiscoveryInfoType | None = None, -) -> None: - """Set up a sensor for a raincloud device.""" - raincloud = hass.data[DATA_RAINCLOUD].data - - sensors = [] - for sensor_type in config[CONF_MONITORED_CONDITIONS]: - if sensor_type == "status": - sensors.append(RainCloudBinarySensor(raincloud.controller, sensor_type)) - sensors.append( - RainCloudBinarySensor(raincloud.controller.faucet, sensor_type) - ) - - else: - # create a sensor for each zone managed by faucet - sensors.extend( - RainCloudBinarySensor(zone, sensor_type) - for zone in raincloud.controller.faucet.zones - ) - - add_entities(sensors, True) - - -class RainCloudBinarySensor(RainCloudEntity, BinarySensorEntity): - """A sensor implementation for raincloud device.""" - - def update(self) -> None: - """Get the latest data and updates the state.""" - _LOGGER.debug("Updating RainCloud sensor: %s", self.name) - state = getattr(self.data, self._sensor_type) - if self._sensor_type == "status": - self._attr_is_on = state == "Online" - else: - self._attr_is_on = state - - @property - @override - def icon(self) -> str | None: - """Return the icon of this device.""" - if self._sensor_type == "is_watering": - return "mdi:water" if self.is_on else "mdi:water-off" - if self._sensor_type == "status": - return "mdi:pipe" if self.is_on else "mdi:pipe-disconnected" - return super().icon diff --git a/homeassistant/components/raincloud/const.py b/homeassistant/components/raincloud/const.py deleted file mode 100644 index 957830ffcc55..000000000000 --- a/homeassistant/components/raincloud/const.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Support for Melnor RainCloud sprinkler water timer.""" - -DATA_RAINCLOUD = "raincloud" - -ICON_MAP = { - "auto_watering": "mdi:autorenew", - "battery": "", - "is_watering": "", - "manual_watering": "mdi:water-pump", - "next_cycle": "mdi:calendar-clock", - "rain_delay": "mdi:weather-rainy", - "status": "", - "watering_time": "mdi:water-pump", -} - - -SIGNAL_UPDATE_RAINCLOUD = "raincloud_update" diff --git a/homeassistant/components/raincloud/entity.py b/homeassistant/components/raincloud/entity.py deleted file mode 100644 index ef18b797188a..000000000000 --- a/homeassistant/components/raincloud/entity.py +++ /dev/null @@ -1,62 +0,0 @@ -"""Support for Melnor RainCloud sprinkler water timer.""" - -from typing import Any, override - -from homeassistant.helpers.dispatcher import async_dispatcher_connect -from homeassistant.helpers.entity import Entity - -from .const import SIGNAL_UPDATE_RAINCLOUD - -KEY_MAP = { - "auto_watering": "Automatic Watering", - "battery": "Battery", - "is_watering": "Watering", - "manual_watering": "Manual Watering", - "next_cycle": "Next Cycle", - "rain_delay": "Rain Delay", - "status": "Status", - "watering_time": "Remaining Watering Time", -} - -ICON_MAP = { - "auto_watering": "mdi:autorenew", - "battery": "", - "is_watering": "", - "manual_watering": "mdi:water-pump", - "next_cycle": "mdi:calendar-clock", - "rain_delay": "mdi:weather-rainy", - "status": "", - "watering_time": "mdi:water-pump", -} - - -class RainCloudEntity(Entity): - """Entity class for RainCloud devices.""" - - _attr_attribution = "Data provided by Melnor Aquatimer.com" - - def __init__(self, data, sensor_type): - """Initialize the RainCloud entity.""" - self.data = data - self._sensor_type = sensor_type - self._attr_name = f"{self.data.name} {KEY_MAP.get(self._sensor_type)}" - self._attr_icon = ICON_MAP.get(self._sensor_type) - - @override - async def async_added_to_hass(self) -> None: - """Register callbacks.""" - self.async_on_remove( - async_dispatcher_connect( - self.hass, SIGNAL_UPDATE_RAINCLOUD, self._update_callback - ) - ) - - def _update_callback(self): - """Call update method.""" - self.schedule_update_ha_state(True) - - @property - @override - def extra_state_attributes(self) -> dict[str, Any]: - """Return the state attributes.""" - return {"identifier": self.data.serial} diff --git a/homeassistant/components/raincloud/manifest.json b/homeassistant/components/raincloud/manifest.json deleted file mode 100644 index b5179622441b..000000000000 --- a/homeassistant/components/raincloud/manifest.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "domain": "raincloud", - "name": "Melnor RainCloud", - "codeowners": ["@vanstinator"], - "documentation": "https://www.home-assistant.io/integrations/raincloud", - "iot_class": "cloud_polling", - "loggers": ["raincloudy"], - "quality_scale": "legacy", - "requirements": ["raincloudy==0.0.7"] -} diff --git a/homeassistant/components/raincloud/sensor.py b/homeassistant/components/raincloud/sensor.py deleted file mode 100644 index b63440a48ca0..000000000000 --- a/homeassistant/components/raincloud/sensor.py +++ /dev/null @@ -1,95 +0,0 @@ -"""Support for Melnor RainCloud sprinkler water timer.""" - -import logging -from typing import cast, override - -import probatio - -from homeassistant.components.sensor import ( - PLATFORM_SCHEMA as SENSOR_PLATFORM_SCHEMA, - SensorEntity, -) -from homeassistant.const import CONF_MONITORED_CONDITIONS, PERCENTAGE, UnitOfTime -from homeassistant.core import HomeAssistant -from homeassistant.helpers import config_validation as cv -from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.icon import icon_for_battery_level -from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType - -from .const import DATA_RAINCLOUD -from .entity import RainCloudEntity - -_LOGGER = logging.getLogger(__name__) - -SENSORS = ["battery", "next_cycle", "rain_delay", "watering_time"] - -PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend( - { - probatio.Optional( - CONF_MONITORED_CONDITIONS, default=list(SENSORS) - ): probatio.All(cv.ensure_list, [probatio.In(SENSORS)]) - } -) - -UNIT_OF_MEASUREMENT_MAP: dict[str, str] = { - "auto_watering": "", - "battery": PERCENTAGE, - "is_watering": "", - "manual_watering": "", - "next_cycle": "", - "rain_delay": UnitOfTime.DAYS, - "status": "", - "watering_time": UnitOfTime.MINUTES, -} - - -def setup_platform( - hass: HomeAssistant, - config: ConfigType, - add_entities: AddEntitiesCallback, - discovery_info: DiscoveryInfoType | None = None, -) -> None: - """Set up a sensor for a raincloud device.""" - raincloud = hass.data[DATA_RAINCLOUD].data - - sensors = [] - for sensor_type in config[CONF_MONITORED_CONDITIONS]: - if sensor_type == "battery": - sensors.append(RainCloudSensor(raincloud.controller.faucet, sensor_type)) - else: - # create a sensor for each zone managed by a faucet - sensors.extend( - RainCloudSensor(zone, sensor_type) - for zone in raincloud.controller.faucet.zones - ) - - add_entities(sensors, True) - - -class RainCloudSensor(RainCloudEntity, SensorEntity): - """A sensor implementation for raincloud device.""" - - @property - @override - def native_unit_of_measurement(self) -> str | None: - """Return the units of measurement.""" - return UNIT_OF_MEASUREMENT_MAP.get(self._sensor_type) - - def update(self) -> None: - """Get the latest data and updates the states.""" - _LOGGER.debug("Updating RainCloud sensor: %s", self.name) - if self._sensor_type == "battery": - self._attr_native_value = self.data.battery - else: - self._attr_native_value = getattr(self.data, self._sensor_type) - - @property - @override - def icon(self) -> str | None: - """Icon to use in the frontend, if any.""" - if self._sensor_type == "battery" and self.native_value is not None: - return icon_for_battery_level( - battery_level=int(cast(float, self.native_value)), - charging=False, - ) - return super().icon diff --git a/homeassistant/components/raincloud/switch.py b/homeassistant/components/raincloud/switch.py deleted file mode 100644 index 8548b63d9cb0..000000000000 --- a/homeassistant/components/raincloud/switch.py +++ /dev/null @@ -1,102 +0,0 @@ -"""Support for Melnor RainCloud sprinkler water timer.""" - -import logging -from typing import Any, override - -import probatio - -from homeassistant.components.switch import ( - PLATFORM_SCHEMA as SWITCH_PLATFORM_SCHEMA, - SwitchEntity, -) -from homeassistant.const import CONF_MONITORED_CONDITIONS -from homeassistant.core import HomeAssistant -from homeassistant.helpers import config_validation as cv -from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType - -from .const import DATA_RAINCLOUD -from .entity import RainCloudEntity - -_LOGGER = logging.getLogger(__name__) - -ALLOWED_WATERING_TIME = [5, 10, 15, 30, 45, 60] -CONF_WATERING_TIME = "watering_minutes" -DEFAULT_WATERING_TIME = 15 - -SWITCHES = ["auto_watering", "manual_watering"] - -PLATFORM_SCHEMA = SWITCH_PLATFORM_SCHEMA.extend( - { - probatio.Optional( - CONF_MONITORED_CONDITIONS, default=list(SWITCHES) - ): probatio.All(cv.ensure_list, [probatio.In(SWITCHES)]), - probatio.Optional( - CONF_WATERING_TIME, default=DEFAULT_WATERING_TIME - ): probatio.All(probatio.In(ALLOWED_WATERING_TIME)), - } -) - - -def setup_platform( - hass: HomeAssistant, - config: ConfigType, - add_entities: AddEntitiesCallback, - discovery_info: DiscoveryInfoType | None = None, -) -> None: - """Set up a sensor for a raincloud device.""" - raincloud = hass.data[DATA_RAINCLOUD].data - default_watering_timer = config[CONF_WATERING_TIME] - - add_entities( - ( - RainCloudSwitch(default_watering_timer, zone, sensor_type) - for zone in raincloud.controller.faucet.zones - for sensor_type in config[CONF_MONITORED_CONDITIONS] - ), - True, - ) - - -class RainCloudSwitch(RainCloudEntity, SwitchEntity): - """A switch implementation for raincloud device.""" - - def __init__(self, default_watering_timer, *args): - """Initialize a switch for raincloud device.""" - super().__init__(*args) - self._default_watering_timer = default_watering_timer - - @override - def turn_on(self, **kwargs: Any) -> None: - """Turn the device on.""" - if self._sensor_type == "manual_watering": - self.data.watering_time = self._default_watering_timer - elif self._sensor_type == "auto_watering": - self.data.auto_watering = True - self._attr_is_on = True - - @override - def turn_off(self, **kwargs: Any) -> None: - """Turn the device off.""" - if self._sensor_type == "manual_watering": - self.data.watering_time = "off" - elif self._sensor_type == "auto_watering": - self.data.auto_watering = False - self._attr_is_on = False - - def update(self) -> None: - """Update device state.""" - _LOGGER.debug("Updating RainCloud switch: %s", self.name) - if self._sensor_type == "manual_watering": - self._attr_is_on = bool(self.data.watering_time) - elif self._sensor_type == "auto_watering": - self._attr_is_on = self.data.auto_watering - - @property - @override - def extra_state_attributes(self) -> dict[str, Any]: - """Return the state attributes.""" - return { - "default_manual_timer": self._default_watering_timer, - "identifier": self.data.serial, - } diff --git a/homeassistant/generated/integrations.json b/homeassistant/generated/integrations.json index d7ba4170c04f..3ca318574873 100644 --- a/homeassistant/generated/integrations.json +++ b/homeassistant/generated/integrations.json @@ -4339,21 +4339,10 @@ "iot_class": "cloud_polling" }, "melnor": { - "name": "Melnor", - "integrations": { - "melnor": { - "integration_type": "device", - "config_flow": true, - "iot_class": "local_polling", - "name": "Melnor Bluetooth" - }, - "raincloud": { - "integration_type": "hub", - "config_flow": false, - "iot_class": "cloud_polling", - "name": "Melnor RainCloud" - } - } + "name": "Melnor Bluetooth", + "integration_type": "device", + "config_flow": true, + "iot_class": "local_polling" }, "meraki": { "name": "Meraki", diff --git a/requirements_all.txt b/requirements_all.txt index 9699a2e8ae90..7ca2c592c0d9 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2952,9 +2952,6 @@ radios==0.3.2 # homeassistant.components.radiotherm radiotherm==2.1.0 -# homeassistant.components.raincloud -raincloudy==0.0.7 - # homeassistant.components.rapt_ble rapt-ble==2.0.0 diff --git a/script/hassfest/quality_scale.py b/script/hassfest/quality_scale.py index ce871ac262fe..9a9805e808fe 100644 --- a/script/hassfest/quality_scale.py +++ b/script/hassfest/quality_scale.py @@ -730,7 +730,6 @@ INTEGRATIONS_WITHOUT_QUALITY_SCALE_FILE = [ "radarr", "radio_browser", "radiotherm", - "raincloud", "rainforest_eagle", "rainforest_raven", "rainmachine", @@ -1667,7 +1666,6 @@ INTEGRATIONS_WITHOUT_SCALE = [ "radarr", "radio_browser", "radiotherm", - "raincloud", "rainforest_eagle", "rainforest_raven", "rainmachine",