From fb18c108d11a3bfac31e87bd30cc3e896c04ed1f Mon Sep 17 00:00:00 2001 From: shbatm Date: Tue, 12 Oct 2021 08:40:46 -0500 Subject: [PATCH] Add service to Rainmachine to push weather data from Home Assistant (#57354) --- .../components/rainmachine/__init__.py | 69 ++++++++- .../components/rainmachine/manifest.json | 4 +- .../components/rainmachine/services.yaml | 134 ++++++++++++++++++ requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 5 files changed, 205 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/rainmachine/__init__.py b/homeassistant/components/rainmachine/__init__.py index 0d25b6c41c7..72951223d09 100644 --- a/homeassistant/components/rainmachine/__init__.py +++ b/homeassistant/components/rainmachine/__init__.py @@ -46,8 +46,6 @@ from .const import ( LOGGER, ) -CONF_SECONDS = "seconds" - DEFAULT_ATTRIBUTION = "Data provided by Green Electronics LLC" DEFAULT_ICON = "mdi:water" DEFAULT_SSL = True @@ -57,7 +55,34 @@ CONFIG_SCHEMA = cv.deprecated(DOMAIN) PLATFORMS = ["binary_sensor", "sensor", "switch"] +# Constants expected by the RainMachine API for Service Data +CONF_CONDITION = "condition" +CONF_DEWPOINT = "dewpoint" +CONF_ET = "et" +CONF_MAXRH = "maxrh" +CONF_MAXTEMP = "maxtemp" +CONF_MINRH = "minrh" +CONF_MINTEMP = "mintemp" +CONF_PRESSURE = "pressure" +CONF_QPF = "qpf" +CONF_RAIN = "rain" +CONF_SECONDS = "seconds" +CONF_SOLARRAD = "solarrad" +CONF_TEMPERATURE = "temperature" +CONF_TIMESTAMP = "timestamp" +CONF_WEATHER = "weather" +CONF_WIND = "wind" + +# Config Validators for Weather Service Data +CV_WX_DATA_VALID_PERCENTAGE = vol.All(vol.Coerce(int), vol.Range(min=0, max=100)) +CV_WX_DATA_VALID_TEMP_RANGE = vol.All(vol.Coerce(float), vol.Range(min=-40.0, max=40.0)) +CV_WX_DATA_VALID_RAIN_RANGE = vol.All(vol.Coerce(float), vol.Range(min=0.0, max=1000.0)) +CV_WX_DATA_VALID_WIND_SPEED = vol.All(vol.Coerce(float), vol.Range(min=0.0, max=65.0)) +CV_WX_DATA_VALID_PRESSURE = vol.All(vol.Coerce(float), vol.Range(min=60.0, max=110.0)) +CV_WX_DATA_VALID_SOLARRAD = vol.All(vol.Coerce(float), vol.Range(min=0.0, max=5.0)) + SERVICE_NAME_PAUSE_WATERING = "pause_watering" +SERVICE_NAME_PUSH_WEATHER_DATA = "push_weather_data" SERVICE_NAME_STOP_ALL = "stop_all" SERVICE_NAME_UNPAUSE_WATERING = "unpause_watering" @@ -73,6 +98,25 @@ SERVICE_PAUSE_WATERING_SCHEMA = SERVICE_SCHEMA.extend( } ) +SERVICE_PUSH_WEATHER_DATA_SCHEMA = SERVICE_SCHEMA.extend( + { + vol.Optional(CONF_TIMESTAMP): cv.positive_float, + vol.Optional(CONF_MINTEMP): CV_WX_DATA_VALID_TEMP_RANGE, + vol.Optional(CONF_MAXTEMP): CV_WX_DATA_VALID_TEMP_RANGE, + vol.Optional(CONF_TEMPERATURE): CV_WX_DATA_VALID_TEMP_RANGE, + vol.Optional(CONF_WIND): CV_WX_DATA_VALID_WIND_SPEED, + vol.Optional(CONF_SOLARRAD): CV_WX_DATA_VALID_SOLARRAD, + vol.Optional(CONF_QPF): CV_WX_DATA_VALID_RAIN_RANGE, + vol.Optional(CONF_RAIN): CV_WX_DATA_VALID_RAIN_RANGE, + vol.Optional(CONF_ET): CV_WX_DATA_VALID_RAIN_RANGE, + vol.Optional(CONF_MINRH): CV_WX_DATA_VALID_PERCENTAGE, + vol.Optional(CONF_MAXRH): CV_WX_DATA_VALID_PERCENTAGE, + vol.Optional(CONF_CONDITION): cv.string, + vol.Optional(CONF_PRESSURE): CV_WX_DATA_VALID_PRESSURE, + vol.Optional(CONF_DEWPOINT): CV_WX_DATA_VALID_TEMP_RANGE, + } +) + @callback def async_get_controller_for_service_call( @@ -201,6 +245,21 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: await controller.watering.pause_all(call.data[CONF_SECONDS]) await async_update_programs_and_zones(hass, entry) + async def async_push_weather_data(call: ServiceCall) -> None: + """Push weather data to the device.""" + controller = async_get_controller_for_service_call(hass, call) + await controller.parsers.post_data( + { + CONF_WEATHER: [ + { + key: value + for key, value in call.data.items() + if key != CONF_DEVICE_ID + } + ] + } + ) + async def async_stop_all(call: ServiceCall) -> None: """Stop all watering.""" controller = async_get_controller_for_service_call(hass, call) @@ -219,6 +278,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: SERVICE_PAUSE_WATERING_SCHEMA, async_pause_watering, ), + ( + SERVICE_NAME_PUSH_WEATHER_DATA, + SERVICE_PUSH_WEATHER_DATA_SCHEMA, + async_push_weather_data, + ), (SERVICE_NAME_STOP_ALL, SERVICE_SCHEMA, async_stop_all), (SERVICE_NAME_UNPAUSE_WATERING, SERVICE_SCHEMA, async_unpause_watering), ): @@ -240,6 +304,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: # during integration setup: for service_name in ( SERVICE_NAME_PAUSE_WATERING, + SERVICE_NAME_PUSH_WEATHER_DATA, SERVICE_NAME_STOP_ALL, SERVICE_NAME_UNPAUSE_WATERING, ): diff --git a/homeassistant/components/rainmachine/manifest.json b/homeassistant/components/rainmachine/manifest.json index 03fedcf8c57..307cd7681f5 100644 --- a/homeassistant/components/rainmachine/manifest.json +++ b/homeassistant/components/rainmachine/manifest.json @@ -3,12 +3,12 @@ "name": "RainMachine", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/rainmachine", - "requirements": ["regenmaschine==3.1.5"], + "requirements": ["regenmaschine==3.2.0"], "codeowners": ["@bachya"], "iot_class": "local_polling", "homekit": { "models": ["Touch HD", "SPK5"] - }, + }, "zeroconf": [ { "type": "_http._tcp.local.", diff --git a/homeassistant/components/rainmachine/services.yaml b/homeassistant/components/rainmachine/services.yaml index fc6a71f8842..1709e32c261 100644 --- a/homeassistant/components/rainmachine/services.yaml +++ b/homeassistant/components/rainmachine/services.yaml @@ -107,3 +107,137 @@ unpause_watering: selector: device: integration: rainmachine +push_weather_data: + name: Push Weather Data + description: >- + Push Weather Data from Home Assistant to the RainMachine device. + + Local Weather Push service should be enabled from Settings > Weather > Developer tab for RainMachine to consider the values being sent. + Units must be sent in metric; no conversions are performed by the integraion. + + See details of RainMachine API Here: https://rainmachine.docs.apiary.io/#reference/weather-services/parserdata/post + fields: + device_id: + name: Controller + description: The controller for the weather data to be pushed. + required: true + selector: + device: + integration: rainmachine + timestamp: + name: Timestamp + description: UNIX Timestamp for the Weather Data. If omitted, the RainMachine device's local time at the time of the call is used. + selector: + text: + mintemp: + name: Min Temp + description: Minimum Temperature (°C). + selector: + number: + min: -40 + max: 40 + step: 0.1 + unit_of_measurement: '°C' + maxtemp: + name: Max Temp + description: Maximum Temperature (°C). + selector: + number: + min: -40 + max: 40 + step: 0.1 + unit_of_measurement: '°C' + temperature: + name: Temperature + description: Current Temperature (°C). + selector: + number: + min: -40 + max: 40 + step: 0.1 + unit_of_measurement: '°C' + wind: + name: Wind Speed + description: Wind Speed (m/s) + selector: + number: + min: 0 + max: 65 + unit_of_measurement: 'm/s' + solarrad: + name: Solar Radiation + description: Solar Radiation (MJ/m²/h) + selector: + number: + min: 0 + max: 5 + step: 0.1 + unit_of_measurement: 'MJ/m²/h' + et: + name: Evapotranspiration + description: Evapotranspiration (mm) + selector: + number: + min: 0 + max: 1000 + unit_of_measurement: 'mm' + qpf: + name: Quantitative Precipitation Forecast + description: >- + Quantitative Precipitation Forecast (mm), or QPF. Note: QPF values shouldn't + be send as cumulative values but the measured/forecasted values for each hour or day. + The RainMachine Mixer will sum all QPF values in the current day to have the day total QPF. + selector: + number: + min: 0 + max: 1000 + unit_of_measurement: 'mm' + rain: + name: Measured Rainfall + description: >- + Measured Rainfail (mm). Note: RAIN values shouldn't be send as cumulative values but the + measured/forecasted values for each hour or day. The RainMachine Mixer will sum all RAIN values + in the current day to have the day total RAIN. + selector: + number: + min: 0 + max: 1000 + unit_of_measurement: 'mm' + minrh: + name: Min Relative Humidity + description: Min Relative Humidity (%RH) + selector: + number: + min: 0 + max: 100 + unit_of_measurement: '%' + maxrh: + name: Max Relative Humidity + description: Max Relative Humidity (%RH) + selector: + number: + min: 0 + max: 100 + unit_of_measurement: '%' + condition: + name: Weather Condition Code + description: Current weather condition code (WNUM). + selector: + text: + pressure: + name: Barametric Pressure + description: Barametric Pressure (kPa) + selector: + number: + min: 60 + max: 110 + unit_of_measurement: "kPa" + dewpoint: + name: Dew Point + description: Dew Point (°C). + selector: + number: + min: -40 + max: 40 + step: 0.1 + unit_of_measurement: '°C' diff --git a/requirements_all.txt b/requirements_all.txt index 1628e027ed0..9512f9bf75f 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2043,7 +2043,7 @@ raincloudy==0.0.7 raspyrfm-client==1.2.8 # homeassistant.components.rainmachine -regenmaschine==3.1.5 +regenmaschine==3.2.0 # homeassistant.components.renault renault-api==0.1.4 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 27f5a1ed220..18211445831 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1175,7 +1175,7 @@ pyzerproc==0.4.8 rachiopy==1.0.3 # homeassistant.components.rainmachine -regenmaschine==3.1.5 +regenmaschine==3.2.0 # homeassistant.components.renault renault-api==0.1.4