Add DROP integration (#104319)
* Add DROP integration * Remove all but one platform for first PR * Simplify initialization of hass.data[] structure * Remove unnecessary mnemonic 'DROP_' prefix from DOMAIN constants * Remove unnecessary whitespace * Clarify configuration 'confirm' step description * Remove unnecessary whitespace * Use device class where applicable * Remove unnecessary constructor and change its elements to class variables * Change base entity inheritance to CoordinatorEntity * Make sensor definitions more concise * Rename HA domain from drop to drop_connect * Remove underscores from class and function names * Remove duplicate temperature sensor * Change title capitalization * Refactor using SensorEntityDescription * Remove unnecessary intermediate dict layer * Remove generated translations file * Remove currently unused string values * Use constants in sensor definitions * Replace values with constants * Move translation keys * Remove unnecessary unique ID and config entry references * Clean up DROPEntity initialization * Clean up sensors * Rename vars and functions according to style * Remove redundant self references * Clean up DROPSensor initializer * Add missing state classes * Simplify detection of configured devices * Change entity identifiers to create device linkage * Move device_info to coordinator * Remove unnecessary properties * Correct hub device IDs * Remove redundant attribute * Replace optional UID with assert * Remove redundant attribute * Correct coordinator initialization * Fix mypy error * Move API functionality to 3rd party library * Abstract device to sensor map into a dict * Unsubscribe MQTT on unload * Move entity device information * Make type checking for mypy conditional * Bump dropmqttapi to 1.0.1 * Freeze dataclass to match parent class * Fix race condition in MQTT unsubscribe setup * Ensure unit tests begin with invalid MQTT state * Change unit tests to reflect device firmware * Move MQTT subscription out of the coordinator * Tidy up initializer * Move entirety of MQTT subscription out of the coordinator * Make drop_api a class property * Remove unnecessary type checks * Simplify some unit test asserts * Remove argument matching default * Add entity category to battery and cartridge life sensorspull/106241/head
parent
243ee2247b
commit
fce1b6d248
|
@ -297,6 +297,8 @@ build.json @home-assistant/supervisor
|
|||
/tests/components/dormakaba_dkey/ @emontnemery
|
||||
/homeassistant/components/dremel_3d_printer/ @tkdrob
|
||||
/tests/components/dremel_3d_printer/ @tkdrob
|
||||
/homeassistant/components/drop_connect/ @ChandlerSystems @pfrazer
|
||||
/tests/components/drop_connect/ @ChandlerSystems @pfrazer
|
||||
/homeassistant/components/dsmr/ @Robbie1221 @frenck
|
||||
/tests/components/dsmr/ @Robbie1221 @frenck
|
||||
/homeassistant/components/dsmr_reader/ @depl0y @glodenox
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
"""The drop_connect integration."""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from homeassistant.components import mqtt
|
||||
from homeassistant.components.mqtt import ReceiveMessage
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
|
||||
from .const import CONF_DATA_TOPIC, CONF_DEVICE_TYPE, DOMAIN
|
||||
from .coordinator import DROPDeviceDataUpdateCoordinator
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
PLATFORMS: list[Platform] = [Platform.SENSOR]
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
|
||||
"""Set up DROP from a config entry."""
|
||||
|
||||
# Make sure MQTT integration is enabled and the client is available.
|
||||
if not await mqtt.async_wait_for_mqtt_client(hass):
|
||||
_LOGGER.error("MQTT integration is not available")
|
||||
return False
|
||||
|
||||
if TYPE_CHECKING:
|
||||
assert config_entry.unique_id is not None
|
||||
drop_data_coordinator = DROPDeviceDataUpdateCoordinator(
|
||||
hass, config_entry.unique_id
|
||||
)
|
||||
|
||||
@callback
|
||||
def mqtt_callback(msg: ReceiveMessage) -> None:
|
||||
"""Pass MQTT payload to DROP API parser."""
|
||||
if drop_data_coordinator.drop_api.parse_drop_message(
|
||||
msg.topic, msg.payload, msg.qos, msg.retain
|
||||
):
|
||||
drop_data_coordinator.async_set_updated_data(None)
|
||||
|
||||
config_entry.async_on_unload(
|
||||
await mqtt.async_subscribe(
|
||||
hass, config_entry.data[CONF_DATA_TOPIC], mqtt_callback
|
||||
)
|
||||
)
|
||||
_LOGGER.debug(
|
||||
"Entry %s (%s) subscribed to %s",
|
||||
config_entry.unique_id,
|
||||
config_entry.data[CONF_DEVICE_TYPE],
|
||||
config_entry.data[CONF_DATA_TOPIC],
|
||||
)
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})[config_entry.entry_id] = drop_data_coordinator
|
||||
await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS)
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(
|
||||
config_entry, PLATFORMS
|
||||
):
|
||||
hass.data[DOMAIN].pop(config_entry.entry_id)
|
||||
return unload_ok
|
|
@ -0,0 +1,98 @@
|
|||
"""Config flow for drop_connect integration."""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from dropmqttapi.discovery import DropDiscovery
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.data_entry_flow import FlowResult
|
||||
from homeassistant.helpers.service_info.mqtt import MqttServiceInfo
|
||||
|
||||
from .const import (
|
||||
CONF_COMMAND_TOPIC,
|
||||
CONF_DATA_TOPIC,
|
||||
CONF_DEVICE_DESC,
|
||||
CONF_DEVICE_ID,
|
||||
CONF_DEVICE_NAME,
|
||||
CONF_DEVICE_OWNER_ID,
|
||||
CONF_DEVICE_TYPE,
|
||||
CONF_HUB_ID,
|
||||
DISCOVERY_TOPIC,
|
||||
DOMAIN,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class FlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
"""Handle DROP config flow."""
|
||||
|
||||
VERSION = 1
|
||||
|
||||
_drop_discovery: DropDiscovery | None = None
|
||||
|
||||
async def async_step_mqtt(self, discovery_info: MqttServiceInfo) -> FlowResult:
|
||||
"""Handle a flow initialized by MQTT discovery."""
|
||||
|
||||
# Abort if the topic does not match our discovery topic or the payload is empty.
|
||||
if (
|
||||
discovery_info.subscribed_topic != DISCOVERY_TOPIC
|
||||
or not discovery_info.payload
|
||||
):
|
||||
return self.async_abort(reason="invalid_discovery_info")
|
||||
|
||||
self._drop_discovery = DropDiscovery(DOMAIN)
|
||||
if not (
|
||||
await self._drop_discovery.parse_discovery(
|
||||
discovery_info.topic, discovery_info.payload
|
||||
)
|
||||
):
|
||||
return self.async_abort(reason="invalid_discovery_info")
|
||||
existing_entry = await self.async_set_unique_id(
|
||||
f"{self._drop_discovery.hub_id}_{self._drop_discovery.device_id}"
|
||||
)
|
||||
if existing_entry is not None:
|
||||
# Note: returning "invalid_discovery_info" here instead of "already_configured"
|
||||
# allows discovery of additional device types.
|
||||
return self.async_abort(reason="invalid_discovery_info")
|
||||
|
||||
self.context.update({"title_placeholders": {"name": self._drop_discovery.name}})
|
||||
|
||||
return await self.async_step_confirm()
|
||||
|
||||
async def async_step_confirm(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> FlowResult:
|
||||
"""Confirm the setup."""
|
||||
if TYPE_CHECKING:
|
||||
assert self._drop_discovery is not None
|
||||
if user_input is not None:
|
||||
device_data = {
|
||||
CONF_COMMAND_TOPIC: self._drop_discovery.command_topic,
|
||||
CONF_DATA_TOPIC: self._drop_discovery.data_topic,
|
||||
CONF_DEVICE_DESC: self._drop_discovery.device_desc,
|
||||
CONF_DEVICE_ID: self._drop_discovery.device_id,
|
||||
CONF_DEVICE_NAME: self._drop_discovery.name,
|
||||
CONF_DEVICE_TYPE: self._drop_discovery.device_type,
|
||||
CONF_HUB_ID: self._drop_discovery.hub_id,
|
||||
CONF_DEVICE_OWNER_ID: self._drop_discovery.owner_id,
|
||||
}
|
||||
return self.async_create_entry(
|
||||
title=self._drop_discovery.name, data=device_data
|
||||
)
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="confirm",
|
||||
description_placeholders={
|
||||
"device_name": self._drop_discovery.name,
|
||||
"device_type": self._drop_discovery.device_desc,
|
||||
},
|
||||
)
|
||||
|
||||
async def async_step_user(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> FlowResult:
|
||||
"""Handle a flow initialized by the user."""
|
||||
return self.async_abort(reason="not_supported")
|
|
@ -0,0 +1,25 @@
|
|||
"""Constants for the drop_connect integration."""
|
||||
|
||||
# Keys for values used in the config_entry data dictionary
|
||||
CONF_COMMAND_TOPIC = "drop_command_topic"
|
||||
CONF_DATA_TOPIC = "drop_data_topic"
|
||||
CONF_DEVICE_DESC = "device_desc"
|
||||
CONF_DEVICE_ID = "device_id"
|
||||
CONF_DEVICE_TYPE = "device_type"
|
||||
CONF_HUB_ID = "drop_hub_id"
|
||||
CONF_DEVICE_NAME = "name"
|
||||
CONF_DEVICE_OWNER_ID = "drop_device_owner_id"
|
||||
|
||||
# Values for DROP device types
|
||||
DEV_FILTER = "filt"
|
||||
DEV_HUB = "hub"
|
||||
DEV_LEAK_DETECTOR = "leak"
|
||||
DEV_PROTECTION_VALVE = "pv"
|
||||
DEV_PUMP_CONTROLLER = "pc"
|
||||
DEV_RO_FILTER = "ro"
|
||||
DEV_SALT_SENSOR = "salt"
|
||||
DEV_SOFTENER = "soft"
|
||||
|
||||
DISCOVERY_TOPIC = "drop_connect/discovery/#"
|
||||
|
||||
DOMAIN = "drop_connect"
|
|
@ -0,0 +1,25 @@
|
|||
"""DROP device data update coordinator object."""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from dropmqttapi.mqttapi import DropAPI
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||
|
||||
from .const import DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DROPDeviceDataUpdateCoordinator(DataUpdateCoordinator):
|
||||
"""DROP device object."""
|
||||
|
||||
config_entry: ConfigEntry
|
||||
|
||||
def __init__(self, hass: HomeAssistant, unique_id: str) -> None:
|
||||
"""Initialize the device."""
|
||||
super().__init__(hass, _LOGGER, name=f"{DOMAIN}-{unique_id}")
|
||||
self.drop_api = DropAPI()
|
|
@ -0,0 +1,53 @@
|
|||
"""Base entity class for DROP entities."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import (
|
||||
CONF_DEVICE_DESC,
|
||||
CONF_DEVICE_NAME,
|
||||
CONF_DEVICE_OWNER_ID,
|
||||
CONF_DEVICE_TYPE,
|
||||
CONF_HUB_ID,
|
||||
DEV_HUB,
|
||||
DOMAIN,
|
||||
)
|
||||
from .coordinator import DROPDeviceDataUpdateCoordinator
|
||||
|
||||
|
||||
class DROPEntity(CoordinatorEntity[DROPDeviceDataUpdateCoordinator]):
|
||||
"""Representation of a DROP device entity."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
|
||||
def __init__(
|
||||
self, entity_type: str, coordinator: DROPDeviceDataUpdateCoordinator
|
||||
) -> None:
|
||||
"""Init DROP entity."""
|
||||
super().__init__(coordinator)
|
||||
if TYPE_CHECKING:
|
||||
assert coordinator.config_entry.unique_id is not None
|
||||
unique_id = coordinator.config_entry.unique_id
|
||||
self._attr_unique_id = f"{unique_id}_{entity_type}"
|
||||
entry_data = coordinator.config_entry.data
|
||||
model: str = entry_data[CONF_DEVICE_DESC]
|
||||
if entry_data[CONF_DEVICE_TYPE] == DEV_HUB:
|
||||
model = f"Hub {entry_data[CONF_HUB_ID]}"
|
||||
self._attr_device_info = DeviceInfo(
|
||||
manufacturer="Chandler Systems, Inc.",
|
||||
model=model,
|
||||
name=entry_data[CONF_DEVICE_NAME],
|
||||
identifiers={(DOMAIN, unique_id)},
|
||||
)
|
||||
if entry_data[CONF_DEVICE_TYPE] != DEV_HUB:
|
||||
self._attr_device_info.update(
|
||||
{
|
||||
"via_device": (
|
||||
DOMAIN,
|
||||
entry_data[CONF_DEVICE_OWNER_ID],
|
||||
)
|
||||
}
|
||||
)
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"domain": "drop_connect",
|
||||
"name": "DROP",
|
||||
"codeowners": ["@ChandlerSystems", "@pfrazer"],
|
||||
"config_flow": true,
|
||||
"dependencies": ["mqtt"],
|
||||
"documentation": "https://www.home-assistant.io/integrations/drop_connect",
|
||||
"iot_class": "local_push",
|
||||
"mqtt": ["drop_connect/discovery/#"],
|
||||
"requirements": ["dropmqttapi==1.0.1"]
|
||||
}
|
|
@ -0,0 +1,285 @@
|
|||
"""Support for DROP sensors."""
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
import logging
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
SensorDeviceClass,
|
||||
SensorEntity,
|
||||
SensorEntityDescription,
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
CONCENTRATION_PARTS_PER_MILLION,
|
||||
PERCENTAGE,
|
||||
EntityCategory,
|
||||
UnitOfPressure,
|
||||
UnitOfTemperature,
|
||||
UnitOfVolume,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import (
|
||||
CONF_DEVICE_TYPE,
|
||||
DEV_FILTER,
|
||||
DEV_HUB,
|
||||
DEV_LEAK_DETECTOR,
|
||||
DEV_PROTECTION_VALVE,
|
||||
DEV_PUMP_CONTROLLER,
|
||||
DEV_RO_FILTER,
|
||||
DEV_SOFTENER,
|
||||
DOMAIN,
|
||||
)
|
||||
from .coordinator import DROPDeviceDataUpdateCoordinator
|
||||
from .entity import DROPEntity
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
FLOW_ICON = "mdi:shower-head"
|
||||
GAUGE_ICON = "mdi:gauge"
|
||||
TDS_ICON = "mdi:water-opacity"
|
||||
|
||||
# Sensor type constants
|
||||
CURRENT_FLOW_RATE = "current_flow_rate"
|
||||
PEAK_FLOW_RATE = "peak_flow_rate"
|
||||
WATER_USED_TODAY = "water_used_today"
|
||||
AVERAGE_WATER_USED = "average_water_used"
|
||||
CAPACITY_REMAINING = "capacity_remaining"
|
||||
CURRENT_SYSTEM_PRESSURE = "current_system_pressure"
|
||||
HIGH_SYSTEM_PRESSURE = "high_system_pressure"
|
||||
LOW_SYSTEM_PRESSURE = "low_system_pressure"
|
||||
BATTERY = "battery"
|
||||
TEMPERATURE = "temperature"
|
||||
INLET_TDS = "inlet_tds"
|
||||
OUTLET_TDS = "outlet_tds"
|
||||
CARTRIDGE_1_LIFE = "cart1"
|
||||
CARTRIDGE_2_LIFE = "cart2"
|
||||
CARTRIDGE_3_LIFE = "cart3"
|
||||
|
||||
|
||||
@dataclass(kw_only=True, frozen=True)
|
||||
class DROPSensorEntityDescription(SensorEntityDescription):
|
||||
"""Describes DROP sensor entity."""
|
||||
|
||||
value_fn: Callable[[DROPDeviceDataUpdateCoordinator], float | int | None]
|
||||
|
||||
|
||||
SENSORS: list[DROPSensorEntityDescription] = [
|
||||
DROPSensorEntityDescription(
|
||||
key=CURRENT_FLOW_RATE,
|
||||
translation_key=CURRENT_FLOW_RATE,
|
||||
icon="mdi:shower-head",
|
||||
native_unit_of_measurement="gpm",
|
||||
suggested_display_precision=1,
|
||||
value_fn=lambda device: device.drop_api.current_flow_rate(),
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
DROPSensorEntityDescription(
|
||||
key=PEAK_FLOW_RATE,
|
||||
translation_key=PEAK_FLOW_RATE,
|
||||
icon="mdi:shower-head",
|
||||
native_unit_of_measurement="gpm",
|
||||
suggested_display_precision=1,
|
||||
value_fn=lambda device: device.drop_api.peak_flow_rate(),
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
DROPSensorEntityDescription(
|
||||
key=WATER_USED_TODAY,
|
||||
translation_key=WATER_USED_TODAY,
|
||||
device_class=SensorDeviceClass.WATER,
|
||||
native_unit_of_measurement=UnitOfVolume.GALLONS,
|
||||
suggested_display_precision=1,
|
||||
value_fn=lambda device: device.drop_api.water_used_today(),
|
||||
state_class=SensorStateClass.TOTAL,
|
||||
),
|
||||
DROPSensorEntityDescription(
|
||||
key=AVERAGE_WATER_USED,
|
||||
translation_key=AVERAGE_WATER_USED,
|
||||
device_class=SensorDeviceClass.WATER,
|
||||
native_unit_of_measurement=UnitOfVolume.GALLONS,
|
||||
suggested_display_precision=0,
|
||||
value_fn=lambda device: device.drop_api.average_water_used(),
|
||||
state_class=SensorStateClass.TOTAL,
|
||||
),
|
||||
DROPSensorEntityDescription(
|
||||
key=CAPACITY_REMAINING,
|
||||
translation_key=CAPACITY_REMAINING,
|
||||
device_class=SensorDeviceClass.WATER,
|
||||
native_unit_of_measurement=UnitOfVolume.GALLONS,
|
||||
suggested_display_precision=0,
|
||||
value_fn=lambda device: device.drop_api.capacity_remaining(),
|
||||
state_class=SensorStateClass.TOTAL,
|
||||
),
|
||||
DROPSensorEntityDescription(
|
||||
key=CURRENT_SYSTEM_PRESSURE,
|
||||
translation_key=CURRENT_SYSTEM_PRESSURE,
|
||||
device_class=SensorDeviceClass.PRESSURE,
|
||||
native_unit_of_measurement=UnitOfPressure.PSI,
|
||||
suggested_display_precision=1,
|
||||
value_fn=lambda device: device.drop_api.current_system_pressure(),
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
DROPSensorEntityDescription(
|
||||
key=HIGH_SYSTEM_PRESSURE,
|
||||
translation_key=HIGH_SYSTEM_PRESSURE,
|
||||
device_class=SensorDeviceClass.PRESSURE,
|
||||
native_unit_of_measurement=UnitOfPressure.PSI,
|
||||
suggested_display_precision=0,
|
||||
value_fn=lambda device: device.drop_api.high_system_pressure(),
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
DROPSensorEntityDescription(
|
||||
key=LOW_SYSTEM_PRESSURE,
|
||||
translation_key=LOW_SYSTEM_PRESSURE,
|
||||
device_class=SensorDeviceClass.PRESSURE,
|
||||
native_unit_of_measurement=UnitOfPressure.PSI,
|
||||
suggested_display_precision=0,
|
||||
value_fn=lambda device: device.drop_api.low_system_pressure(),
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
DROPSensorEntityDescription(
|
||||
key=BATTERY,
|
||||
device_class=SensorDeviceClass.BATTERY,
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
suggested_display_precision=0,
|
||||
value_fn=lambda device: device.drop_api.battery(),
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
),
|
||||
DROPSensorEntityDescription(
|
||||
key=TEMPERATURE,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT,
|
||||
suggested_display_precision=1,
|
||||
value_fn=lambda device: device.drop_api.temperature(),
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
DROPSensorEntityDescription(
|
||||
key=INLET_TDS,
|
||||
translation_key=INLET_TDS,
|
||||
icon=TDS_ICON,
|
||||
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
suggested_display_precision=0,
|
||||
value_fn=lambda device: device.drop_api.inlet_tds(),
|
||||
),
|
||||
DROPSensorEntityDescription(
|
||||
key=OUTLET_TDS,
|
||||
translation_key=OUTLET_TDS,
|
||||
icon=TDS_ICON,
|
||||
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
suggested_display_precision=0,
|
||||
value_fn=lambda device: device.drop_api.outlet_tds(),
|
||||
),
|
||||
DROPSensorEntityDescription(
|
||||
key=CARTRIDGE_1_LIFE,
|
||||
translation_key=CARTRIDGE_1_LIFE,
|
||||
icon=GAUGE_ICON,
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
suggested_display_precision=0,
|
||||
value_fn=lambda device: device.drop_api.cart1(),
|
||||
),
|
||||
DROPSensorEntityDescription(
|
||||
key=CARTRIDGE_2_LIFE,
|
||||
translation_key=CARTRIDGE_2_LIFE,
|
||||
icon=GAUGE_ICON,
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
suggested_display_precision=0,
|
||||
value_fn=lambda device: device.drop_api.cart2(),
|
||||
),
|
||||
DROPSensorEntityDescription(
|
||||
key=CARTRIDGE_3_LIFE,
|
||||
translation_key=CARTRIDGE_3_LIFE,
|
||||
icon=GAUGE_ICON,
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
suggested_display_precision=0,
|
||||
value_fn=lambda device: device.drop_api.cart3(),
|
||||
),
|
||||
]
|
||||
|
||||
# Defines which sensors are used by each device type
|
||||
DEVICE_SENSORS: dict[str, list[str]] = {
|
||||
DEV_HUB: [
|
||||
AVERAGE_WATER_USED,
|
||||
BATTERY,
|
||||
CURRENT_FLOW_RATE,
|
||||
CURRENT_SYSTEM_PRESSURE,
|
||||
HIGH_SYSTEM_PRESSURE,
|
||||
LOW_SYSTEM_PRESSURE,
|
||||
PEAK_FLOW_RATE,
|
||||
WATER_USED_TODAY,
|
||||
],
|
||||
DEV_SOFTENER: [
|
||||
BATTERY,
|
||||
CAPACITY_REMAINING,
|
||||
CURRENT_FLOW_RATE,
|
||||
CURRENT_SYSTEM_PRESSURE,
|
||||
],
|
||||
DEV_FILTER: [BATTERY, CURRENT_FLOW_RATE, CURRENT_SYSTEM_PRESSURE],
|
||||
DEV_LEAK_DETECTOR: [BATTERY, TEMPERATURE],
|
||||
DEV_PROTECTION_VALVE: [
|
||||
BATTERY,
|
||||
CURRENT_FLOW_RATE,
|
||||
CURRENT_SYSTEM_PRESSURE,
|
||||
TEMPERATURE,
|
||||
],
|
||||
DEV_PUMP_CONTROLLER: [CURRENT_FLOW_RATE, CURRENT_SYSTEM_PRESSURE, TEMPERATURE],
|
||||
DEV_RO_FILTER: [
|
||||
CARTRIDGE_1_LIFE,
|
||||
CARTRIDGE_2_LIFE,
|
||||
CARTRIDGE_3_LIFE,
|
||||
INLET_TDS,
|
||||
OUTLET_TDS,
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the DROP sensors from config entry."""
|
||||
_LOGGER.debug(
|
||||
"Set up sensor for device type %s with entry_id is %s",
|
||||
config_entry.data[CONF_DEVICE_TYPE],
|
||||
config_entry.entry_id,
|
||||
)
|
||||
|
||||
if config_entry.data[CONF_DEVICE_TYPE] in DEVICE_SENSORS:
|
||||
async_add_entities(
|
||||
DROPSensor(hass.data[DOMAIN][config_entry.entry_id], sensor)
|
||||
for sensor in SENSORS
|
||||
if sensor.key in DEVICE_SENSORS[config_entry.data[CONF_DEVICE_TYPE]]
|
||||
)
|
||||
|
||||
|
||||
class DROPSensor(DROPEntity, SensorEntity):
|
||||
"""Representation of a DROP sensor."""
|
||||
|
||||
entity_description: DROPSensorEntityDescription
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: DROPDeviceDataUpdateCoordinator,
|
||||
entity_description: DROPSensorEntityDescription,
|
||||
) -> None:
|
||||
"""Initialize the sensor."""
|
||||
super().__init__(entity_description.key, coordinator)
|
||||
self.entity_description = entity_description
|
||||
|
||||
@property
|
||||
def native_value(self) -> float | int | None:
|
||||
"""Return the value reported by the sensor."""
|
||||
return self.entity_description.value_fn(self.coordinator)
|
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"not_supported": "Configuration for DROP is through MQTT discovery. Use the DROP Connect app to connect your DROP Hub to your MQTT broker."
|
||||
},
|
||||
"step": {
|
||||
"confirm": {
|
||||
"title": "Confirm association",
|
||||
"description": "Do you want to configure the DROP {device_type} named {device_name}?'"
|
||||
}
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"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" },
|
||||
"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" },
|
||||
"cart2": { "name": "Cartridge 2 life remaining" },
|
||||
"cart3": { "name": "Cartridge 3 life remaining" }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -111,6 +111,7 @@ FLOWS = {
|
|||
"doorbird",
|
||||
"dormakaba_dkey",
|
||||
"dremel_3d_printer",
|
||||
"drop_connect",
|
||||
"dsmr",
|
||||
"dsmr_reader",
|
||||
"dunehd",
|
||||
|
|
|
@ -1253,6 +1253,12 @@
|
|||
"config_flow": true,
|
||||
"iot_class": "local_polling"
|
||||
},
|
||||
"drop_connect": {
|
||||
"name": "DROP",
|
||||
"integration_type": "hub",
|
||||
"config_flow": true,
|
||||
"iot_class": "local_push"
|
||||
},
|
||||
"dsmr": {
|
||||
"name": "DSMR Slimme Meter",
|
||||
"integration_type": "hub",
|
||||
|
|
|
@ -4,6 +4,9 @@ To update, run python3 -m script.hassfest
|
|||
"""
|
||||
|
||||
MQTT = {
|
||||
"drop_connect": [
|
||||
"drop_connect/discovery/#",
|
||||
],
|
||||
"dsmr_reader": [
|
||||
"dsmr/#",
|
||||
],
|
||||
|
|
|
@ -712,6 +712,9 @@ dovado==0.4.1
|
|||
# homeassistant.components.dremel_3d_printer
|
||||
dremel3dpy==2.1.1
|
||||
|
||||
# homeassistant.components.drop_connect
|
||||
dropmqttapi==1.0.1
|
||||
|
||||
# homeassistant.components.dsmr
|
||||
dsmr-parser==1.3.1
|
||||
|
||||
|
|
|
@ -581,6 +581,9 @@ discovery30303==0.2.1
|
|||
# homeassistant.components.dremel_3d_printer
|
||||
dremel3dpy==2.1.1
|
||||
|
||||
# homeassistant.components.drop_connect
|
||||
dropmqttapi==1.0.1
|
||||
|
||||
# homeassistant.components.dsmr
|
||||
dsmr-parser==1.3.1
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
"""Tests for the DROP integration."""
|
|
@ -0,0 +1,51 @@
|
|||
"""Define common test values."""
|
||||
|
||||
TEST_DATA_HUB_TOPIC = "drop_connect/DROP-1_C0FFEE/255"
|
||||
TEST_DATA_HUB = (
|
||||
'{"curFlow":5.77,"peakFlow":13.8,"usedToday":232.77,"avgUsed":76,"psi":62.2,"psiLow":61,"psiHigh":62,'
|
||||
'"water":1,"bypass":0,"pMode":"HOME","battery":50,"notif":1,"leak":0}'
|
||||
)
|
||||
TEST_DATA_HUB_RESET = (
|
||||
'{"curFlow":0,"peakFlow":0,"usedToday":0,"avgUsed":0,"psi":0,"psiLow":0,"psiHigh":0,'
|
||||
'"water":0,"bypass":0,"pMode":"AWAY","battery":0,"notif":0,"leak":0}'
|
||||
)
|
||||
|
||||
TEST_DATA_SALT_TOPIC = "drop_connect/DROP-1_C0FFEE/8"
|
||||
TEST_DATA_SALT = '{"salt":1}'
|
||||
TEST_DATA_SALT_RESET = '{"salt":0}'
|
||||
|
||||
TEST_DATA_LEAK_TOPIC = "drop_connect/DROP-1_C0FFEE/20"
|
||||
TEST_DATA_LEAK = '{"battery":100,"leak":1,"temp":68.2}'
|
||||
TEST_DATA_LEAK_RESET = '{"battery":0,"leak":0,"temp":0}'
|
||||
|
||||
TEST_DATA_SOFTENER_TOPIC = "drop_connect/DROP-1_C0FFEE/0"
|
||||
TEST_DATA_SOFTENER = (
|
||||
'{"curFlow":5.0,"bypass":0,"battery":20,"capacity":1000,"resInUse":1,"psi":50.5}'
|
||||
)
|
||||
TEST_DATA_SOFTENER_RESET = (
|
||||
'{"curFlow":0,"bypass":0,"battery":0,"capacity":0,"resInUse":0,"psi":null}'
|
||||
)
|
||||
|
||||
TEST_DATA_FILTER_TOPIC = "drop_connect/DROP-1_C0FFEE/4"
|
||||
TEST_DATA_FILTER = '{"curFlow":19.84,"bypass":0,"battery":12,"psi":38.2}'
|
||||
TEST_DATA_FILTER_RESET = '{"curFlow":0,"bypass":0,"battery":0,"psi":null}'
|
||||
|
||||
TEST_DATA_PROTECTION_VALVE_TOPIC = "drop_connect/DROP-1_C0FFEE/78"
|
||||
TEST_DATA_PROTECTION_VALVE = (
|
||||
'{"curFlow":7.1,"psi":61.3,"water":1,"battery":0,"leak":1,"temp":70.5}'
|
||||
)
|
||||
TEST_DATA_PROTECTION_VALVE_RESET = (
|
||||
'{"curFlow":0,"psi":0,"water":0,"battery":0,"leak":0,"temp":0}'
|
||||
)
|
||||
|
||||
TEST_DATA_PUMP_CONTROLLER_TOPIC = "drop_connect/DROP-1_C0FFEE/83"
|
||||
TEST_DATA_PUMP_CONTROLLER = '{"curFlow":2.2,"psi":62.2,"pump":1,"leak":1,"temp":68.8}'
|
||||
TEST_DATA_PUMP_CONTROLLER_RESET = '{"curFlow":0,"psi":0,"pump":0,"leak":0,"temp":0}'
|
||||
|
||||
TEST_DATA_RO_FILTER_TOPIC = "drop_connect/DROP-1_C0FFEE/95"
|
||||
TEST_DATA_RO_FILTER = (
|
||||
'{"leak":1,"tdsIn":164,"tdsOut":9,"cart1":59,"cart2":80,"cart3":59}'
|
||||
)
|
||||
TEST_DATA_RO_FILTER_RESET = (
|
||||
'{"leak":0,"tdsIn":0,"tdsOut":0,"cart1":0,"cart2":0,"cart3":0}'
|
||||
)
|
|
@ -0,0 +1,177 @@
|
|||
"""Define fixtures available for all tests."""
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.drop_connect.const import (
|
||||
CONF_COMMAND_TOPIC,
|
||||
CONF_DATA_TOPIC,
|
||||
CONF_DEVICE_DESC,
|
||||
CONF_DEVICE_ID,
|
||||
CONF_DEVICE_NAME,
|
||||
CONF_DEVICE_OWNER_ID,
|
||||
CONF_DEVICE_TYPE,
|
||||
CONF_HUB_ID,
|
||||
DOMAIN,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def config_entry_hub(hass: HomeAssistant):
|
||||
"""Config entry version 1 fixture."""
|
||||
return MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
unique_id="DROP-1_C0FFEE_255",
|
||||
data={
|
||||
CONF_COMMAND_TOPIC: "drop_connect/DROP-1_C0FFEE/255/cmd",
|
||||
CONF_DATA_TOPIC: "drop_connect/DROP-1_C0FFEE/255/#",
|
||||
CONF_DEVICE_DESC: "Hub",
|
||||
CONF_DEVICE_ID: 255,
|
||||
CONF_DEVICE_NAME: "Hub DROP-1_C0FFEE",
|
||||
CONF_DEVICE_TYPE: "hub",
|
||||
CONF_HUB_ID: "DROP-1_C0FFEE",
|
||||
CONF_DEVICE_OWNER_ID: "DROP-1_C0FFEE_255",
|
||||
},
|
||||
version=1,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def config_entry_salt(hass: HomeAssistant):
|
||||
"""Config entry version 1 fixture."""
|
||||
return MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
unique_id="DROP-1_C0FFEE_8",
|
||||
data={
|
||||
CONF_COMMAND_TOPIC: "drop_connect/DROP-1_C0FFEE/8/cmd",
|
||||
CONF_DATA_TOPIC: "drop_connect/DROP-1_C0FFEE/8/#",
|
||||
CONF_DEVICE_DESC: "Salt Sensor",
|
||||
CONF_DEVICE_ID: 8,
|
||||
CONF_DEVICE_NAME: "Salt Sensor",
|
||||
CONF_DEVICE_TYPE: "salt",
|
||||
CONF_HUB_ID: "DROP-1_C0FFEE",
|
||||
CONF_DEVICE_OWNER_ID: "DROP-1_C0FFEE_255",
|
||||
},
|
||||
version=1,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def config_entry_leak(hass: HomeAssistant):
|
||||
"""Config entry version 1 fixture."""
|
||||
return MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
unique_id="DROP-1_C0FFEE_20",
|
||||
data={
|
||||
CONF_COMMAND_TOPIC: "drop_connect/DROP-1_C0FFEE/20/cmd",
|
||||
CONF_DATA_TOPIC: "drop_connect/DROP-1_C0FFEE/20/#",
|
||||
CONF_DEVICE_DESC: "Leak Detector",
|
||||
CONF_DEVICE_ID: 20,
|
||||
CONF_DEVICE_NAME: "Leak Detector",
|
||||
CONF_DEVICE_TYPE: "leak",
|
||||
CONF_HUB_ID: "DROP-1_C0FFEE",
|
||||
CONF_DEVICE_OWNER_ID: "DROP-1_C0FFEE_255",
|
||||
},
|
||||
version=1,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def config_entry_softener(hass: HomeAssistant):
|
||||
"""Config entry version 1 fixture."""
|
||||
return MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
unique_id="DROP-1_C0FFEE_0",
|
||||
data={
|
||||
CONF_COMMAND_TOPIC: "drop_connect/DROP-1_C0FFEE/0/cmd",
|
||||
CONF_DATA_TOPIC: "drop_connect/DROP-1_C0FFEE/0/#",
|
||||
CONF_DEVICE_DESC: "Softener",
|
||||
CONF_DEVICE_ID: 0,
|
||||
CONF_DEVICE_NAME: "Softener",
|
||||
CONF_DEVICE_TYPE: "soft",
|
||||
CONF_HUB_ID: "DROP-1_C0FFEE",
|
||||
CONF_DEVICE_OWNER_ID: "DROP-1_C0FFEE_255",
|
||||
},
|
||||
version=1,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def config_entry_filter(hass: HomeAssistant):
|
||||
"""Config entry version 1 fixture."""
|
||||
return MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
unique_id="DROP-1_C0FFEE_4",
|
||||
data={
|
||||
CONF_COMMAND_TOPIC: "drop_connect/DROP-1_C0FFEE/4/cmd",
|
||||
CONF_DATA_TOPIC: "drop_connect/DROP-1_C0FFEE/4/#",
|
||||
CONF_DEVICE_DESC: "Filter",
|
||||
CONF_DEVICE_ID: 4,
|
||||
CONF_DEVICE_NAME: "Filter",
|
||||
CONF_DEVICE_TYPE: "filt",
|
||||
CONF_HUB_ID: "DROP-1_C0FFEE",
|
||||
CONF_DEVICE_OWNER_ID: "DROP-1_C0FFEE_255",
|
||||
},
|
||||
version=1,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def config_entry_protection_valve(hass: HomeAssistant):
|
||||
"""Config entry version 1 fixture."""
|
||||
return MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
unique_id="DROP-1_C0FFEE_78",
|
||||
data={
|
||||
CONF_COMMAND_TOPIC: "drop_connect/DROP-1_C0FFEE/78/cmd",
|
||||
CONF_DATA_TOPIC: "drop_connect/DROP-1_C0FFEE/78/#",
|
||||
CONF_DEVICE_DESC: "Protection Valve",
|
||||
CONF_DEVICE_ID: 78,
|
||||
CONF_DEVICE_NAME: "Protection Valve",
|
||||
CONF_DEVICE_TYPE: "pv",
|
||||
CONF_HUB_ID: "DROP-1_C0FFEE",
|
||||
CONF_DEVICE_OWNER_ID: "DROP-1_C0FFEE_255",
|
||||
},
|
||||
version=1,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def config_entry_pump_controller(hass: HomeAssistant):
|
||||
"""Config entry version 1 fixture."""
|
||||
return MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
unique_id="DROP-1_C0FFEE_83",
|
||||
data={
|
||||
CONF_COMMAND_TOPIC: "drop_connect/DROP-1_C0FFEE/83/cmd",
|
||||
CONF_DATA_TOPIC: "drop_connect/DROP-1_C0FFEE/83/#",
|
||||
CONF_DEVICE_DESC: "Pump Controller",
|
||||
CONF_DEVICE_ID: 83,
|
||||
CONF_DEVICE_NAME: "Pump Controller",
|
||||
CONF_DEVICE_TYPE: "pc",
|
||||
CONF_HUB_ID: "DROP-1_C0FFEE",
|
||||
CONF_DEVICE_OWNER_ID: "DROP-1_C0FFEE_255",
|
||||
},
|
||||
version=1,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def config_entry_ro_filter(hass: HomeAssistant):
|
||||
"""Config entry version 1 fixture."""
|
||||
return MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
unique_id="DROP-1_C0FFEE_255",
|
||||
data={
|
||||
CONF_COMMAND_TOPIC: "drop_connect/DROP-1_C0FFEE/95/cmd",
|
||||
CONF_DATA_TOPIC: "drop_connect/DROP-1_C0FFEE/95/#",
|
||||
CONF_DEVICE_DESC: "RO Filter",
|
||||
CONF_DEVICE_ID: 95,
|
||||
CONF_DEVICE_NAME: "RO Filter",
|
||||
CONF_DEVICE_TYPE: "ro",
|
||||
CONF_HUB_ID: "DROP-1_C0FFEE",
|
||||
CONF_DEVICE_OWNER_ID: "DROP-1_C0FFEE_255",
|
||||
},
|
||||
version=1,
|
||||
)
|
|
@ -0,0 +1,178 @@
|
|||
"""Test config flow."""
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import FlowResultType
|
||||
from homeassistant.helpers.service_info.mqtt import MqttServiceInfo
|
||||
|
||||
from tests.typing import MqttMockHAClient
|
||||
|
||||
|
||||
async def test_mqtt_setup(hass: HomeAssistant, mqtt_mock: MqttMockHAClient) -> None:
|
||||
"""Test we can finish a config flow through MQTT with custom prefix."""
|
||||
discovery_info = MqttServiceInfo(
|
||||
topic="drop_connect/discovery/DROP-1_C0FFEE/255",
|
||||
payload='{"devDesc":"Hub","devType":"hub","name":"Hub DROP-1_C0FFEE"}',
|
||||
qos=0,
|
||||
retain=False,
|
||||
subscribed_topic="drop_connect/discovery/#",
|
||||
timestamp=None,
|
||||
)
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
"drop_connect",
|
||||
context={"source": config_entries.SOURCE_MQTT},
|
||||
data=discovery_info,
|
||||
)
|
||||
assert result is not None
|
||||
assert result["type"] == "form"
|
||||
assert result["step_id"] == "confirm"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert result is not None
|
||||
assert result["type"] == FlowResultType.CREATE_ENTRY
|
||||
assert result["data"] == {
|
||||
"drop_command_topic": "drop_connect/DROP-1_C0FFEE/cmd/255",
|
||||
"drop_data_topic": "drop_connect/DROP-1_C0FFEE/data/255/#",
|
||||
"device_desc": "Hub",
|
||||
"device_id": "255",
|
||||
"name": "Hub DROP-1_C0FFEE",
|
||||
"device_type": "hub",
|
||||
"drop_hub_id": "DROP-1_C0FFEE",
|
||||
"drop_device_owner_id": "DROP-1_C0FFEE_255",
|
||||
}
|
||||
|
||||
|
||||
async def test_duplicate(hass: HomeAssistant, mqtt_mock: MqttMockHAClient) -> None:
|
||||
"""Test we can finish a config flow through MQTT with custom prefix."""
|
||||
discovery_info = MqttServiceInfo(
|
||||
topic="drop_connect/discovery/DROP-1_C0FFEE/255",
|
||||
payload='{"devDesc":"Hub","devType":"hub","name":"Hub DROP-1_C0FFEE"}',
|
||||
qos=0,
|
||||
retain=False,
|
||||
subscribed_topic="drop_connect/discovery/#",
|
||||
timestamp=None,
|
||||
)
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
"drop_connect",
|
||||
context={"source": config_entries.SOURCE_MQTT},
|
||||
data=discovery_info,
|
||||
)
|
||||
assert result is not None
|
||||
assert result["type"] == "form"
|
||||
assert result["step_id"] == "confirm"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert result is not None
|
||||
assert result["type"] == FlowResultType.CREATE_ENTRY
|
||||
|
||||
# Attempting configuration of the same object should abort
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
"drop_connect",
|
||||
context={"source": config_entries.SOURCE_MQTT},
|
||||
data=discovery_info,
|
||||
)
|
||||
assert result is not None
|
||||
assert result["type"] == "abort"
|
||||
assert result["reason"] == "invalid_discovery_info"
|
||||
|
||||
|
||||
async def test_mqtt_setup_incomplete_payload(
|
||||
hass: HomeAssistant, mqtt_mock: MqttMockHAClient
|
||||
) -> None:
|
||||
"""Test we can finish a config flow through MQTT with custom prefix."""
|
||||
discovery_info = MqttServiceInfo(
|
||||
topic="drop_connect/discovery/DROP-1_C0FFEE/255",
|
||||
payload='{"devDesc":"Hub"}',
|
||||
qos=0,
|
||||
retain=False,
|
||||
subscribed_topic="drop_connect/discovery/#",
|
||||
timestamp=None,
|
||||
)
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
"drop_connect",
|
||||
context={"source": config_entries.SOURCE_MQTT},
|
||||
data=discovery_info,
|
||||
)
|
||||
assert result is not None
|
||||
assert result["type"] == "abort"
|
||||
assert result["reason"] == "invalid_discovery_info"
|
||||
|
||||
|
||||
async def test_mqtt_setup_bad_json(
|
||||
hass: HomeAssistant, mqtt_mock: MqttMockHAClient
|
||||
) -> None:
|
||||
"""Test we can finish a config flow through MQTT with custom prefix."""
|
||||
discovery_info = MqttServiceInfo(
|
||||
topic="drop_connect/discovery/DROP-1_C0FFEE/255",
|
||||
payload="{BAD JSON}",
|
||||
qos=0,
|
||||
retain=False,
|
||||
subscribed_topic="drop_connect/discovery/#",
|
||||
timestamp=None,
|
||||
)
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
"drop_connect",
|
||||
context={"source": config_entries.SOURCE_MQTT},
|
||||
data=discovery_info,
|
||||
)
|
||||
assert result is not None
|
||||
assert result["type"] == "abort"
|
||||
assert result["reason"] == "invalid_discovery_info"
|
||||
|
||||
|
||||
async def test_mqtt_setup_bad_topic(
|
||||
hass: HomeAssistant, mqtt_mock: MqttMockHAClient
|
||||
) -> None:
|
||||
"""Test we can finish a config flow through MQTT with custom prefix."""
|
||||
discovery_info = MqttServiceInfo(
|
||||
topic="drop_connect/discovery/FOO",
|
||||
payload=('{"devDesc":"Hub","devType":"hub","name":"Hub DROP-1_C0FFEE"}'),
|
||||
qos=0,
|
||||
retain=False,
|
||||
subscribed_topic="drop_connect/discovery/#",
|
||||
timestamp=None,
|
||||
)
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
"drop_connect",
|
||||
context={"source": config_entries.SOURCE_MQTT},
|
||||
data=discovery_info,
|
||||
)
|
||||
assert result is not None
|
||||
assert result["type"] == "abort"
|
||||
assert result["reason"] == "invalid_discovery_info"
|
||||
|
||||
|
||||
async def test_mqtt_setup_no_payload(
|
||||
hass: HomeAssistant, mqtt_mock: MqttMockHAClient
|
||||
) -> None:
|
||||
"""Test we can finish a config flow through MQTT with custom prefix."""
|
||||
discovery_info = MqttServiceInfo(
|
||||
topic="drop_connect/discovery/DROP-1_C0FFEE/255",
|
||||
payload="",
|
||||
qos=0,
|
||||
retain=False,
|
||||
subscribed_topic="drop_connect/discovery/#",
|
||||
timestamp=None,
|
||||
)
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
"drop_connect",
|
||||
context={"source": config_entries.SOURCE_MQTT},
|
||||
data=discovery_info,
|
||||
)
|
||||
assert result is not None
|
||||
assert result["type"] == "abort"
|
||||
assert result["reason"] == "invalid_discovery_info"
|
||||
|
||||
|
||||
async def test_user_setup(hass: HomeAssistant) -> None:
|
||||
"""Test user setup."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
"drop_connect", context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
assert result["type"] == "abort"
|
||||
assert result["reason"] == "not_supported"
|
|
@ -0,0 +1,74 @@
|
|||
"""Test DROP coordinator."""
|
||||
from homeassistant.components.drop_connect.const import DOMAIN
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.const import STATE_UNAVAILABLE, STATE_UNKNOWN
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from .common import TEST_DATA_HUB, TEST_DATA_HUB_RESET, TEST_DATA_HUB_TOPIC
|
||||
|
||||
from tests.common import async_fire_mqtt_message
|
||||
from tests.typing import MqttMockHAClient
|
||||
|
||||
|
||||
async def test_bad_json(
|
||||
hass: HomeAssistant, config_entry_hub, mqtt_mock: MqttMockHAClient
|
||||
) -> None:
|
||||
"""Test bad JSON."""
|
||||
config_entry_hub.add_to_hass(hass)
|
||||
assert await async_setup_component(hass, DOMAIN, {})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
current_flow_sensor_name = "sensor.hub_drop_1_c0ffee_water_flow_rate"
|
||||
hass.states.async_set(current_flow_sensor_name, STATE_UNKNOWN)
|
||||
|
||||
async_fire_mqtt_message(hass, TEST_DATA_HUB_TOPIC, "{BAD JSON}")
|
||||
await hass.async_block_till_done()
|
||||
|
||||
current_flow_sensor = hass.states.get(current_flow_sensor_name)
|
||||
assert current_flow_sensor
|
||||
assert current_flow_sensor.state == STATE_UNKNOWN
|
||||
|
||||
|
||||
async def test_unload(
|
||||
hass: HomeAssistant, config_entry_hub, mqtt_mock: MqttMockHAClient
|
||||
) -> None:
|
||||
"""Test entity unload."""
|
||||
# Load the hub device
|
||||
config_entry_hub.add_to_hass(hass)
|
||||
assert await async_setup_component(hass, DOMAIN, {})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
current_flow_sensor_name = "sensor.hub_drop_1_c0ffee_water_flow_rate"
|
||||
hass.states.async_set(current_flow_sensor_name, STATE_UNKNOWN)
|
||||
|
||||
async_fire_mqtt_message(hass, TEST_DATA_HUB_TOPIC, TEST_DATA_HUB_RESET)
|
||||
await hass.async_block_till_done()
|
||||
async_fire_mqtt_message(hass, TEST_DATA_HUB_TOPIC, TEST_DATA_HUB)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
current_flow_sensor = hass.states.get(current_flow_sensor_name)
|
||||
assert current_flow_sensor
|
||||
assert round(float(current_flow_sensor.state), 1) == 5.8
|
||||
|
||||
# Unload the device
|
||||
await hass.config_entries.async_unload(config_entry_hub.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert config_entry_hub.state is ConfigEntryState.NOT_LOADED
|
||||
|
||||
# Verify sensor is unavailable
|
||||
current_flow_sensor = hass.states.get(current_flow_sensor_name)
|
||||
assert current_flow_sensor
|
||||
assert current_flow_sensor.state == STATE_UNAVAILABLE
|
||||
|
||||
|
||||
async def test_no_mqtt(hass: HomeAssistant, config_entry_hub) -> None:
|
||||
"""Test no MQTT."""
|
||||
config_entry_hub.add_to_hass(hass)
|
||||
assert await async_setup_component(hass, DOMAIN, {})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
protect_mode_select_name = "select.hub_drop_1_c0ffee_protect_mode"
|
||||
protect_mode_select = hass.states.get(protect_mode_select_name)
|
||||
assert protect_mode_select is None
|
|
@ -0,0 +1,319 @@
|
|||
"""Test DROP sensor entities."""
|
||||
from homeassistant.components.drop_connect.const import DOMAIN
|
||||
from homeassistant.const import STATE_UNKNOWN
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from .common import (
|
||||
TEST_DATA_FILTER,
|
||||
TEST_DATA_FILTER_RESET,
|
||||
TEST_DATA_FILTER_TOPIC,
|
||||
TEST_DATA_HUB,
|
||||
TEST_DATA_HUB_RESET,
|
||||
TEST_DATA_HUB_TOPIC,
|
||||
TEST_DATA_LEAK,
|
||||
TEST_DATA_LEAK_RESET,
|
||||
TEST_DATA_LEAK_TOPIC,
|
||||
TEST_DATA_PROTECTION_VALVE,
|
||||
TEST_DATA_PROTECTION_VALVE_RESET,
|
||||
TEST_DATA_PROTECTION_VALVE_TOPIC,
|
||||
TEST_DATA_PUMP_CONTROLLER,
|
||||
TEST_DATA_PUMP_CONTROLLER_RESET,
|
||||
TEST_DATA_PUMP_CONTROLLER_TOPIC,
|
||||
TEST_DATA_RO_FILTER,
|
||||
TEST_DATA_RO_FILTER_RESET,
|
||||
TEST_DATA_RO_FILTER_TOPIC,
|
||||
TEST_DATA_SOFTENER,
|
||||
TEST_DATA_SOFTENER_RESET,
|
||||
TEST_DATA_SOFTENER_TOPIC,
|
||||
)
|
||||
|
||||
from tests.common import async_fire_mqtt_message
|
||||
from tests.typing import MqttMockHAClient
|
||||
|
||||
|
||||
async def test_sensors_hub(
|
||||
hass: HomeAssistant, config_entry_hub, mqtt_mock: MqttMockHAClient
|
||||
) -> None:
|
||||
"""Test DROP sensors for hubs."""
|
||||
config_entry_hub.add_to_hass(hass)
|
||||
assert await async_setup_component(hass, DOMAIN, {})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
current_flow_sensor_name = "sensor.hub_drop_1_c0ffee_water_flow_rate"
|
||||
hass.states.async_set(current_flow_sensor_name, STATE_UNKNOWN)
|
||||
peak_flow_sensor_name = "sensor.hub_drop_1_c0ffee_peak_water_flow_rate_today"
|
||||
hass.states.async_set(peak_flow_sensor_name, STATE_UNKNOWN)
|
||||
used_today_sensor_name = "sensor.hub_drop_1_c0ffee_total_water_used_today"
|
||||
hass.states.async_set(used_today_sensor_name, STATE_UNKNOWN)
|
||||
average_usage_sensor_name = "sensor.hub_drop_1_c0ffee_average_daily_water_usage"
|
||||
hass.states.async_set(average_usage_sensor_name, STATE_UNKNOWN)
|
||||
psi_sensor_name = "sensor.hub_drop_1_c0ffee_current_water_pressure"
|
||||
hass.states.async_set(psi_sensor_name, STATE_UNKNOWN)
|
||||
psi_high_sensor_name = "sensor.hub_drop_1_c0ffee_high_water_pressure_today"
|
||||
hass.states.async_set(psi_high_sensor_name, STATE_UNKNOWN)
|
||||
psi_low_sensor_name = "sensor.hub_drop_1_c0ffee_low_water_pressure_today"
|
||||
hass.states.async_set(psi_low_sensor_name, STATE_UNKNOWN)
|
||||
battery_sensor_name = "sensor.hub_drop_1_c0ffee_battery"
|
||||
hass.states.async_set(battery_sensor_name, STATE_UNKNOWN)
|
||||
|
||||
async_fire_mqtt_message(hass, TEST_DATA_HUB_TOPIC, TEST_DATA_HUB_RESET)
|
||||
await hass.async_block_till_done()
|
||||
async_fire_mqtt_message(hass, TEST_DATA_HUB_TOPIC, TEST_DATA_HUB)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
current_flow_sensor = hass.states.get(current_flow_sensor_name)
|
||||
assert current_flow_sensor
|
||||
assert round(float(current_flow_sensor.state), 1) == 5.8
|
||||
|
||||
peak_flow_sensor = hass.states.get(peak_flow_sensor_name)
|
||||
assert peak_flow_sensor
|
||||
assert round(float(peak_flow_sensor.state), 1) == 13.8
|
||||
|
||||
used_today_sensor = hass.states.get(used_today_sensor_name)
|
||||
assert used_today_sensor
|
||||
assert round(float(used_today_sensor.state), 1) == 881.1 # liters
|
||||
|
||||
average_usage_sensor = hass.states.get(average_usage_sensor_name)
|
||||
assert average_usage_sensor
|
||||
assert round(float(average_usage_sensor.state), 1) == 287.7 # liters
|
||||
|
||||
psi_sensor = hass.states.get(psi_sensor_name)
|
||||
assert psi_sensor
|
||||
assert round(float(psi_sensor.state), 1) == 428.9 # centibars
|
||||
|
||||
psi_high_sensor = hass.states.get(psi_high_sensor_name)
|
||||
assert psi_high_sensor
|
||||
assert round(float(psi_high_sensor.state), 1) == 427.5 # centibars
|
||||
|
||||
psi_low_sensor = hass.states.get(psi_low_sensor_name)
|
||||
assert psi_low_sensor
|
||||
assert round(float(psi_low_sensor.state), 1) == 420.6 # centibars
|
||||
|
||||
battery_sensor = hass.states.get(battery_sensor_name)
|
||||
assert battery_sensor
|
||||
assert int(battery_sensor.state) == 50
|
||||
|
||||
|
||||
async def test_sensors_leak(
|
||||
hass: HomeAssistant, config_entry_leak, mqtt_mock: MqttMockHAClient
|
||||
) -> None:
|
||||
"""Test DROP sensors for leak detectors."""
|
||||
config_entry_leak.add_to_hass(hass)
|
||||
assert await async_setup_component(hass, DOMAIN, {})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
battery_sensor_name = "sensor.leak_detector_battery"
|
||||
hass.states.async_set(battery_sensor_name, STATE_UNKNOWN)
|
||||
temp_sensor_name = "sensor.leak_detector_temperature"
|
||||
hass.states.async_set(temp_sensor_name, STATE_UNKNOWN)
|
||||
|
||||
async_fire_mqtt_message(hass, TEST_DATA_LEAK_TOPIC, TEST_DATA_LEAK_RESET)
|
||||
await hass.async_block_till_done()
|
||||
async_fire_mqtt_message(hass, TEST_DATA_LEAK_TOPIC, TEST_DATA_LEAK)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
battery_sensor = hass.states.get(battery_sensor_name)
|
||||
assert battery_sensor
|
||||
assert int(battery_sensor.state) == 100
|
||||
|
||||
temp_sensor = hass.states.get(temp_sensor_name)
|
||||
assert temp_sensor
|
||||
assert round(float(temp_sensor.state), 1) == 20.1 # C
|
||||
|
||||
|
||||
async def test_sensors_softener(
|
||||
hass: HomeAssistant, config_entry_softener, mqtt_mock: MqttMockHAClient
|
||||
) -> None:
|
||||
"""Test DROP sensors for softeners."""
|
||||
config_entry_softener.add_to_hass(hass)
|
||||
assert await async_setup_component(hass, DOMAIN, {})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
battery_sensor_name = "sensor.softener_battery"
|
||||
hass.states.async_set(battery_sensor_name, STATE_UNKNOWN)
|
||||
current_flow_sensor_name = "sensor.softener_water_flow_rate"
|
||||
hass.states.async_set(current_flow_sensor_name, STATE_UNKNOWN)
|
||||
psi_sensor_name = "sensor.softener_current_water_pressure"
|
||||
hass.states.async_set(psi_sensor_name, STATE_UNKNOWN)
|
||||
capacity_sensor_name = "sensor.softener_capacity_remaining"
|
||||
hass.states.async_set(capacity_sensor_name, STATE_UNKNOWN)
|
||||
|
||||
async_fire_mqtt_message(hass, TEST_DATA_SOFTENER_TOPIC, TEST_DATA_SOFTENER_RESET)
|
||||
await hass.async_block_till_done()
|
||||
async_fire_mqtt_message(hass, TEST_DATA_SOFTENER_TOPIC, TEST_DATA_SOFTENER)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
battery_sensor = hass.states.get(battery_sensor_name)
|
||||
assert battery_sensor
|
||||
assert int(battery_sensor.state) == 20
|
||||
|
||||
current_flow_sensor = hass.states.get(current_flow_sensor_name)
|
||||
assert current_flow_sensor
|
||||
assert round(float(current_flow_sensor.state), 1) == 5.0
|
||||
|
||||
psi_sensor = hass.states.get(psi_sensor_name)
|
||||
assert psi_sensor
|
||||
assert round(float(psi_sensor.state), 1) == 348.2 # centibars
|
||||
|
||||
capacity_sensor = hass.states.get(capacity_sensor_name)
|
||||
assert capacity_sensor
|
||||
assert round(float(capacity_sensor.state), 1) == 3785.4 # liters
|
||||
|
||||
|
||||
async def test_sensors_filter(
|
||||
hass: HomeAssistant, config_entry_filter, mqtt_mock: MqttMockHAClient
|
||||
) -> None:
|
||||
"""Test DROP sensors for filters."""
|
||||
config_entry_filter.add_to_hass(hass)
|
||||
assert await async_setup_component(hass, DOMAIN, {})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
battery_sensor_name = "sensor.filter_battery"
|
||||
hass.states.async_set(battery_sensor_name, STATE_UNKNOWN)
|
||||
current_flow_sensor_name = "sensor.filter_water_flow_rate"
|
||||
hass.states.async_set(current_flow_sensor_name, STATE_UNKNOWN)
|
||||
psi_sensor_name = "sensor.filter_current_water_pressure"
|
||||
hass.states.async_set(psi_sensor_name, STATE_UNKNOWN)
|
||||
|
||||
async_fire_mqtt_message(hass, TEST_DATA_FILTER_TOPIC, TEST_DATA_FILTER_RESET)
|
||||
await hass.async_block_till_done()
|
||||
async_fire_mqtt_message(hass, TEST_DATA_FILTER_TOPIC, TEST_DATA_FILTER)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
battery_sensor = hass.states.get(battery_sensor_name)
|
||||
assert battery_sensor
|
||||
assert round(float(battery_sensor.state), 1) == 12.0
|
||||
|
||||
current_flow_sensor = hass.states.get(current_flow_sensor_name)
|
||||
assert current_flow_sensor
|
||||
assert round(float(current_flow_sensor.state), 1) == 19.8
|
||||
|
||||
psi_sensor = hass.states.get(psi_sensor_name)
|
||||
assert psi_sensor
|
||||
assert round(float(psi_sensor.state), 1) == 263.4 # centibars
|
||||
|
||||
|
||||
async def test_sensors_protection_valve(
|
||||
hass: HomeAssistant, config_entry_protection_valve, mqtt_mock: MqttMockHAClient
|
||||
) -> None:
|
||||
"""Test DROP sensors for protection valves."""
|
||||
config_entry_protection_valve.add_to_hass(hass)
|
||||
assert await async_setup_component(hass, DOMAIN, {})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
battery_sensor_name = "sensor.protection_valve_battery"
|
||||
hass.states.async_set(battery_sensor_name, STATE_UNKNOWN)
|
||||
current_flow_sensor_name = "sensor.protection_valve_water_flow_rate"
|
||||
hass.states.async_set(current_flow_sensor_name, STATE_UNKNOWN)
|
||||
psi_sensor_name = "sensor.protection_valve_current_water_pressure"
|
||||
hass.states.async_set(psi_sensor_name, STATE_UNKNOWN)
|
||||
temp_sensor_name = "sensor.protection_valve_temperature"
|
||||
hass.states.async_set(temp_sensor_name, STATE_UNKNOWN)
|
||||
|
||||
async_fire_mqtt_message(
|
||||
hass, TEST_DATA_PROTECTION_VALVE_TOPIC, TEST_DATA_PROTECTION_VALVE_RESET
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
async_fire_mqtt_message(
|
||||
hass, TEST_DATA_PROTECTION_VALVE_TOPIC, TEST_DATA_PROTECTION_VALVE
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
battery_sensor = hass.states.get(battery_sensor_name)
|
||||
assert battery_sensor
|
||||
assert int(battery_sensor.state) == 0
|
||||
|
||||
current_flow_sensor = hass.states.get(current_flow_sensor_name)
|
||||
assert current_flow_sensor
|
||||
assert round(float(current_flow_sensor.state), 1) == 7.1
|
||||
|
||||
psi_sensor = hass.states.get(psi_sensor_name)
|
||||
assert psi_sensor
|
||||
assert round(float(psi_sensor.state), 1) == 422.6 # centibars
|
||||
|
||||
temp_sensor = hass.states.get(temp_sensor_name)
|
||||
assert temp_sensor
|
||||
assert round(float(temp_sensor.state), 1) == 21.4 # C
|
||||
|
||||
|
||||
async def test_sensors_pump_controller(
|
||||
hass: HomeAssistant, config_entry_pump_controller, mqtt_mock: MqttMockHAClient
|
||||
) -> None:
|
||||
"""Test DROP sensors for pump controllers."""
|
||||
config_entry_pump_controller.add_to_hass(hass)
|
||||
assert await async_setup_component(hass, DOMAIN, {})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
current_flow_sensor_name = "sensor.pump_controller_water_flow_rate"
|
||||
hass.states.async_set(current_flow_sensor_name, STATE_UNKNOWN)
|
||||
psi_sensor_name = "sensor.pump_controller_current_water_pressure"
|
||||
hass.states.async_set(psi_sensor_name, STATE_UNKNOWN)
|
||||
temp_sensor_name = "sensor.pump_controller_temperature"
|
||||
hass.states.async_set(temp_sensor_name, STATE_UNKNOWN)
|
||||
|
||||
async_fire_mqtt_message(
|
||||
hass, TEST_DATA_PUMP_CONTROLLER_TOPIC, TEST_DATA_PUMP_CONTROLLER_RESET
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
async_fire_mqtt_message(
|
||||
hass, TEST_DATA_PUMP_CONTROLLER_TOPIC, TEST_DATA_PUMP_CONTROLLER
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
current_flow_sensor = hass.states.get(current_flow_sensor_name)
|
||||
assert current_flow_sensor
|
||||
assert round(float(current_flow_sensor.state), 1) == 2.2
|
||||
|
||||
psi_sensor = hass.states.get(psi_sensor_name)
|
||||
assert psi_sensor
|
||||
assert round(float(psi_sensor.state), 1) == 428.9 # centibars
|
||||
|
||||
temp_sensor = hass.states.get(temp_sensor_name)
|
||||
assert temp_sensor
|
||||
assert round(float(temp_sensor.state), 1) == 20.4 # C
|
||||
|
||||
|
||||
async def test_sensors_ro_filter(
|
||||
hass: HomeAssistant, config_entry_ro_filter, mqtt_mock: MqttMockHAClient
|
||||
) -> None:
|
||||
"""Test DROP sensors for RO filters."""
|
||||
config_entry_ro_filter.add_to_hass(hass)
|
||||
assert await async_setup_component(hass, DOMAIN, {})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
tds_in_sensor_name = "sensor.ro_filter_inlet_tds"
|
||||
hass.states.async_set(tds_in_sensor_name, STATE_UNKNOWN)
|
||||
tds_out_sensor_name = "sensor.ro_filter_outlet_tds"
|
||||
hass.states.async_set(tds_out_sensor_name, STATE_UNKNOWN)
|
||||
cart1_sensor_name = "sensor.ro_filter_cartridge_1_life_remaining"
|
||||
hass.states.async_set(cart1_sensor_name, STATE_UNKNOWN)
|
||||
cart2_sensor_name = "sensor.ro_filter_cartridge_2_life_remaining"
|
||||
hass.states.async_set(cart2_sensor_name, STATE_UNKNOWN)
|
||||
cart3_sensor_name = "sensor.ro_filter_cartridge_3_life_remaining"
|
||||
hass.states.async_set(cart3_sensor_name, STATE_UNKNOWN)
|
||||
|
||||
async_fire_mqtt_message(hass, TEST_DATA_RO_FILTER_TOPIC, TEST_DATA_RO_FILTER_RESET)
|
||||
await hass.async_block_till_done()
|
||||
async_fire_mqtt_message(hass, TEST_DATA_RO_FILTER_TOPIC, TEST_DATA_RO_FILTER)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
tds_in_sensor = hass.states.get(tds_in_sensor_name)
|
||||
assert tds_in_sensor
|
||||
assert int(tds_in_sensor.state) == 164
|
||||
|
||||
tds_out_sensor = hass.states.get(tds_out_sensor_name)
|
||||
assert tds_out_sensor
|
||||
assert int(tds_out_sensor.state) == 9
|
||||
|
||||
cart1_sensor = hass.states.get(cart1_sensor_name)
|
||||
assert cart1_sensor
|
||||
assert int(cart1_sensor.state) == 59
|
||||
|
||||
cart2_sensor = hass.states.get(cart2_sensor_name)
|
||||
assert cart2_sensor
|
||||
assert int(cart2_sensor.state) == 80
|
||||
|
||||
cart3_sensor = hass.states.get(cart3_sensor_name)
|
||||
assert cart3_sensor
|
||||
assert int(cart3_sensor.state) == 59
|
Loading…
Reference in New Issue