P1 Monitor add water meter support (#74004)

This commit is contained in:
Klaas Schoute 2022-08-18 22:35:28 +02:00 committed by GitHub
parent eec45c1208
commit b8d8d5540e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 367 additions and 231 deletions

View File

@ -3,7 +3,14 @@ from __future__ import annotations
from typing import TypedDict from typing import TypedDict
from p1monitor import P1Monitor, Phases, Settings, SmartMeter from p1monitor import (
P1Monitor,
P1MonitorNoDataError,
Phases,
Settings,
SmartMeter,
WaterMeter,
)
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, Platform from homeassistant.const import CONF_HOST, Platform
@ -19,6 +26,7 @@ from .const import (
SERVICE_PHASES, SERVICE_PHASES,
SERVICE_SETTINGS, SERVICE_SETTINGS,
SERVICE_SMARTMETER, SERVICE_SMARTMETER,
SERVICE_WATERMETER,
) )
PLATFORMS = [Platform.SENSOR] PLATFORMS = [Platform.SENSOR]
@ -55,12 +63,14 @@ class P1MonitorData(TypedDict):
smartmeter: SmartMeter smartmeter: SmartMeter
phases: Phases phases: Phases
settings: Settings settings: Settings
watermeter: WaterMeter | None
class P1MonitorDataUpdateCoordinator(DataUpdateCoordinator[P1MonitorData]): class P1MonitorDataUpdateCoordinator(DataUpdateCoordinator[P1MonitorData]):
"""Class to manage fetching P1 Monitor data from single endpoint.""" """Class to manage fetching P1 Monitor data from single endpoint."""
config_entry: ConfigEntry config_entry: ConfigEntry
has_water_meter: bool | None = None
def __init__( def __init__(
self, self,
@ -84,6 +94,16 @@ class P1MonitorDataUpdateCoordinator(DataUpdateCoordinator[P1MonitorData]):
SERVICE_SMARTMETER: await self.p1monitor.smartmeter(), SERVICE_SMARTMETER: await self.p1monitor.smartmeter(),
SERVICE_PHASES: await self.p1monitor.phases(), SERVICE_PHASES: await self.p1monitor.phases(),
SERVICE_SETTINGS: await self.p1monitor.settings(), SERVICE_SETTINGS: await self.p1monitor.settings(),
SERVICE_WATERMETER: None,
} }
if self.has_water_meter or self.has_water_meter is None:
try:
data[SERVICE_WATERMETER] = await self.p1monitor.watermeter()
self.has_water_meter = True
except P1MonitorNoDataError:
LOGGER.debug("No watermeter data received from P1 Monitor")
if self.has_water_meter is None:
self.has_water_meter = False
return data return data

View File

@ -10,11 +10,6 @@ LOGGER = logging.getLogger(__package__)
SCAN_INTERVAL = timedelta(seconds=5) SCAN_INTERVAL = timedelta(seconds=5)
SERVICE_SMARTMETER: Final = "smartmeter" SERVICE_SMARTMETER: Final = "smartmeter"
SERVICE_WATERMETER: Final = "watermeter"
SERVICE_PHASES: Final = "phases" SERVICE_PHASES: Final = "phases"
SERVICE_SETTINGS: Final = "settings" SERVICE_SETTINGS: Final = "settings"
SERVICES: dict[str, str] = {
SERVICE_SMARTMETER: "SmartMeter",
SERVICE_PHASES: "Phases",
SERVICE_SETTINGS: "Settings",
}

View File

@ -10,7 +10,13 @@ from homeassistant.const import CONF_HOST
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from . import P1MonitorDataUpdateCoordinator from . import P1MonitorDataUpdateCoordinator
from .const import DOMAIN, SERVICE_PHASES, SERVICE_SETTINGS, SERVICE_SMARTMETER from .const import (
DOMAIN,
SERVICE_PHASES,
SERVICE_SETTINGS,
SERVICE_SMARTMETER,
SERVICE_WATERMETER,
)
TO_REDACT = { TO_REDACT = {
CONF_HOST, CONF_HOST,
@ -23,7 +29,7 @@ async def async_get_config_entry_diagnostics(
"""Return diagnostics for a config entry.""" """Return diagnostics for a config entry."""
coordinator: P1MonitorDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] coordinator: P1MonitorDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
return { data = {
"entry": { "entry": {
"title": entry.title, "title": entry.title,
"data": async_redact_data(entry.data, TO_REDACT), "data": async_redact_data(entry.data, TO_REDACT),
@ -34,3 +40,8 @@ async def async_get_config_entry_diagnostics(
"settings": asdict(coordinator.data[SERVICE_SETTINGS]), "settings": asdict(coordinator.data[SERVICE_SETTINGS]),
}, },
} }
if coordinator.has_water_meter:
data["data"]["watermeter"] = asdict(coordinator.data[SERVICE_WATERMETER])
return data

View File

@ -3,7 +3,7 @@
"name": "P1 Monitor", "name": "P1 Monitor",
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/p1_monitor", "documentation": "https://www.home-assistant.io/integrations/p1_monitor",
"requirements": ["p1monitor==1.0.1"], "requirements": ["p1monitor==2.1.0"],
"codeowners": ["@klaasnicolaas"], "codeowners": ["@klaasnicolaas"],
"quality_scale": "platinum", "quality_scale": "platinum",
"iot_class": "local_polling", "iot_class": "local_polling",

View File

@ -19,6 +19,7 @@ from homeassistant.const import (
ENERGY_KILO_WATT_HOUR, ENERGY_KILO_WATT_HOUR,
POWER_WATT, POWER_WATT,
VOLUME_CUBIC_METERS, VOLUME_CUBIC_METERS,
VOLUME_LITERS,
) )
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceEntryType from homeassistant.helpers.device_registry import DeviceEntryType
@ -33,206 +34,256 @@ from .const import (
SERVICE_PHASES, SERVICE_PHASES,
SERVICE_SETTINGS, SERVICE_SETTINGS,
SERVICE_SMARTMETER, SERVICE_SMARTMETER,
SERVICES, SERVICE_WATERMETER,
) )
SENSORS: dict[ SENSORS_SMARTMETER: tuple[SensorEntityDescription, ...] = (
Literal["smartmeter", "phases", "settings"], tuple[SensorEntityDescription, ...] SensorEntityDescription(
] = { key="gas_consumption",
SERVICE_SMARTMETER: ( name="Gas Consumption",
SensorEntityDescription( entity_registry_enabled_default=False,
key="gas_consumption", native_unit_of_measurement=VOLUME_CUBIC_METERS,
name="Gas Consumption", device_class=SensorDeviceClass.GAS,
entity_registry_enabled_default=False, state_class=SensorStateClass.TOTAL_INCREASING,
native_unit_of_measurement=VOLUME_CUBIC_METERS,
device_class=SensorDeviceClass.GAS,
state_class=SensorStateClass.TOTAL_INCREASING,
),
SensorEntityDescription(
key="power_consumption",
name="Power Consumption",
native_unit_of_measurement=POWER_WATT,
device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key="energy_consumption_high",
name="Energy Consumption - High Tariff",
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
),
SensorEntityDescription(
key="energy_consumption_low",
name="Energy Consumption - Low Tariff",
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
),
SensorEntityDescription(
key="power_production",
name="Power Production",
native_unit_of_measurement=POWER_WATT,
device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key="energy_production_high",
name="Energy Production - High Tariff",
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
),
SensorEntityDescription(
key="energy_production_low",
name="Energy Production - Low Tariff",
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
),
SensorEntityDescription(
key="energy_tariff_period",
name="Energy Tariff Period",
icon="mdi:calendar-clock",
),
), ),
SERVICE_PHASES: ( SensorEntityDescription(
SensorEntityDescription( key="power_consumption",
key="voltage_phase_l1", name="Power Consumption",
name="Voltage Phase L1", native_unit_of_measurement=POWER_WATT,
native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, device_class=SensorDeviceClass.POWER,
device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key="voltage_phase_l2",
name="Voltage Phase L2",
native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT,
device_class=SensorDeviceClass.VOLTAGE,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key="voltage_phase_l3",
name="Voltage Phase L3",
native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT,
device_class=SensorDeviceClass.VOLTAGE,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key="current_phase_l1",
name="Current Phase L1",
native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE,
device_class=SensorDeviceClass.CURRENT,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key="current_phase_l2",
name="Current Phase L2",
native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE,
device_class=SensorDeviceClass.CURRENT,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key="current_phase_l3",
name="Current Phase L3",
native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE,
device_class=SensorDeviceClass.CURRENT,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key="power_consumed_phase_l1",
name="Power Consumed Phase L1",
native_unit_of_measurement=POWER_WATT,
device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key="power_consumed_phase_l2",
name="Power Consumed Phase L2",
native_unit_of_measurement=POWER_WATT,
device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key="power_consumed_phase_l3",
name="Power Consumed Phase L3",
native_unit_of_measurement=POWER_WATT,
device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key="power_produced_phase_l1",
name="Power Produced Phase L1",
native_unit_of_measurement=POWER_WATT,
device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key="power_produced_phase_l2",
name="Power Produced Phase L2",
native_unit_of_measurement=POWER_WATT,
device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key="power_produced_phase_l3",
name="Power Produced Phase L3",
native_unit_of_measurement=POWER_WATT,
device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
),
), ),
SERVICE_SETTINGS: ( SensorEntityDescription(
SensorEntityDescription( key="energy_consumption_high",
key="gas_consumption_price", name="Energy Consumption - High Tariff",
name="Gas Consumption Price", native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
entity_registry_enabled_default=False, device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.TOTAL_INCREASING,
native_unit_of_measurement=f"{CURRENCY_EURO}/{VOLUME_CUBIC_METERS}",
),
SensorEntityDescription(
key="energy_consumption_price_low",
name="Energy Consumption Price - Low",
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=f"{CURRENCY_EURO}/{ENERGY_KILO_WATT_HOUR}",
),
SensorEntityDescription(
key="energy_consumption_price_high",
name="Energy Consumption Price - High",
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=f"{CURRENCY_EURO}/{ENERGY_KILO_WATT_HOUR}",
),
SensorEntityDescription(
key="energy_production_price_low",
name="Energy Production Price - Low",
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=f"{CURRENCY_EURO}/{ENERGY_KILO_WATT_HOUR}",
),
SensorEntityDescription(
key="energy_production_price_high",
name="Energy Production Price - High",
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=f"{CURRENCY_EURO}/{ENERGY_KILO_WATT_HOUR}",
),
), ),
} SensorEntityDescription(
key="energy_consumption_low",
name="Energy Consumption - Low Tariff",
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
),
SensorEntityDescription(
key="power_production",
name="Power Production",
native_unit_of_measurement=POWER_WATT,
device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key="energy_production_high",
name="Energy Production - High Tariff",
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
),
SensorEntityDescription(
key="energy_production_low",
name="Energy Production - Low Tariff",
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
),
SensorEntityDescription(
key="energy_tariff_period",
name="Energy Tariff Period",
icon="mdi:calendar-clock",
),
)
SENSORS_PHASES: tuple[SensorEntityDescription, ...] = (
SensorEntityDescription(
key="voltage_phase_l1",
name="Voltage Phase L1",
native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT,
device_class=SensorDeviceClass.VOLTAGE,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key="voltage_phase_l2",
name="Voltage Phase L2",
native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT,
device_class=SensorDeviceClass.VOLTAGE,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key="voltage_phase_l3",
name="Voltage Phase L3",
native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT,
device_class=SensorDeviceClass.VOLTAGE,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key="current_phase_l1",
name="Current Phase L1",
native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE,
device_class=SensorDeviceClass.CURRENT,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key="current_phase_l2",
name="Current Phase L2",
native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE,
device_class=SensorDeviceClass.CURRENT,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key="current_phase_l3",
name="Current Phase L3",
native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE,
device_class=SensorDeviceClass.CURRENT,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key="power_consumed_phase_l1",
name="Power Consumed Phase L1",
native_unit_of_measurement=POWER_WATT,
device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key="power_consumed_phase_l2",
name="Power Consumed Phase L2",
native_unit_of_measurement=POWER_WATT,
device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key="power_consumed_phase_l3",
name="Power Consumed Phase L3",
native_unit_of_measurement=POWER_WATT,
device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key="power_produced_phase_l1",
name="Power Produced Phase L1",
native_unit_of_measurement=POWER_WATT,
device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key="power_produced_phase_l2",
name="Power Produced Phase L2",
native_unit_of_measurement=POWER_WATT,
device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key="power_produced_phase_l3",
name="Power Produced Phase L3",
native_unit_of_measurement=POWER_WATT,
device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
),
)
SENSORS_SETTINGS: tuple[SensorEntityDescription, ...] = (
SensorEntityDescription(
key="gas_consumption_price",
name="Gas Consumption Price",
entity_registry_enabled_default=False,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=f"{CURRENCY_EURO}/{VOLUME_CUBIC_METERS}",
),
SensorEntityDescription(
key="energy_consumption_price_low",
name="Energy Consumption Price - Low",
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=f"{CURRENCY_EURO}/{ENERGY_KILO_WATT_HOUR}",
),
SensorEntityDescription(
key="energy_consumption_price_high",
name="Energy Consumption Price - High",
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=f"{CURRENCY_EURO}/{ENERGY_KILO_WATT_HOUR}",
),
SensorEntityDescription(
key="energy_production_price_low",
name="Energy Production Price - Low",
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=f"{CURRENCY_EURO}/{ENERGY_KILO_WATT_HOUR}",
),
SensorEntityDescription(
key="energy_production_price_high",
name="Energy Production Price - High",
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=f"{CURRENCY_EURO}/{ENERGY_KILO_WATT_HOUR}",
),
)
SENSORS_WATERMETER: tuple[SensorEntityDescription, ...] = (
SensorEntityDescription(
key="consumption_day",
name="Consumption Day",
state_class=SensorStateClass.TOTAL_INCREASING,
native_unit_of_measurement=VOLUME_LITERS,
),
SensorEntityDescription(
key="consumption_total",
name="Consumption Total",
state_class=SensorStateClass.TOTAL_INCREASING,
native_unit_of_measurement=VOLUME_CUBIC_METERS,
),
SensorEntityDescription(
key="pulse_count",
name="Pulse Count",
),
)
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None: ) -> None:
"""Set up P1 Monitor Sensors based on a config entry.""" """Set up P1 Monitor Sensors based on a config entry."""
async_add_entities( coordinator: P1MonitorDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
entities: list[P1MonitorSensorEntity] = []
entities.extend(
P1MonitorSensorEntity( P1MonitorSensorEntity(
coordinator=hass.data[DOMAIN][entry.entry_id], coordinator=coordinator,
description=description, description=description,
service_key=service_key, name="SmartMeter",
name=entry.title, service_key="smartmeter",
service=SERVICES[service_key], service=SERVICE_SMARTMETER,
) )
for service_key, service_sensors in SENSORS.items() for description in SENSORS_SMARTMETER
for description in service_sensors
) )
entities.extend(
P1MonitorSensorEntity(
coordinator=coordinator,
description=description,
name="Phases",
service_key="phases",
service=SERVICE_PHASES,
)
for description in SENSORS_PHASES
)
entities.extend(
P1MonitorSensorEntity(
coordinator=coordinator,
description=description,
name="Settings",
service_key="settings",
service=SERVICE_SETTINGS,
)
for description in SENSORS_SETTINGS
)
if coordinator.has_water_meter:
entities.extend(
P1MonitorSensorEntity(
coordinator=coordinator,
description=description,
name="WaterMeter",
service_key="watermeter",
service=SERVICE_WATERMETER,
)
for description in SENSORS_WATERMETER
)
async_add_entities(entities)
class P1MonitorSensorEntity( class P1MonitorSensorEntity(
@ -245,7 +296,7 @@ class P1MonitorSensorEntity(
*, *,
coordinator: P1MonitorDataUpdateCoordinator, coordinator: P1MonitorDataUpdateCoordinator,
description: SensorEntityDescription, description: SensorEntityDescription,
service_key: Literal["smartmeter", "phases", "settings"], service_key: Literal["smartmeter", "watermeter", "phases", "settings"],
name: str, name: str,
service: str, service: str,
) -> None: ) -> None:
@ -253,7 +304,7 @@ class P1MonitorSensorEntity(
super().__init__(coordinator=coordinator) super().__init__(coordinator=coordinator)
self._service_key = service_key self._service_key = service_key
self.entity_id = f"{SENSOR_DOMAIN}.{name}_{description.key}" self.entity_id = f"{SENSOR_DOMAIN}.{service}_{description.key}"
self.entity_description = description self.entity_description = description
self._attr_unique_id = ( self._attr_unique_id = (
f"{coordinator.config_entry.entry_id}_{service_key}_{description.key}" f"{coordinator.config_entry.entry_id}_{service_key}_{description.key}"
@ -266,7 +317,7 @@ class P1MonitorSensorEntity(
}, },
configuration_url=f"http://{coordinator.config_entry.data[CONF_HOST]}", configuration_url=f"http://{coordinator.config_entry.data[CONF_HOST]}",
manufacturer="P1 Monitor", manufacturer="P1 Monitor",
name=service, name=name,
) )
@property @property

View File

@ -1206,7 +1206,7 @@ orvibo==1.1.1
ovoenergy==1.2.0 ovoenergy==1.2.0
# homeassistant.components.p1_monitor # homeassistant.components.p1_monitor
p1monitor==1.0.1 p1monitor==2.1.0
# homeassistant.components.mqtt # homeassistant.components.mqtt
# homeassistant.components.shiftr # homeassistant.components.shiftr

View File

@ -842,7 +842,7 @@ openerz-api==0.1.0
ovoenergy==1.2.0 ovoenergy==1.2.0
# homeassistant.components.p1_monitor # homeassistant.components.p1_monitor
p1monitor==1.0.1 p1monitor==2.1.0
# homeassistant.components.mqtt # homeassistant.components.mqtt
# homeassistant.components.shiftr # homeassistant.components.shiftr

View File

@ -2,7 +2,7 @@
import json import json
from unittest.mock import AsyncMock, MagicMock, patch from unittest.mock import AsyncMock, MagicMock, patch
from p1monitor import Phases, Settings, SmartMeter from p1monitor import Phases, Settings, SmartMeter, WaterMeter
import pytest import pytest
from homeassistant.components.p1_monitor.const import DOMAIN from homeassistant.components.p1_monitor.const import DOMAIN
@ -43,7 +43,12 @@ def mock_p1monitor():
json.loads(load_fixture("p1_monitor/settings.json")) json.loads(load_fixture("p1_monitor/settings.json"))
) )
) )
yield p1monitor_mock client.watermeter = AsyncMock(
return_value=WaterMeter.from_dict(
json.loads(load_fixture("p1_monitor/watermeter.json"))
)
)
yield client
@pytest.fixture @pytest.fixture

View File

@ -0,0 +1,10 @@
[
{
"TIMEPERIOD_ID": 13,
"TIMESTAMP_UTC": 1656194400,
"TIMESTAMP_lOCAL": "2022-06-26 00:00:00",
"WATERMETER_CONSUMPTION_LITER": 112.0,
"WATERMETER_CONSUMPTION_TOTAL_M3": 1696.14,
"WATERMETER_PULS_COUNT": 112.0
}
]

View File

@ -27,17 +27,12 @@ async def test_full_user_flow(hass: HomeAssistant) -> None:
) as mock_setup_entry: ) as mock_setup_entry:
result2 = await hass.config_entries.flow.async_configure( result2 = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
user_input={ user_input={CONF_NAME: "Name", CONF_HOST: "example.com"},
CONF_NAME: "Name",
CONF_HOST: "example.com",
},
) )
assert result2.get("type") == FlowResultType.CREATE_ENTRY assert result2.get("type") == FlowResultType.CREATE_ENTRY
assert result2.get("title") == "Name" assert result2.get("title") == "Name"
assert result2.get("data") == { assert result2.get("data") == {CONF_HOST: "example.com"}
CONF_HOST: "example.com",
}
assert len(mock_setup_entry.mock_calls) == 1 assert len(mock_setup_entry.mock_calls) == 1
assert len(mock_p1monitor.mock_calls) == 1 assert len(mock_p1monitor.mock_calls) == 1
@ -52,10 +47,7 @@ async def test_api_error(hass: HomeAssistant) -> None:
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, DOMAIN,
context={"source": SOURCE_USER}, context={"source": SOURCE_USER},
data={ data={CONF_NAME: "Name", CONF_HOST: "example.com"},
CONF_NAME: "Name",
CONF_HOST: "example.com",
},
) )
assert result.get("type") == FlowResultType.FORM assert result.get("type") == FlowResultType.FORM

View File

@ -55,5 +55,10 @@ async def test_diagnostics(
"energy_production_price_high": "0.20522", "energy_production_price_high": "0.20522",
"energy_production_price_low": "0.20522", "energy_production_price_low": "0.20522",
}, },
"watermeter": {
"consumption_day": 112.0,
"consumption_total": 1696.14,
"pulse_count": 112.0,
},
}, },
} }

View File

@ -28,7 +28,7 @@ async def test_load_unload_config_entry(
@patch( @patch(
"homeassistant.components.p1_monitor.P1Monitor.request", "homeassistant.components.p1_monitor.P1Monitor._request",
side_effect=P1MonitorConnectionError, side_effect=P1MonitorConnectionError,
) )
async def test_config_entry_not_ready( async def test_config_entry_not_ready(

View File

@ -1,4 +1,7 @@
"""Tests for the sensors provided by the P1 Monitor integration.""" """Tests for the sensors provided by the P1 Monitor integration."""
from unittest.mock import MagicMock
from p1monitor import P1MonitorNoDataError
import pytest import pytest
from homeassistant.components.p1_monitor.const import DOMAIN from homeassistant.components.p1_monitor.const import DOMAIN
@ -17,6 +20,7 @@ from homeassistant.const import (
ELECTRIC_POTENTIAL_VOLT, ELECTRIC_POTENTIAL_VOLT,
ENERGY_KILO_WATT_HOUR, ENERGY_KILO_WATT_HOUR,
POWER_WATT, POWER_WATT,
VOLUME_LITERS,
) )
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.helpers import device_registry as dr, entity_registry as er
@ -33,8 +37,8 @@ async def test_smartmeter(
entity_registry = er.async_get(hass) entity_registry = er.async_get(hass)
device_registry = dr.async_get(hass) device_registry = dr.async_get(hass)
state = hass.states.get("sensor.monitor_power_consumption") state = hass.states.get("sensor.smartmeter_power_consumption")
entry = entity_registry.async_get("sensor.monitor_power_consumption") entry = entity_registry.async_get("sensor.smartmeter_power_consumption")
assert entry assert entry
assert state assert state
assert entry.unique_id == f"{entry_id}_smartmeter_power_consumption" assert entry.unique_id == f"{entry_id}_smartmeter_power_consumption"
@ -45,8 +49,8 @@ async def test_smartmeter(
assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.POWER assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.POWER
assert ATTR_ICON not in state.attributes assert ATTR_ICON not in state.attributes
state = hass.states.get("sensor.monitor_energy_consumption_high") state = hass.states.get("sensor.smartmeter_energy_consumption_high")
entry = entity_registry.async_get("sensor.monitor_energy_consumption_high") entry = entity_registry.async_get("sensor.smartmeter_energy_consumption_high")
assert entry assert entry
assert state assert state
assert entry.unique_id == f"{entry_id}_smartmeter_energy_consumption_high" assert entry.unique_id == f"{entry_id}_smartmeter_energy_consumption_high"
@ -59,8 +63,8 @@ async def test_smartmeter(
assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.ENERGY assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.ENERGY
assert ATTR_ICON not in state.attributes assert ATTR_ICON not in state.attributes
state = hass.states.get("sensor.monitor_energy_tariff_period") state = hass.states.get("sensor.smartmeter_energy_tariff_period")
entry = entity_registry.async_get("sensor.monitor_energy_tariff_period") entry = entity_registry.async_get("sensor.smartmeter_energy_tariff_period")
assert entry assert entry
assert state assert state
assert entry.unique_id == f"{entry_id}_smartmeter_energy_tariff_period" assert entry.unique_id == f"{entry_id}_smartmeter_energy_tariff_period"
@ -90,8 +94,8 @@ async def test_phases(
entity_registry = er.async_get(hass) entity_registry = er.async_get(hass)
device_registry = dr.async_get(hass) device_registry = dr.async_get(hass)
state = hass.states.get("sensor.monitor_voltage_phase_l1") state = hass.states.get("sensor.phases_voltage_phase_l1")
entry = entity_registry.async_get("sensor.monitor_voltage_phase_l1") entry = entity_registry.async_get("sensor.phases_voltage_phase_l1")
assert entry assert entry
assert state assert state
assert entry.unique_id == f"{entry_id}_phases_voltage_phase_l1" assert entry.unique_id == f"{entry_id}_phases_voltage_phase_l1"
@ -102,8 +106,8 @@ async def test_phases(
assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.VOLTAGE assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.VOLTAGE
assert ATTR_ICON not in state.attributes assert ATTR_ICON not in state.attributes
state = hass.states.get("sensor.monitor_current_phase_l1") state = hass.states.get("sensor.phases_current_phase_l1")
entry = entity_registry.async_get("sensor.monitor_current_phase_l1") entry = entity_registry.async_get("sensor.phases_current_phase_l1")
assert entry assert entry
assert state assert state
assert entry.unique_id == f"{entry_id}_phases_current_phase_l1" assert entry.unique_id == f"{entry_id}_phases_current_phase_l1"
@ -114,8 +118,8 @@ async def test_phases(
assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.CURRENT assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.CURRENT
assert ATTR_ICON not in state.attributes assert ATTR_ICON not in state.attributes
state = hass.states.get("sensor.monitor_power_consumed_phase_l1") state = hass.states.get("sensor.phases_power_consumed_phase_l1")
entry = entity_registry.async_get("sensor.monitor_power_consumed_phase_l1") entry = entity_registry.async_get("sensor.phases_power_consumed_phase_l1")
assert entry assert entry
assert state assert state
assert entry.unique_id == f"{entry_id}_phases_power_consumed_phase_l1" assert entry.unique_id == f"{entry_id}_phases_power_consumed_phase_l1"
@ -146,8 +150,8 @@ async def test_settings(
entity_registry = er.async_get(hass) entity_registry = er.async_get(hass)
device_registry = dr.async_get(hass) device_registry = dr.async_get(hass)
state = hass.states.get("sensor.monitor_energy_consumption_price_low") state = hass.states.get("sensor.settings_energy_consumption_price_low")
entry = entity_registry.async_get("sensor.monitor_energy_consumption_price_low") entry = entity_registry.async_get("sensor.settings_energy_consumption_price_low")
assert entry assert entry
assert state assert state
assert entry.unique_id == f"{entry_id}_settings_energy_consumption_price_low" assert entry.unique_id == f"{entry_id}_settings_energy_consumption_price_low"
@ -159,8 +163,8 @@ async def test_settings(
== f"{CURRENCY_EURO}/{ENERGY_KILO_WATT_HOUR}" == f"{CURRENCY_EURO}/{ENERGY_KILO_WATT_HOUR}"
) )
state = hass.states.get("sensor.monitor_energy_production_price_low") state = hass.states.get("sensor.settings_energy_production_price_low")
entry = entity_registry.async_get("sensor.monitor_energy_production_price_low") entry = entity_registry.async_get("sensor.settings_energy_production_price_low")
assert entry assert entry
assert state assert state
assert entry.unique_id == f"{entry_id}_settings_energy_production_price_low" assert entry.unique_id == f"{entry_id}_settings_energy_production_price_low"
@ -183,9 +187,52 @@ async def test_settings(
assert not device_entry.sw_version assert not device_entry.sw_version
async def test_watermeter(
hass: HomeAssistant,
init_integration: MockConfigEntry,
) -> None:
"""Test the P1 Monitor - WaterMeter sensors."""
entry_id = init_integration.entry_id
entity_registry = er.async_get(hass)
device_registry = dr.async_get(hass)
state = hass.states.get("sensor.watermeter_consumption_day")
entry = entity_registry.async_get("sensor.watermeter_consumption_day")
assert entry
assert state
assert entry.unique_id == f"{entry_id}_watermeter_consumption_day"
assert state.state == "112.0"
assert state.attributes.get(ATTR_FRIENDLY_NAME) == "Consumption Day"
assert state.attributes.get(ATTR_STATE_CLASS) is SensorStateClass.TOTAL_INCREASING
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == VOLUME_LITERS
assert entry.device_id
device_entry = device_registry.async_get(entry.device_id)
assert device_entry
assert device_entry.identifiers == {(DOMAIN, f"{entry_id}_watermeter")}
assert device_entry.manufacturer == "P1 Monitor"
assert device_entry.name == "WaterMeter"
assert device_entry.entry_type is dr.DeviceEntryType.SERVICE
assert not device_entry.model
assert not device_entry.sw_version
async def test_no_watermeter(
hass: HomeAssistant, mock_p1monitor: MagicMock, mock_config_entry: MockConfigEntry
) -> None:
"""Test the P1 Monitor - Without WaterMeter sensors."""
mock_p1monitor.watermeter.side_effect = P1MonitorNoDataError
mock_config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()
assert not hass.states.get("sensor.watermeter_consumption_day")
assert not hass.states.get("sensor.consumption_total")
assert not hass.states.get("sensor.pulse_count")
@pytest.mark.parametrize( @pytest.mark.parametrize(
"entity_id", "entity_id",
("sensor.monitor_gas_consumption",), ("sensor.smartmeter_gas_consumption",),
) )
async def test_smartmeter_disabled_by_default( async def test_smartmeter_disabled_by_default(
hass: HomeAssistant, init_integration: MockConfigEntry, entity_id: str hass: HomeAssistant, init_integration: MockConfigEntry, entity_id: str