Add DROP Alert product support (#117867)

* Add DROP Alert product support

* Add DROP Alert to sensor selftest

* Fix Alert sensor naming ambiguity

* Reorder a constant

* Alphabetize strings

* Remove unnecessary translation key
This commit is contained in:
Patrick Frazer 2024-08-21 15:56:59 -04:00 committed by GitHub
parent e9798cd1b4
commit 5f53d3f917
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 236 additions and 13 deletions

View file

@ -17,6 +17,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import ( from .const import (
CONF_DEVICE_TYPE, CONF_DEVICE_TYPE,
DEV_ALERT,
DEV_HUB, DEV_HUB,
DEV_LEAK_DETECTOR, DEV_LEAK_DETECTOR,
DEV_PROTECTION_VALVE, DEV_PROTECTION_VALVE,
@ -33,8 +34,10 @@ _LOGGER = logging.getLogger(__name__)
# Binary sensor type constants # Binary sensor type constants
ALERT_SENSOR = "alert_sensor"
LEAK_DETECTED = "leak" LEAK_DETECTED = "leak"
PENDING_NOTIFICATION = "pending_notification" PENDING_NOTIFICATION = "pending_notification"
POWER = "power"
PUMP_STATUS = "pump" PUMP_STATUS = "pump"
RESERVE_IN_USE = "reserve_in_use" RESERVE_IN_USE = "reserve_in_use"
SALT_LOW = "salt" SALT_LOW = "salt"
@ -74,10 +77,23 @@ BINARY_SENSORS: list[DROPBinarySensorEntityDescription] = [
translation_key=PUMP_STATUS, translation_key=PUMP_STATUS,
value_fn=lambda device: device.drop_api.pump_status(), value_fn=lambda device: device.drop_api.pump_status(),
), ),
DROPBinarySensorEntityDescription(
key=ALERT_SENSOR,
translation_key=ALERT_SENSOR,
device_class=BinarySensorDeviceClass.PROBLEM,
value_fn=lambda device: device.drop_api.sensor_high(),
),
DROPBinarySensorEntityDescription(
key=POWER,
translation_key=None, # Use name provided by binary sensor device class
device_class=BinarySensorDeviceClass.POWER,
value_fn=lambda device: device.drop_api.power(),
),
] ]
# Defines which binary sensors are used by each device type # Defines which binary sensors are used by each device type
DEVICE_BINARY_SENSORS: dict[str, list[str]] = { DEVICE_BINARY_SENSORS: dict[str, list[str]] = {
DEV_ALERT: [ALERT_SENSOR, POWER],
DEV_HUB: [LEAK_DETECTED, PENDING_NOTIFICATION], DEV_HUB: [LEAK_DETECTED, PENDING_NOTIFICATION],
DEV_LEAK_DETECTOR: [LEAK_DETECTED], DEV_LEAK_DETECTOR: [LEAK_DETECTED],
DEV_PROTECTION_VALVE: [LEAK_DETECTED], DEV_PROTECTION_VALVE: [LEAK_DETECTED],

View file

@ -11,6 +11,7 @@ CONF_DEVICE_NAME = "name"
CONF_DEVICE_OWNER_ID = "drop_device_owner_id" CONF_DEVICE_OWNER_ID = "drop_device_owner_id"
# Values for DROP device types # Values for DROP device types
DEV_ALERT = "alrt"
DEV_FILTER = "filt" DEV_FILTER = "filt"
DEV_HUB = "hub" DEV_HUB = "hub"
DEV_LEAK_DETECTOR = "leak" DEV_LEAK_DETECTOR = "leak"

View file

@ -27,6 +27,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import ( from .const import (
CONF_DEVICE_TYPE, CONF_DEVICE_TYPE,
DEV_ALERT,
DEV_FILTER, DEV_FILTER,
DEV_HUB, DEV_HUB,
DEV_LEAK_DETECTOR, DEV_LEAK_DETECTOR,
@ -222,6 +223,7 @@ DEVICE_SENSORS: dict[str, list[str]] = {
], ],
DEV_FILTER: [BATTERY, CURRENT_FLOW_RATE, CURRENT_SYSTEM_PRESSURE], DEV_FILTER: [BATTERY, CURRENT_FLOW_RATE, CURRENT_SYSTEM_PRESSURE],
DEV_LEAK_DETECTOR: [BATTERY, TEMPERATURE], DEV_LEAK_DETECTOR: [BATTERY, TEMPERATURE],
DEV_ALERT: [BATTERY, TEMPERATURE],
DEV_PROTECTION_VALVE: [ DEV_PROTECTION_VALVE: [
BATTERY, BATTERY,
CURRENT_FLOW_RATE, CURRENT_FLOW_RATE,

View file

@ -12,26 +12,27 @@
}, },
"entity": { "entity": {
"sensor": { "sensor": {
"current_flow_rate": { "name": "Water flow rate" },
"peak_flow_rate": { "name": "Peak water flow rate today" },
"water_used_today": { "name": "Total water used today" },
"average_water_used": { "name": "Average daily water usage" }, "average_water_used": { "name": "Average daily water usage" },
"capacity_remaining": { "name": "Capacity remaining" }, "capacity_remaining": { "name": "Capacity remaining" },
"current_system_pressure": { "name": "Current water pressure" },
"high_system_pressure": { "name": "High water pressure today" },
"low_system_pressure": { "name": "Low water pressure today" },
"inlet_tds": { "name": "Inlet TDS" },
"outlet_tds": { "name": "Outlet TDS" },
"cart1": { "name": "Cartridge 1 life remaining" }, "cart1": { "name": "Cartridge 1 life remaining" },
"cart2": { "name": "Cartridge 2 life remaining" }, "cart2": { "name": "Cartridge 2 life remaining" },
"cart3": { "name": "Cartridge 3 life remaining" } "cart3": { "name": "Cartridge 3 life remaining" },
"current_flow_rate": { "name": "Water flow rate" },
"current_system_pressure": { "name": "Current water pressure" },
"high_system_pressure": { "name": "High water pressure today" },
"inlet_tds": { "name": "Inlet TDS" },
"low_system_pressure": { "name": "Low water pressure today" },
"outlet_tds": { "name": "Outlet TDS" },
"peak_flow_rate": { "name": "Peak water flow rate today" },
"water_used_today": { "name": "Total water used today" }
}, },
"binary_sensor": { "binary_sensor": {
"alert_sensor": { "name": "Sensor" },
"leak": { "name": "Leak detected" }, "leak": { "name": "Leak detected" },
"pending_notification": { "name": "Notification unread" }, "pending_notification": { "name": "Notification unread" },
"pump": { "name": "Pump status" },
"reserve_in_use": { "name": "Reserve capacity in use" }, "reserve_in_use": { "name": "Reserve capacity in use" },
"salt": { "name": "Salt low" }, "salt": { "name": "Salt low" }
"pump": { "name": "Pump status" }
}, },
"select": { "select": {
"protect_mode": { "protect_mode": {
@ -44,8 +45,8 @@
} }
}, },
"switch": { "switch": {
"water": { "name": "Water supply" }, "bypass": { "name": "Treatment bypass" },
"bypass": { "name": "Treatment bypass" } "water": { "name": "Water supply" }
} }
} }
} }

View file

@ -34,6 +34,10 @@ TEST_DATA_SALT_TOPIC = "drop_connect/DROP-1_C0FFEE/8"
TEST_DATA_SALT = '{"salt":1}' TEST_DATA_SALT = '{"salt":1}'
TEST_DATA_SALT_RESET = '{"salt":0}' TEST_DATA_SALT_RESET = '{"salt":0}'
TEST_DATA_ALERT_TOPIC = "drop_connect/DROP-1_C0FFEE/81"
TEST_DATA_ALERT = '{"battery":100,"sens":1,"pwrOff":0,"temp":68.2}'
TEST_DATA_ALERT_RESET = '{"battery":0,"sens":0,"pwrOff":1,"temp":0}'
TEST_DATA_LEAK_TOPIC = "drop_connect/DROP-1_C0FFEE/20" TEST_DATA_LEAK_TOPIC = "drop_connect/DROP-1_C0FFEE/20"
TEST_DATA_LEAK = '{"battery":100,"leak":1,"temp":68.2}' TEST_DATA_LEAK = '{"battery":100,"leak":1,"temp":68.2}'
TEST_DATA_LEAK_RESET = '{"battery":0,"leak":0,"temp":0}' TEST_DATA_LEAK_RESET = '{"battery":0,"leak":0,"temp":0}'
@ -109,6 +113,25 @@ def config_entry_salt() -> ConfigEntry:
) )
def config_entry_alert() -> ConfigEntry:
"""Config entry version 1 fixture."""
return MockConfigEntry(
domain=DOMAIN,
unique_id="DROP-1_C0FFEE_81",
data={
CONF_COMMAND_TOPIC: "drop_connect/DROP-1_C0FFEE/81/cmd",
CONF_DATA_TOPIC: "drop_connect/DROP-1_C0FFEE/81/#",
CONF_DEVICE_DESC: "Alert",
CONF_DEVICE_ID: 81,
CONF_DEVICE_NAME: "Alert",
CONF_DEVICE_TYPE: "alrt",
CONF_HUB_ID: "DROP-1_C0FFEE",
CONF_DEVICE_OWNER_ID: "DROP-1_C0FFEE_255",
},
version=1,
)
def config_entry_leak() -> ConfigEntry: def config_entry_leak() -> ConfigEntry:
"""Config entry version 1 fixture.""" """Config entry version 1 fixture."""
return MockConfigEntry( return MockConfigEntry(

View file

@ -1,4 +1,98 @@
# serializer version: 1 # serializer version: 1
# name: test_sensors[alert][binary_sensor.alert_power-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'binary_sensor',
'entity_category': None,
'entity_id': 'binary_sensor.alert_power',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <BinarySensorDeviceClass.POWER: 'power'>,
'original_icon': None,
'original_name': 'Power',
'platform': 'drop_connect',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': 'DROP-1_C0FFEE_81_power',
'unit_of_measurement': None,
})
# ---
# name: test_sensors[alert][binary_sensor.alert_power-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'power',
'friendly_name': 'Alert Power',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.alert_power',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'on',
})
# ---
# name: test_sensors[alert][binary_sensor.alert_sensor-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'binary_sensor',
'entity_category': None,
'entity_id': 'binary_sensor.alert_sensor',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <BinarySensorDeviceClass.PROBLEM: 'problem'>,
'original_icon': None,
'original_name': 'Sensor',
'platform': 'drop_connect',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'alert_sensor',
'unique_id': 'DROP-1_C0FFEE_81_alert_sensor',
'unit_of_measurement': None,
})
# ---
# name: test_sensors[alert][binary_sensor.alert_sensor-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'problem',
'friendly_name': 'Alert Sensor',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.alert_sensor',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'on',
})
# ---
# name: test_sensors[hub][binary_sensor.hub_drop_1_c0ffee_leak_detected-entry] # name: test_sensors[hub][binary_sensor.hub_drop_1_c0ffee_leak_detected-entry]
EntityRegistryEntrySnapshot({ EntityRegistryEntrySnapshot({
'aliases': set({ 'aliases': set({

View file

@ -1,4 +1,68 @@
# serializer version: 1 # serializer version: 1
# name: test_sensors[alert][sensor.alert_battery-data]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'battery',
'friendly_name': 'Alert Battery',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
}),
'context': <ANY>,
'entity_id': 'sensor.alert_battery',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '100',
})
# ---
# name: test_sensors[alert][sensor.alert_battery-reset]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'battery',
'friendly_name': 'Alert Battery',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
}),
'context': <ANY>,
'entity_id': 'sensor.alert_battery',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '0',
})
# ---
# name: test_sensors[alert][sensor.alert_temperature-data]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'temperature',
'friendly_name': 'Alert Temperature',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
}),
'context': <ANY>,
'entity_id': 'sensor.alert_temperature',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '20.1111111111111',
})
# ---
# name: test_sensors[alert][sensor.alert_temperature-reset]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'temperature',
'friendly_name': 'Alert Temperature',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
}),
'context': <ANY>,
'entity_id': 'sensor.alert_temperature',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '-17.7777777777778',
})
# ---
# name: test_sensors[filter][sensor.filter_battery-data] # name: test_sensors[filter][sensor.filter_battery-data]
StateSnapshot({ StateSnapshot({
'attributes': ReadOnlyDict({ 'attributes': ReadOnlyDict({

View file

@ -10,6 +10,9 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er from homeassistant.helpers import entity_registry as er
from .common import ( from .common import (
TEST_DATA_ALERT,
TEST_DATA_ALERT_RESET,
TEST_DATA_ALERT_TOPIC,
TEST_DATA_HUB, TEST_DATA_HUB,
TEST_DATA_HUB_RESET, TEST_DATA_HUB_RESET,
TEST_DATA_HUB_TOPIC, TEST_DATA_HUB_TOPIC,
@ -28,6 +31,7 @@ from .common import (
TEST_DATA_SOFTENER, TEST_DATA_SOFTENER,
TEST_DATA_SOFTENER_RESET, TEST_DATA_SOFTENER_RESET,
TEST_DATA_SOFTENER_TOPIC, TEST_DATA_SOFTENER_TOPIC,
config_entry_alert,
config_entry_hub, config_entry_hub,
config_entry_leak, config_entry_leak,
config_entry_protection_valve, config_entry_protection_valve,
@ -44,6 +48,12 @@ from tests.typing import MqttMockHAClient
("config_entry", "topic", "reset", "data"), ("config_entry", "topic", "reset", "data"),
[ [
(config_entry_hub(), TEST_DATA_HUB_TOPIC, TEST_DATA_HUB_RESET, TEST_DATA_HUB), (config_entry_hub(), TEST_DATA_HUB_TOPIC, TEST_DATA_HUB_RESET, TEST_DATA_HUB),
(
config_entry_alert(),
TEST_DATA_ALERT_TOPIC,
TEST_DATA_ALERT_RESET,
TEST_DATA_ALERT,
),
( (
config_entry_leak(), config_entry_leak(),
TEST_DATA_LEAK_TOPIC, TEST_DATA_LEAK_TOPIC,
@ -77,6 +87,7 @@ from tests.typing import MqttMockHAClient
], ],
ids=[ ids=[
"hub", "hub",
"alert",
"leak", "leak",
"softener", "softener",
"protection_valve", "protection_valve",

View file

@ -11,6 +11,9 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er from homeassistant.helpers import entity_registry as er
from .common import ( from .common import (
TEST_DATA_ALERT,
TEST_DATA_ALERT_RESET,
TEST_DATA_ALERT_TOPIC,
TEST_DATA_FILTER, TEST_DATA_FILTER,
TEST_DATA_FILTER_RESET, TEST_DATA_FILTER_RESET,
TEST_DATA_FILTER_TOPIC, TEST_DATA_FILTER_TOPIC,
@ -32,6 +35,7 @@ from .common import (
TEST_DATA_SOFTENER, TEST_DATA_SOFTENER,
TEST_DATA_SOFTENER_RESET, TEST_DATA_SOFTENER_RESET,
TEST_DATA_SOFTENER_TOPIC, TEST_DATA_SOFTENER_TOPIC,
config_entry_alert,
config_entry_filter, config_entry_filter,
config_entry_hub, config_entry_hub,
config_entry_leak, config_entry_leak,
@ -57,6 +61,12 @@ def only_sensor_platform() -> Generator[None]:
("config_entry", "topic", "reset", "data"), ("config_entry", "topic", "reset", "data"),
[ [
(config_entry_hub(), TEST_DATA_HUB_TOPIC, TEST_DATA_HUB_RESET, TEST_DATA_HUB), (config_entry_hub(), TEST_DATA_HUB_TOPIC, TEST_DATA_HUB_RESET, TEST_DATA_HUB),
(
config_entry_alert(),
TEST_DATA_ALERT_TOPIC,
TEST_DATA_ALERT_RESET,
TEST_DATA_ALERT,
),
( (
config_entry_leak(), config_entry_leak(),
TEST_DATA_LEAK_TOPIC, TEST_DATA_LEAK_TOPIC,
@ -96,6 +106,7 @@ def only_sensor_platform() -> Generator[None]:
], ],
ids=[ ids=[
"hub", "hub",
"alert",
"leak", "leak",
"softener", "softener",
"filter", "filter",