Alter RainMachine to not create entities if the underlying data is missing (#72733)
parent
638992f9c4
commit
a3e1b285cf
|
@ -970,6 +970,7 @@ omit =
|
|||
homeassistant/components/rainmachine/model.py
|
||||
homeassistant/components/rainmachine/sensor.py
|
||||
homeassistant/components/rainmachine/switch.py
|
||||
homeassistant/components/rainmachine/util.py
|
||||
homeassistant/components/raspyrfm/*
|
||||
homeassistant/components/recollect_waste/__init__.py
|
||||
homeassistant/components/recollect_waste/sensor.py
|
||||
|
|
|
@ -50,10 +50,7 @@ from .const import (
|
|||
LOGGER,
|
||||
)
|
||||
|
||||
DEFAULT_ATTRIBUTION = "Data provided by Green Electronics LLC"
|
||||
DEFAULT_ICON = "mdi:water"
|
||||
DEFAULT_SSL = True
|
||||
DEFAULT_UPDATE_INTERVAL = timedelta(seconds=15)
|
||||
|
||||
CONFIG_SCHEMA = cv.removed(DOMAIN, raise_if_present=False)
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
"""This platform provides binary sensors for key RainMachine data."""
|
||||
from dataclasses import dataclass
|
||||
from functools import partial
|
||||
|
||||
from homeassistant.components.binary_sensor import (
|
||||
BinarySensorEntity,
|
||||
|
@ -21,6 +20,7 @@ from .const import (
|
|||
DOMAIN,
|
||||
)
|
||||
from .model import RainMachineDescriptionMixinApiCategory
|
||||
from .util import key_exists
|
||||
|
||||
TYPE_FLOW_SENSOR = "flow_sensor"
|
||||
TYPE_FREEZE = "freeze"
|
||||
|
@ -46,6 +46,7 @@ BINARY_SENSOR_DESCRIPTIONS = (
|
|||
name="Flow Sensor",
|
||||
icon="mdi:water-pump",
|
||||
api_category=DATA_PROVISION_SETTINGS,
|
||||
data_key="useFlowSensor",
|
||||
),
|
||||
RainMachineBinarySensorDescription(
|
||||
key=TYPE_FREEZE,
|
||||
|
@ -53,6 +54,7 @@ BINARY_SENSOR_DESCRIPTIONS = (
|
|||
icon="mdi:cancel",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
api_category=DATA_RESTRICTIONS_CURRENT,
|
||||
data_key="freeze",
|
||||
),
|
||||
RainMachineBinarySensorDescription(
|
||||
key=TYPE_FREEZE_PROTECTION,
|
||||
|
@ -60,6 +62,7 @@ BINARY_SENSOR_DESCRIPTIONS = (
|
|||
icon="mdi:weather-snowy",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
api_category=DATA_RESTRICTIONS_UNIVERSAL,
|
||||
data_key="freezeProtectEnabled",
|
||||
),
|
||||
RainMachineBinarySensorDescription(
|
||||
key=TYPE_HOT_DAYS,
|
||||
|
@ -67,6 +70,7 @@ BINARY_SENSOR_DESCRIPTIONS = (
|
|||
icon="mdi:thermometer-lines",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
api_category=DATA_RESTRICTIONS_UNIVERSAL,
|
||||
data_key="hotDaysExtraWatering",
|
||||
),
|
||||
RainMachineBinarySensorDescription(
|
||||
key=TYPE_HOURLY,
|
||||
|
@ -75,6 +79,7 @@ BINARY_SENSOR_DESCRIPTIONS = (
|
|||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
api_category=DATA_RESTRICTIONS_CURRENT,
|
||||
data_key="hourly",
|
||||
),
|
||||
RainMachineBinarySensorDescription(
|
||||
key=TYPE_MONTH,
|
||||
|
@ -83,6 +88,7 @@ BINARY_SENSOR_DESCRIPTIONS = (
|
|||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
api_category=DATA_RESTRICTIONS_CURRENT,
|
||||
data_key="month",
|
||||
),
|
||||
RainMachineBinarySensorDescription(
|
||||
key=TYPE_RAINDELAY,
|
||||
|
@ -91,6 +97,7 @@ BINARY_SENSOR_DESCRIPTIONS = (
|
|||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
api_category=DATA_RESTRICTIONS_CURRENT,
|
||||
data_key="rainDelay",
|
||||
),
|
||||
RainMachineBinarySensorDescription(
|
||||
key=TYPE_RAINSENSOR,
|
||||
|
@ -99,6 +106,7 @@ BINARY_SENSOR_DESCRIPTIONS = (
|
|||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
api_category=DATA_RESTRICTIONS_CURRENT,
|
||||
data_key="rainSensor",
|
||||
),
|
||||
RainMachineBinarySensorDescription(
|
||||
key=TYPE_WEEKDAY,
|
||||
|
@ -107,6 +115,7 @@ BINARY_SENSOR_DESCRIPTIONS = (
|
|||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
api_category=DATA_RESTRICTIONS_CURRENT,
|
||||
data_key="weekDay",
|
||||
),
|
||||
)
|
||||
|
||||
|
@ -118,35 +127,20 @@ async def async_setup_entry(
|
|||
controller = hass.data[DOMAIN][entry.entry_id][DATA_CONTROLLER]
|
||||
coordinators = hass.data[DOMAIN][entry.entry_id][DATA_COORDINATOR]
|
||||
|
||||
@callback
|
||||
def async_get_sensor_by_api_category(api_category: str) -> partial:
|
||||
"""Generate the appropriate sensor object for an API category."""
|
||||
if api_category == DATA_PROVISION_SETTINGS:
|
||||
return partial(
|
||||
ProvisionSettingsBinarySensor,
|
||||
entry,
|
||||
coordinators[DATA_PROVISION_SETTINGS],
|
||||
)
|
||||
|
||||
if api_category == DATA_RESTRICTIONS_CURRENT:
|
||||
return partial(
|
||||
CurrentRestrictionsBinarySensor,
|
||||
entry,
|
||||
coordinators[DATA_RESTRICTIONS_CURRENT],
|
||||
)
|
||||
|
||||
return partial(
|
||||
UniversalRestrictionsBinarySensor,
|
||||
entry,
|
||||
coordinators[DATA_RESTRICTIONS_UNIVERSAL],
|
||||
)
|
||||
api_category_sensor_map = {
|
||||
DATA_PROVISION_SETTINGS: ProvisionSettingsBinarySensor,
|
||||
DATA_RESTRICTIONS_CURRENT: CurrentRestrictionsBinarySensor,
|
||||
DATA_RESTRICTIONS_UNIVERSAL: UniversalRestrictionsBinarySensor,
|
||||
}
|
||||
|
||||
async_add_entities(
|
||||
[
|
||||
async_get_sensor_by_api_category(description.api_category)(
|
||||
controller, description
|
||||
api_category_sensor_map[description.api_category](
|
||||
entry, coordinator, controller, description
|
||||
)
|
||||
for description in BINARY_SENSOR_DESCRIPTIONS
|
||||
if (coordinator := coordinators[description.api_category]) is not None
|
||||
and key_exists(coordinator.data, description.data_key)
|
||||
]
|
||||
)
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ class RainMachineDescriptionMixinApiCategory:
|
|||
"""Define an entity description mixin for binary and regular sensors."""
|
||||
|
||||
api_category: str
|
||||
data_key: str
|
||||
|
||||
|
||||
@dataclass
|
||||
|
|
|
@ -3,7 +3,6 @@ from __future__ import annotations
|
|||
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime, timedelta
|
||||
from functools import partial
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
SensorDeviceClass,
|
||||
|
@ -33,6 +32,7 @@ from .model import (
|
|||
RainMachineDescriptionMixinApiCategory,
|
||||
RainMachineDescriptionMixinUid,
|
||||
)
|
||||
from .util import key_exists
|
||||
|
||||
DEFAULT_ZONE_COMPLETION_TIME_WOBBLE_TOLERANCE = timedelta(seconds=5)
|
||||
|
||||
|
@ -68,6 +68,7 @@ SENSOR_DESCRIPTIONS = (
|
|||
entity_registry_enabled_default=False,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
api_category=DATA_PROVISION_SETTINGS,
|
||||
data_key="flowSensorClicksPerCubicMeter",
|
||||
),
|
||||
RainMachineSensorDescriptionApiCategory(
|
||||
key=TYPE_FLOW_SENSOR_CONSUMED_LITERS,
|
||||
|
@ -78,6 +79,7 @@ SENSOR_DESCRIPTIONS = (
|
|||
entity_registry_enabled_default=False,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
api_category=DATA_PROVISION_SETTINGS,
|
||||
data_key="flowSensorWateringClicks",
|
||||
),
|
||||
RainMachineSensorDescriptionApiCategory(
|
||||
key=TYPE_FLOW_SENSOR_START_INDEX,
|
||||
|
@ -87,6 +89,7 @@ SENSOR_DESCRIPTIONS = (
|
|||
native_unit_of_measurement="index",
|
||||
entity_registry_enabled_default=False,
|
||||
api_category=DATA_PROVISION_SETTINGS,
|
||||
data_key="flowSensorStartIndex",
|
||||
),
|
||||
RainMachineSensorDescriptionApiCategory(
|
||||
key=TYPE_FLOW_SENSOR_WATERING_CLICKS,
|
||||
|
@ -97,6 +100,7 @@ SENSOR_DESCRIPTIONS = (
|
|||
entity_registry_enabled_default=False,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
api_category=DATA_PROVISION_SETTINGS,
|
||||
data_key="flowSensorWateringClicks",
|
||||
),
|
||||
RainMachineSensorDescriptionApiCategory(
|
||||
key=TYPE_FREEZE_TEMP,
|
||||
|
@ -107,6 +111,7 @@ SENSOR_DESCRIPTIONS = (
|
|||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
api_category=DATA_RESTRICTIONS_UNIVERSAL,
|
||||
data_key="freezeProtectTemp",
|
||||
),
|
||||
)
|
||||
|
||||
|
@ -118,27 +123,18 @@ async def async_setup_entry(
|
|||
controller = hass.data[DOMAIN][entry.entry_id][DATA_CONTROLLER]
|
||||
coordinators = hass.data[DOMAIN][entry.entry_id][DATA_COORDINATOR]
|
||||
|
||||
@callback
|
||||
def async_get_sensor_by_api_category(api_category: str) -> partial:
|
||||
"""Generate the appropriate sensor object for an API category."""
|
||||
if api_category == DATA_PROVISION_SETTINGS:
|
||||
return partial(
|
||||
ProvisionSettingsSensor,
|
||||
entry,
|
||||
coordinators[DATA_PROVISION_SETTINGS],
|
||||
)
|
||||
|
||||
return partial(
|
||||
UniversalRestrictionsSensor,
|
||||
entry,
|
||||
coordinators[DATA_RESTRICTIONS_UNIVERSAL],
|
||||
)
|
||||
api_category_sensor_map = {
|
||||
DATA_PROVISION_SETTINGS: ProvisionSettingsSensor,
|
||||
DATA_RESTRICTIONS_UNIVERSAL: UniversalRestrictionsSensor,
|
||||
}
|
||||
|
||||
sensors = [
|
||||
async_get_sensor_by_api_category(description.api_category)(
|
||||
controller, description
|
||||
api_category_sensor_map[description.api_category](
|
||||
entry, coordinator, controller, description
|
||||
)
|
||||
for description in SENSOR_DESCRIPTIONS
|
||||
if (coordinator := coordinators[description.api_category]) is not None
|
||||
and key_exists(coordinator.data, description.data_key)
|
||||
]
|
||||
|
||||
zone_coordinator = coordinators[DATA_ZONES]
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
"""Define RainMachine utilities."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
|
||||
def key_exists(data: dict[str, Any], search_key: str) -> bool:
|
||||
"""Return whether a key exists in a nested dict."""
|
||||
for key, value in data.items():
|
||||
if key == search_key:
|
||||
return True
|
||||
if isinstance(value, dict):
|
||||
return key_exists(value, search_key)
|
||||
return False
|
Loading…
Reference in New Issue