Landis+Gyr Heat Meter code improvements (#81184)
parent
2eb37f527a
commit
0cd9fe3288
|
@ -4,11 +4,12 @@ from __future__ import annotations
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from ultraheat_api import HeatMeterService, UltraheatReader
|
import ultraheat_api
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import CONF_DEVICE, Platform
|
from homeassistant.const import CONF_DEVICE, Platform
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant, callback
|
||||||
|
from homeassistant.helpers.entity_registry import async_migrate_entries
|
||||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
|
@ -22,13 +23,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
"""Set up heat meter from a config entry."""
|
"""Set up heat meter from a config entry."""
|
||||||
|
|
||||||
_LOGGER.debug("Initializing %s integration on %s", DOMAIN, entry.data[CONF_DEVICE])
|
_LOGGER.debug("Initializing %s integration on %s", DOMAIN, entry.data[CONF_DEVICE])
|
||||||
reader = UltraheatReader(entry.data[CONF_DEVICE])
|
reader = ultraheat_api.UltraheatReader(entry.data[CONF_DEVICE])
|
||||||
|
api = ultraheat_api.HeatMeterService(reader)
|
||||||
api = HeatMeterService(reader)
|
|
||||||
|
|
||||||
async def async_update_data():
|
async def async_update_data():
|
||||||
"""Fetch data from the API."""
|
"""Fetch data from the API."""
|
||||||
_LOGGER.info("Polling on %s", entry.data[CONF_DEVICE])
|
_LOGGER.debug("Polling on %s", entry.data[CONF_DEVICE])
|
||||||
return await hass.async_add_executor_job(api.read)
|
return await hass.async_add_executor_job(api.read)
|
||||||
|
|
||||||
# Polling is only daily to prevent battery drain.
|
# Polling is only daily to prevent battery drain.
|
||||||
|
@ -53,3 +53,35 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
hass.data[DOMAIN].pop(entry.entry_id)
|
hass.data[DOMAIN].pop(entry.entry_id)
|
||||||
|
|
||||||
return unload_ok
|
return unload_ok
|
||||||
|
|
||||||
|
|
||||||
|
async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
|
||||||
|
"""Migrate old entry."""
|
||||||
|
_LOGGER.debug("Migrating from version %s", config_entry.version)
|
||||||
|
|
||||||
|
# Removing domain name and config entry id from entity unique id's, replacing it with device number
|
||||||
|
if config_entry.version == 1:
|
||||||
|
|
||||||
|
config_entry.version = 2
|
||||||
|
|
||||||
|
device_number = config_entry.data["device_number"]
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def update_entity_unique_id(entity_entry):
|
||||||
|
"""Update unique ID of entity entry."""
|
||||||
|
if entity_entry.platform in entity_entry.unique_id:
|
||||||
|
return {
|
||||||
|
"new_unique_id": entity_entry.unique_id.replace(
|
||||||
|
f"{entity_entry.platform}_{entity_entry.config_entry_id}",
|
||||||
|
f"{device_number}",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
await async_migrate_entries(
|
||||||
|
hass, config_entry.entry_id, update_entity_unique_id
|
||||||
|
)
|
||||||
|
hass.config_entries.async_update_entry(config_entry)
|
||||||
|
|
||||||
|
_LOGGER.info("Migration to version %s successful", config_entry.version)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
|
@ -1,17 +1,21 @@
|
||||||
"""Config flow for Landis+Gyr Heat Meter integration."""
|
"""Config flow for Landis+Gyr Heat Meter integration."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
import os
|
from typing import Any
|
||||||
|
|
||||||
import async_timeout
|
import async_timeout
|
||||||
import serial
|
import serial
|
||||||
import serial.tools.list_ports
|
from serial.tools import list_ports
|
||||||
from ultraheat_api import HeatMeterService, UltraheatReader
|
import ultraheat_api
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant import config_entries
|
from homeassistant import config_entries
|
||||||
|
from homeassistant.components import usb
|
||||||
from homeassistant.const import CONF_DEVICE
|
from homeassistant.const import CONF_DEVICE
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.data_entry_flow import FlowResult
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
|
|
||||||
from .const import DOMAIN, ULTRAHEAT_TIMEOUT
|
from .const import DOMAIN, ULTRAHEAT_TIMEOUT
|
||||||
|
@ -30,9 +34,11 @@ STEP_USER_DATA_SCHEMA = vol.Schema(
|
||||||
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
"""Handle a config flow for Ultraheat Heat Meter."""
|
"""Handle a config flow for Ultraheat Heat Meter."""
|
||||||
|
|
||||||
VERSION = 1
|
VERSION = 2
|
||||||
|
|
||||||
async def async_step_user(self, user_input=None):
|
async def async_step_user(
|
||||||
|
self, user_input: dict[str, Any] | None = None
|
||||||
|
) -> FlowResult:
|
||||||
"""Step when setting up serial configuration."""
|
"""Step when setting up serial configuration."""
|
||||||
errors = {}
|
errors = {}
|
||||||
|
|
||||||
|
@ -41,7 +47,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
return await self.async_step_setup_serial_manual_path()
|
return await self.async_step_setup_serial_manual_path()
|
||||||
|
|
||||||
dev_path = await self.hass.async_add_executor_job(
|
dev_path = await self.hass.async_add_executor_job(
|
||||||
get_serial_by_id, user_input[CONF_DEVICE]
|
usb.get_serial_by_id, user_input[CONF_DEVICE]
|
||||||
)
|
)
|
||||||
_LOGGER.debug("Using this path : %s", dev_path)
|
_LOGGER.debug("Using this path : %s", dev_path)
|
||||||
|
|
||||||
|
@ -50,12 +56,15 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
except CannotConnect:
|
except CannotConnect:
|
||||||
errors["base"] = "cannot_connect"
|
errors["base"] = "cannot_connect"
|
||||||
|
|
||||||
ports = await self.get_ports()
|
ports = await get_usb_ports(self.hass)
|
||||||
|
ports[CONF_MANUAL_PATH] = CONF_MANUAL_PATH
|
||||||
|
|
||||||
schema = vol.Schema({vol.Required(CONF_DEVICE): vol.In(ports)})
|
schema = vol.Schema({vol.Required(CONF_DEVICE): vol.In(ports)})
|
||||||
return self.async_show_form(step_id="user", data_schema=schema, errors=errors)
|
return self.async_show_form(step_id="user", data_schema=schema, errors=errors)
|
||||||
|
|
||||||
async def async_step_setup_serial_manual_path(self, user_input=None):
|
async def async_step_setup_serial_manual_path(
|
||||||
|
self, user_input: dict[str, Any] | None = None
|
||||||
|
) -> FlowResult:
|
||||||
"""Set path manually."""
|
"""Set path manually."""
|
||||||
errors = {}
|
errors = {}
|
||||||
|
|
||||||
|
@ -78,7 +87,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
model, device_number = await self.validate_ultraheat(dev_path)
|
model, device_number = await self.validate_ultraheat(dev_path)
|
||||||
|
|
||||||
_LOGGER.debug("Got model %s and device_number %s", model, device_number)
|
_LOGGER.debug("Got model %s and device_number %s", model, device_number)
|
||||||
await self.async_set_unique_id(device_number)
|
await self.async_set_unique_id(f"{device_number}")
|
||||||
self._abort_if_unique_id_configured()
|
self._abort_if_unique_id_configured()
|
||||||
data = {
|
data = {
|
||||||
CONF_DEVICE: dev_path,
|
CONF_DEVICE: dev_path,
|
||||||
|
@ -90,48 +99,44 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
data=data,
|
data=data,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def validate_ultraheat(self, port: str):
|
async def validate_ultraheat(self, port: str) -> tuple[str, str]:
|
||||||
"""Validate the user input allows us to connect."""
|
"""Validate the user input allows us to connect."""
|
||||||
|
|
||||||
reader = UltraheatReader(port)
|
reader = ultraheat_api.UltraheatReader(port)
|
||||||
heat_meter = HeatMeterService(reader)
|
heat_meter = ultraheat_api.HeatMeterService(reader)
|
||||||
try:
|
try:
|
||||||
async with async_timeout.timeout(ULTRAHEAT_TIMEOUT):
|
async with async_timeout.timeout(ULTRAHEAT_TIMEOUT):
|
||||||
# validate and retrieve the model and device number for a unique id
|
# validate and retrieve the model and device number for a unique id
|
||||||
data = await self.hass.async_add_executor_job(heat_meter.read)
|
data = await self.hass.async_add_executor_job(heat_meter.read)
|
||||||
_LOGGER.debug("Got data from Ultraheat API: %s", data)
|
|
||||||
|
|
||||||
except Exception as err:
|
except (asyncio.TimeoutError, serial.serialutil.SerialException) as err:
|
||||||
_LOGGER.warning("Failed read data from: %s. %s", port, err)
|
_LOGGER.warning("Failed read data from: %s. %s", port, err)
|
||||||
raise CannotConnect(f"Error communicating with device: {err}") from err
|
raise CannotConnect(f"Error communicating with device: {err}") from err
|
||||||
|
|
||||||
_LOGGER.debug("Successfully connected to %s", port)
|
_LOGGER.debug("Successfully connected to %s. Got data: %s", port, data)
|
||||||
return data.model, data.device_number
|
return data.model, data.device_number
|
||||||
|
|
||||||
async def get_ports(self) -> dict:
|
|
||||||
"""Get the available ports."""
|
async def get_usb_ports(hass: HomeAssistant) -> dict[str, str]:
|
||||||
ports = await self.hass.async_add_executor_job(serial.tools.list_ports.comports)
|
"""Return a dict of USB ports and their friendly names."""
|
||||||
formatted_ports = {}
|
ports = await hass.async_add_executor_job(list_ports.comports)
|
||||||
|
port_descriptions = {}
|
||||||
for port in ports:
|
for port in ports:
|
||||||
formatted_ports[
|
# this prevents an issue with usb_device_from_port not working for ports without vid on RPi
|
||||||
port.device
|
if port.vid:
|
||||||
] = f"{port}, s/n: {port.serial_number or 'n/a'}" + (
|
usb_device = usb.usb_device_from_port(port)
|
||||||
f" - {port.manufacturer}" if port.manufacturer else ""
|
dev_path = usb.get_serial_by_id(usb_device.device)
|
||||||
|
human_name = usb.human_readable_device_name(
|
||||||
|
dev_path,
|
||||||
|
usb_device.serial_number,
|
||||||
|
usb_device.manufacturer,
|
||||||
|
usb_device.description,
|
||||||
|
usb_device.vid,
|
||||||
|
usb_device.pid,
|
||||||
)
|
)
|
||||||
formatted_ports[CONF_MANUAL_PATH] = CONF_MANUAL_PATH
|
port_descriptions[dev_path] = human_name
|
||||||
return formatted_ports
|
|
||||||
|
|
||||||
|
return port_descriptions
|
||||||
def get_serial_by_id(dev_path: str) -> str:
|
|
||||||
"""Return a /dev/serial/by-id match for given device if available."""
|
|
||||||
by_id = "/dev/serial/by-id"
|
|
||||||
if not os.path.isdir(by_id):
|
|
||||||
return dev_path
|
|
||||||
|
|
||||||
for path in (entry.path for entry in os.scandir(by_id) if entry.is_symlink()):
|
|
||||||
if os.path.realpath(path) == dev_path:
|
|
||||||
return path
|
|
||||||
return dev_path
|
|
||||||
|
|
||||||
|
|
||||||
class CannotConnect(HomeAssistantError):
|
class CannotConnect(HomeAssistantError):
|
||||||
|
|
|
@ -5,7 +5,15 @@ from homeassistant.components.sensor import (
|
||||||
SensorEntityDescription,
|
SensorEntityDescription,
|
||||||
SensorStateClass,
|
SensorStateClass,
|
||||||
)
|
)
|
||||||
from homeassistant.const import ENERGY_MEGA_WATT_HOUR, TEMP_CELSIUS, VOLUME_CUBIC_METERS
|
from homeassistant.const import (
|
||||||
|
ENERGY_MEGA_WATT_HOUR,
|
||||||
|
POWER_KILO_WATT,
|
||||||
|
TEMP_CELSIUS,
|
||||||
|
TIME_HOURS,
|
||||||
|
TIME_MINUTES,
|
||||||
|
VOLUME_CUBIC_METERS,
|
||||||
|
VOLUME_FLOW_RATE_CUBIC_METERS_PER_HOUR,
|
||||||
|
)
|
||||||
from homeassistant.helpers.entity import EntityCategory
|
from homeassistant.helpers.entity import EntityCategory
|
||||||
|
|
||||||
DOMAIN = "landisgyr_heat_meter"
|
DOMAIN = "landisgyr_heat_meter"
|
||||||
|
@ -26,6 +34,7 @@ HEAT_METER_SENSOR_TYPES = (
|
||||||
key="volume_usage_m3",
|
key="volume_usage_m3",
|
||||||
icon="mdi:fire",
|
icon="mdi:fire",
|
||||||
name="Volume usage",
|
name="Volume usage",
|
||||||
|
device_class=SensorDeviceClass.VOLUME,
|
||||||
native_unit_of_measurement=VOLUME_CUBIC_METERS,
|
native_unit_of_measurement=VOLUME_CUBIC_METERS,
|
||||||
state_class=SensorStateClass.TOTAL,
|
state_class=SensorStateClass.TOTAL,
|
||||||
),
|
),
|
||||||
|
@ -56,12 +65,14 @@ HEAT_METER_SENSOR_TYPES = (
|
||||||
key="volume_previous_year_m3",
|
key="volume_previous_year_m3",
|
||||||
icon="mdi:fire",
|
icon="mdi:fire",
|
||||||
name="Volume usage previous year",
|
name="Volume usage previous year",
|
||||||
|
device_class=SensorDeviceClass.VOLUME,
|
||||||
native_unit_of_measurement=VOLUME_CUBIC_METERS,
|
native_unit_of_measurement=VOLUME_CUBIC_METERS,
|
||||||
entity_category=EntityCategory.DIAGNOSTIC,
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
),
|
),
|
||||||
SensorEntityDescription(
|
SensorEntityDescription(
|
||||||
key="ownership_number",
|
key="ownership_number",
|
||||||
name="Ownership number",
|
name="Ownership number",
|
||||||
|
icon="mdi:identifier",
|
||||||
entity_category=EntityCategory.DIAGNOSTIC,
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
),
|
),
|
||||||
SensorEntityDescription(
|
SensorEntityDescription(
|
||||||
|
@ -73,41 +84,41 @@ HEAT_METER_SENSOR_TYPES = (
|
||||||
SensorEntityDescription(
|
SensorEntityDescription(
|
||||||
key="device_number",
|
key="device_number",
|
||||||
name="Device number",
|
name="Device number",
|
||||||
|
icon="mdi:identifier",
|
||||||
entity_category=EntityCategory.DIAGNOSTIC,
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
),
|
),
|
||||||
SensorEntityDescription(
|
SensorEntityDescription(
|
||||||
key="measurement_period_minutes",
|
key="measurement_period_minutes",
|
||||||
name="Measurement period minutes",
|
name="Measurement period minutes",
|
||||||
icon="mdi:clock-outline",
|
device_class=SensorDeviceClass.DURATION,
|
||||||
|
native_unit_of_measurement=TIME_MINUTES,
|
||||||
entity_category=EntityCategory.DIAGNOSTIC,
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
),
|
),
|
||||||
SensorEntityDescription(
|
SensorEntityDescription(
|
||||||
key="power_max_kw",
|
key="power_max_kw",
|
||||||
name="Power max",
|
name="Power max",
|
||||||
native_unit_of_measurement="kW",
|
native_unit_of_measurement=POWER_KILO_WATT,
|
||||||
icon="mdi:power-plug-outline",
|
|
||||||
device_class=SensorDeviceClass.POWER,
|
device_class=SensorDeviceClass.POWER,
|
||||||
entity_category=EntityCategory.DIAGNOSTIC,
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
),
|
),
|
||||||
SensorEntityDescription(
|
SensorEntityDescription(
|
||||||
key="power_max_previous_year_kw",
|
key="power_max_previous_year_kw",
|
||||||
name="Power max previous year",
|
name="Power max previous year",
|
||||||
native_unit_of_measurement="kW",
|
native_unit_of_measurement=POWER_KILO_WATT,
|
||||||
icon="mdi:power-plug-outline",
|
|
||||||
device_class=SensorDeviceClass.POWER,
|
device_class=SensorDeviceClass.POWER,
|
||||||
entity_category=EntityCategory.DIAGNOSTIC,
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
),
|
),
|
||||||
SensorEntityDescription(
|
SensorEntityDescription(
|
||||||
key="flowrate_max_m3ph",
|
key="flowrate_max_m3ph",
|
||||||
name="Flowrate max",
|
name="Flowrate max",
|
||||||
native_unit_of_measurement="m3ph",
|
native_unit_of_measurement=VOLUME_FLOW_RATE_CUBIC_METERS_PER_HOUR,
|
||||||
icon="mdi:water-outline",
|
icon="mdi:water-outline",
|
||||||
entity_category=EntityCategory.DIAGNOSTIC,
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
),
|
),
|
||||||
SensorEntityDescription(
|
SensorEntityDescription(
|
||||||
key="flowrate_max_previous_year_m3ph",
|
key="flowrate_max_previous_year_m3ph",
|
||||||
name="Flowrate max previous year",
|
name="Flowrate max previous year",
|
||||||
native_unit_of_measurement="m3ph",
|
native_unit_of_measurement=VOLUME_FLOW_RATE_CUBIC_METERS_PER_HOUR,
|
||||||
icon="mdi:water-outline",
|
icon="mdi:water-outline",
|
||||||
entity_category=EntityCategory.DIAGNOSTIC,
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
),
|
),
|
||||||
|
@ -115,7 +126,6 @@ HEAT_METER_SENSOR_TYPES = (
|
||||||
key="return_temperature_max_c",
|
key="return_temperature_max_c",
|
||||||
name="Return temperature max",
|
name="Return temperature max",
|
||||||
native_unit_of_measurement=TEMP_CELSIUS,
|
native_unit_of_measurement=TEMP_CELSIUS,
|
||||||
icon="mdi:thermometer",
|
|
||||||
device_class=SensorDeviceClass.TEMPERATURE,
|
device_class=SensorDeviceClass.TEMPERATURE,
|
||||||
entity_category=EntityCategory.DIAGNOSTIC,
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
),
|
),
|
||||||
|
@ -123,7 +133,6 @@ HEAT_METER_SENSOR_TYPES = (
|
||||||
key="return_temperature_max_previous_year_c",
|
key="return_temperature_max_previous_year_c",
|
||||||
name="Return temperature max previous year",
|
name="Return temperature max previous year",
|
||||||
native_unit_of_measurement=TEMP_CELSIUS,
|
native_unit_of_measurement=TEMP_CELSIUS,
|
||||||
icon="mdi:thermometer",
|
|
||||||
device_class=SensorDeviceClass.TEMPERATURE,
|
device_class=SensorDeviceClass.TEMPERATURE,
|
||||||
entity_category=EntityCategory.DIAGNOSTIC,
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
),
|
),
|
||||||
|
@ -131,7 +140,6 @@ HEAT_METER_SENSOR_TYPES = (
|
||||||
key="flow_temperature_max_c",
|
key="flow_temperature_max_c",
|
||||||
name="Flow temperature max",
|
name="Flow temperature max",
|
||||||
native_unit_of_measurement=TEMP_CELSIUS,
|
native_unit_of_measurement=TEMP_CELSIUS,
|
||||||
icon="mdi:thermometer",
|
|
||||||
device_class=SensorDeviceClass.TEMPERATURE,
|
device_class=SensorDeviceClass.TEMPERATURE,
|
||||||
entity_category=EntityCategory.DIAGNOSTIC,
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
),
|
),
|
||||||
|
@ -139,32 +147,35 @@ HEAT_METER_SENSOR_TYPES = (
|
||||||
key="flow_temperature_max_previous_year_c",
|
key="flow_temperature_max_previous_year_c",
|
||||||
name="Flow temperature max previous year",
|
name="Flow temperature max previous year",
|
||||||
native_unit_of_measurement=TEMP_CELSIUS,
|
native_unit_of_measurement=TEMP_CELSIUS,
|
||||||
icon="mdi:thermometer",
|
|
||||||
device_class=SensorDeviceClass.TEMPERATURE,
|
device_class=SensorDeviceClass.TEMPERATURE,
|
||||||
entity_category=EntityCategory.DIAGNOSTIC,
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
),
|
),
|
||||||
SensorEntityDescription(
|
SensorEntityDescription(
|
||||||
key="operating_hours",
|
key="operating_hours",
|
||||||
name="Operating hours",
|
name="Operating hours",
|
||||||
icon="mdi:clock-outline",
|
device_class=SensorDeviceClass.DURATION,
|
||||||
|
native_unit_of_measurement=TIME_HOURS,
|
||||||
entity_category=EntityCategory.DIAGNOSTIC,
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
),
|
),
|
||||||
SensorEntityDescription(
|
SensorEntityDescription(
|
||||||
key="flow_hours",
|
key="flow_hours",
|
||||||
name="Flow hours",
|
name="Flow hours",
|
||||||
icon="mdi:clock-outline",
|
device_class=SensorDeviceClass.DURATION,
|
||||||
|
native_unit_of_measurement=TIME_HOURS,
|
||||||
entity_category=EntityCategory.DIAGNOSTIC,
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
),
|
),
|
||||||
SensorEntityDescription(
|
SensorEntityDescription(
|
||||||
key="fault_hours",
|
key="fault_hours",
|
||||||
name="Fault hours",
|
name="Fault hours",
|
||||||
icon="mdi:clock-outline",
|
device_class=SensorDeviceClass.DURATION,
|
||||||
|
native_unit_of_measurement=TIME_HOURS,
|
||||||
entity_category=EntityCategory.DIAGNOSTIC,
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
),
|
),
|
||||||
SensorEntityDescription(
|
SensorEntityDescription(
|
||||||
key="fault_hours_previous_year",
|
key="fault_hours_previous_year",
|
||||||
name="Fault hours previous year",
|
name="Fault hours previous year",
|
||||||
icon="mdi:clock-outline",
|
device_class=SensorDeviceClass.DURATION,
|
||||||
|
native_unit_of_measurement=TIME_HOURS,
|
||||||
entity_category=EntityCategory.DIAGNOSTIC,
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
),
|
),
|
||||||
SensorEntityDescription(
|
SensorEntityDescription(
|
||||||
|
@ -189,7 +200,7 @@ HEAT_METER_SENSOR_TYPES = (
|
||||||
SensorEntityDescription(
|
SensorEntityDescription(
|
||||||
key="measuring_range_m3ph",
|
key="measuring_range_m3ph",
|
||||||
name="Measuring range",
|
name="Measuring range",
|
||||||
native_unit_of_measurement="m3ph",
|
native_unit_of_measurement=VOLUME_FLOW_RATE_CUBIC_METERS_PER_HOUR,
|
||||||
icon="mdi:water-outline",
|
icon="mdi:water-outline",
|
||||||
entity_category=EntityCategory.DIAGNOSTIC,
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
),
|
),
|
||||||
|
|
|
@ -9,5 +9,6 @@
|
||||||
"homekit": {},
|
"homekit": {},
|
||||||
"dependencies": [],
|
"dependencies": [],
|
||||||
"codeowners": ["@vpathuis"],
|
"codeowners": ["@vpathuis"],
|
||||||
|
"dependencies": ["usb"],
|
||||||
"iot_class": "local_polling"
|
"iot_class": "local_polling"
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,13 +4,8 @@ from __future__ import annotations
|
||||||
from dataclasses import asdict
|
from dataclasses import asdict
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from homeassistant.components.sensor import (
|
from homeassistant.components.sensor import RestoreSensor, SensorDeviceClass
|
||||||
ATTR_STATE_CLASS,
|
|
||||||
RestoreSensor,
|
|
||||||
SensorDeviceClass,
|
|
||||||
)
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import ATTR_DEVICE_CLASS, ATTR_UNIT_OF_MEASUREMENT
|
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.entity import DeviceInfo
|
from homeassistant.helpers.entity import DeviceInfo
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
@ -27,8 +22,6 @@ 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 the sensor platform."""
|
"""Set up the sensor platform."""
|
||||||
_LOGGER.info("The Landis+Gyr Heat Meter sensor platform is being set up!")
|
|
||||||
|
|
||||||
unique_id = entry.entry_id
|
unique_id = entry.entry_id
|
||||||
coordinator = hass.data[DOMAIN][entry.entry_id]
|
coordinator = hass.data[DOMAIN][entry.entry_id]
|
||||||
|
|
||||||
|
@ -44,7 +37,7 @@ async def async_setup_entry(
|
||||||
sensors = []
|
sensors = []
|
||||||
|
|
||||||
for description in HEAT_METER_SENSOR_TYPES:
|
for description in HEAT_METER_SENSOR_TYPES:
|
||||||
sensors.append(HeatMeterSensor(coordinator, unique_id, description, device))
|
sensors.append(HeatMeterSensor(coordinator, description, device))
|
||||||
|
|
||||||
async_add_entities(sensors)
|
async_add_entities(sensors)
|
||||||
|
|
||||||
|
@ -52,24 +45,16 @@ async def async_setup_entry(
|
||||||
class HeatMeterSensor(CoordinatorEntity, RestoreSensor):
|
class HeatMeterSensor(CoordinatorEntity, RestoreSensor):
|
||||||
"""Representation of a Sensor."""
|
"""Representation of a Sensor."""
|
||||||
|
|
||||||
def __init__(self, coordinator, unique_id, description, device):
|
def __init__(self, coordinator, description, device):
|
||||||
"""Set up the sensor with the initial values."""
|
"""Set up the sensor with the initial values."""
|
||||||
super().__init__(coordinator)
|
super().__init__(coordinator)
|
||||||
self.key = description.key
|
self.key = description.key
|
||||||
self._attr_unique_id = f"{DOMAIN}_{unique_id}_{description.key}"
|
self._attr_unique_id = (
|
||||||
self._attr_name = "Heat Meter " + description.name
|
f"{coordinator.config_entry.data['device_number']}_{description.key}"
|
||||||
if hasattr(description, "icon"):
|
|
||||||
self._attr_icon = description.icon
|
|
||||||
if hasattr(description, "entity_category"):
|
|
||||||
self._attr_entity_category = description.entity_category
|
|
||||||
if hasattr(description, ATTR_STATE_CLASS):
|
|
||||||
self._attr_state_class = description.state_class
|
|
||||||
if hasattr(description, ATTR_DEVICE_CLASS):
|
|
||||||
self._attr_device_class = description.device_class
|
|
||||||
if hasattr(description, ATTR_UNIT_OF_MEASUREMENT):
|
|
||||||
self._attr_native_unit_of_measurement = (
|
|
||||||
description.native_unit_of_measurement
|
|
||||||
)
|
)
|
||||||
|
self._attr_name = f"Heat Meter {description.name}"
|
||||||
|
self.entity_description = description
|
||||||
|
|
||||||
self._attr_device_info = device
|
self._attr_device_info = device
|
||||||
self._attr_should_poll = bool(self.key in ("heat_usage", "heat_previous_year"))
|
self._attr_should_poll = bool(self.key in ("heat_usage", "heat_previous_year"))
|
||||||
|
|
||||||
|
|
|
@ -12,10 +12,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
|
||||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
|
||||||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
|
||||||
},
|
|
||||||
"abort": {
|
"abort": {
|
||||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
"""Test the Landis + Gyr Heat Meter config flow."""
|
"""Test the Landis + Gyr Heat Meter config flow."""
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
import serial
|
||||||
import serial.tools.list_ports
|
import serial.tools.list_ports
|
||||||
|
|
||||||
from homeassistant import config_entries
|
from homeassistant import config_entries
|
||||||
|
@ -9,6 +10,10 @@ from homeassistant.components.landisgyr_heat_meter import DOMAIN
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.data_entry_flow import FlowResultType
|
from homeassistant.data_entry_flow import FlowResultType
|
||||||
|
|
||||||
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
|
API_HEAT_METER_SERVICE = "homeassistant.components.landisgyr_heat_meter.config_flow.ultraheat_api.HeatMeterService"
|
||||||
|
|
||||||
|
|
||||||
def mock_serial_port():
|
def mock_serial_port():
|
||||||
"""Mock of a serial port."""
|
"""Mock of a serial port."""
|
||||||
|
@ -17,6 +22,8 @@ def mock_serial_port():
|
||||||
port.manufacturer = "Virtual serial port"
|
port.manufacturer = "Virtual serial port"
|
||||||
port.device = "/dev/ttyUSB1234"
|
port.device = "/dev/ttyUSB1234"
|
||||||
port.description = "Some serial port"
|
port.description = "Some serial port"
|
||||||
|
port.pid = 9876
|
||||||
|
port.vid = 5678
|
||||||
|
|
||||||
return port
|
return port
|
||||||
|
|
||||||
|
@ -29,7 +36,7 @@ class MockUltraheatRead:
|
||||||
device_number: str
|
device_number: str
|
||||||
|
|
||||||
|
|
||||||
@patch("homeassistant.components.landisgyr_heat_meter.config_flow.HeatMeterService")
|
@patch(API_HEAT_METER_SERVICE)
|
||||||
async def test_manual_entry(mock_heat_meter, hass: HomeAssistant) -> None:
|
async def test_manual_entry(mock_heat_meter, hass: HomeAssistant) -> None:
|
||||||
"""Test manual entry."""
|
"""Test manual entry."""
|
||||||
|
|
||||||
|
@ -67,7 +74,7 @@ async def test_manual_entry(mock_heat_meter, hass: HomeAssistant) -> None:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@patch("homeassistant.components.landisgyr_heat_meter.config_flow.HeatMeterService")
|
@patch(API_HEAT_METER_SERVICE)
|
||||||
@patch("serial.tools.list_ports.comports", return_value=[mock_serial_port()])
|
@patch("serial.tools.list_ports.comports", return_value=[mock_serial_port()])
|
||||||
async def test_list_entry(mock_port, mock_heat_meter, hass: HomeAssistant) -> None:
|
async def test_list_entry(mock_port, mock_heat_meter, hass: HomeAssistant) -> None:
|
||||||
"""Test select from list entry."""
|
"""Test select from list entry."""
|
||||||
|
@ -94,11 +101,11 @@ async def test_list_entry(mock_port, mock_heat_meter, hass: HomeAssistant) -> No
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@patch("homeassistant.components.landisgyr_heat_meter.config_flow.HeatMeterService")
|
@patch(API_HEAT_METER_SERVICE)
|
||||||
async def test_manual_entry_fail(mock_heat_meter, hass: HomeAssistant) -> None:
|
async def test_manual_entry_fail(mock_heat_meter, hass: HomeAssistant) -> None:
|
||||||
"""Test manual entry fails."""
|
"""Test manual entry fails."""
|
||||||
|
|
||||||
mock_heat_meter().read.side_effect = Exception
|
mock_heat_meter().read.side_effect = serial.serialutil.SerialException
|
||||||
|
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||||
|
@ -128,12 +135,12 @@ async def test_manual_entry_fail(mock_heat_meter, hass: HomeAssistant) -> None:
|
||||||
assert result["errors"] == {"base": "cannot_connect"}
|
assert result["errors"] == {"base": "cannot_connect"}
|
||||||
|
|
||||||
|
|
||||||
@patch("homeassistant.components.landisgyr_heat_meter.config_flow.HeatMeterService")
|
@patch(API_HEAT_METER_SERVICE)
|
||||||
@patch("serial.tools.list_ports.comports", return_value=[mock_serial_port()])
|
@patch("serial.tools.list_ports.comports", return_value=[mock_serial_port()])
|
||||||
async def test_list_entry_fail(mock_port, mock_heat_meter, hass: HomeAssistant) -> None:
|
async def test_list_entry_fail(mock_port, mock_heat_meter, hass: HomeAssistant) -> None:
|
||||||
"""Test select from list entry fails."""
|
"""Test select from list entry fails."""
|
||||||
|
|
||||||
mock_heat_meter().read.side_effect = Exception
|
mock_heat_meter().read.side_effect = serial.serialutil.SerialException
|
||||||
port = mock_serial_port()
|
port = mock_serial_port()
|
||||||
|
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
@ -151,77 +158,36 @@ async def test_list_entry_fail(mock_port, mock_heat_meter, hass: HomeAssistant)
|
||||||
assert result["errors"] == {"base": "cannot_connect"}
|
assert result["errors"] == {"base": "cannot_connect"}
|
||||||
|
|
||||||
|
|
||||||
@patch("homeassistant.components.landisgyr_heat_meter.config_flow.HeatMeterService")
|
@patch(API_HEAT_METER_SERVICE)
|
||||||
@patch("serial.tools.list_ports.comports", return_value=[mock_serial_port()])
|
@patch("serial.tools.list_ports.comports", return_value=[mock_serial_port()])
|
||||||
async def test_get_serial_by_id_realpath(
|
async def test_already_configured(
|
||||||
mock_port, mock_heat_meter, hass: HomeAssistant
|
mock_port, mock_heat_meter, hass: HomeAssistant
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test getting the serial path name."""
|
"""Test we abort if the Heat Meter is already configured."""
|
||||||
|
|
||||||
|
# create and add existing entry
|
||||||
|
entry_data = {
|
||||||
|
"device": "/dev/USB0",
|
||||||
|
"model": "LUGCUH50",
|
||||||
|
"device_number": "123456789",
|
||||||
|
}
|
||||||
|
mock_entry = MockConfigEntry(domain=DOMAIN, unique_id="123456789", data=entry_data)
|
||||||
|
mock_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
await hass.config_entries.async_setup(mock_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
# run flow and see if it aborts
|
||||||
mock_heat_meter().read.return_value = MockUltraheatRead("LUGCUH50", "123456789")
|
mock_heat_meter().read.return_value = MockUltraheatRead("LUGCUH50", "123456789")
|
||||||
port = mock_serial_port()
|
port = mock_serial_port()
|
||||||
|
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||||
)
|
)
|
||||||
assert result["type"] == FlowResultType.FORM
|
|
||||||
assert result["step_id"] == "user"
|
|
||||||
assert result["errors"] == {}
|
|
||||||
|
|
||||||
scandir = [MagicMock(), MagicMock()]
|
|
||||||
scandir[0].path = "/dev/ttyUSB1234"
|
|
||||||
scandir[0].is_symlink.return_value = True
|
|
||||||
scandir[1].path = "/dev/ttyUSB5678"
|
|
||||||
scandir[1].is_symlink.return_value = True
|
|
||||||
|
|
||||||
with patch("os.path") as path:
|
|
||||||
with patch("os.scandir", return_value=scandir):
|
|
||||||
path.isdir.return_value = True
|
|
||||||
path.realpath.side_effect = ["/dev/ttyUSB1234", "/dev/ttyUSB5678"]
|
|
||||||
result = await hass.config_entries.flow.async_configure(
|
result = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"], {"device": port.device}
|
result["flow_id"], {"device": port.device}
|
||||||
)
|
)
|
||||||
assert result["type"] == FlowResultType.CREATE_ENTRY
|
|
||||||
assert result["title"] == "LUGCUH50"
|
|
||||||
assert result["data"] == {
|
|
||||||
"device": port.device,
|
|
||||||
"model": "LUGCUH50",
|
|
||||||
"device_number": "123456789",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
assert result["type"] == FlowResultType.ABORT
|
||||||
@patch("homeassistant.components.landisgyr_heat_meter.config_flow.HeatMeterService")
|
assert result["reason"] == "already_configured"
|
||||||
@patch("serial.tools.list_ports.comports", return_value=[mock_serial_port()])
|
|
||||||
async def test_get_serial_by_id_dev_path(
|
|
||||||
mock_port, mock_heat_meter, hass: HomeAssistant
|
|
||||||
) -> None:
|
|
||||||
"""Test getting the serial path name with no realpath result."""
|
|
||||||
|
|
||||||
mock_heat_meter().read.return_value = MockUltraheatRead("LUGCUH50", "123456789")
|
|
||||||
port = mock_serial_port()
|
|
||||||
|
|
||||||
result = await hass.config_entries.flow.async_init(
|
|
||||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
|
||||||
)
|
|
||||||
assert result["type"] == FlowResultType.FORM
|
|
||||||
assert result["step_id"] == "user"
|
|
||||||
assert result["errors"] == {}
|
|
||||||
|
|
||||||
scandir = [MagicMock()]
|
|
||||||
scandir[0].path.return_value = "/dev/serial/by-id/USB5678"
|
|
||||||
scandir[0].is_symlink.return_value = True
|
|
||||||
|
|
||||||
with patch("os.path") as path:
|
|
||||||
with patch("os.scandir", return_value=scandir):
|
|
||||||
path.isdir.return_value = True
|
|
||||||
path.realpath.side_effect = ["/dev/ttyUSB5678"]
|
|
||||||
result = await hass.config_entries.flow.async_configure(
|
|
||||||
result["flow_id"], {"device": port.device}
|
|
||||||
)
|
|
||||||
assert result["type"] == FlowResultType.CREATE_ENTRY
|
|
||||||
assert result["title"] == "LUGCUH50"
|
|
||||||
assert result["data"] == {
|
|
||||||
"device": port.device,
|
|
||||||
"model": "LUGCUH50",
|
|
||||||
"device_number": "123456789",
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,22 +1,78 @@
|
||||||
"""Test the Landis + Gyr Heat Meter init."""
|
"""Test the Landis + Gyr Heat Meter init."""
|
||||||
|
|
||||||
from homeassistant.const import CONF_DEVICE
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from homeassistant.components.landisgyr_heat_meter.const import (
|
||||||
|
DOMAIN as LANDISGYR_HEAT_METER_DOMAIN,
|
||||||
|
)
|
||||||
|
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
||||||
|
from homeassistant.helpers import entity_registry as er
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
|
API_HEAT_METER_SERVICE = (
|
||||||
async def test_unload_entry(hass):
|
"homeassistant.components.landisgyr_heat_meter.ultraheat_api.HeatMeterService"
|
||||||
"""Test removing config entry."""
|
|
||||||
entry = MockConfigEntry(
|
|
||||||
domain="landisgyr_heat_meter",
|
|
||||||
title="LUGCUH50",
|
|
||||||
data={CONF_DEVICE: "/dev/1234"},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
entry.add_to_hass(hass)
|
|
||||||
|
|
||||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
@patch(API_HEAT_METER_SERVICE)
|
||||||
|
async def test_unload_entry(_, hass):
|
||||||
|
"""Test removing config entry."""
|
||||||
|
mock_entry_data = {
|
||||||
|
"device": "/dev/USB0",
|
||||||
|
"model": "LUGCUH50",
|
||||||
|
"device_number": "12345",
|
||||||
|
}
|
||||||
|
mock_entry = MockConfigEntry(
|
||||||
|
domain="landisgyr_heat_meter",
|
||||||
|
title="LUGCUH50",
|
||||||
|
entry_id="987654321",
|
||||||
|
data=mock_entry_data,
|
||||||
|
)
|
||||||
|
mock_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
assert await hass.config_entries.async_setup(mock_entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert "landisgyr_heat_meter" in hass.config.components
|
assert "landisgyr_heat_meter" in hass.config.components
|
||||||
|
|
||||||
assert await hass.config_entries.async_remove(entry.entry_id)
|
assert await hass.config_entries.async_remove(mock_entry.entry_id)
|
||||||
|
|
||||||
|
|
||||||
|
@patch(API_HEAT_METER_SERVICE)
|
||||||
|
async def test_migrate_entry(_, hass):
|
||||||
|
"""Test successful migration of entry data from version 1 to 2."""
|
||||||
|
|
||||||
|
mock_entry_data = {
|
||||||
|
"device": "/dev/USB0",
|
||||||
|
"model": "LUGCUH50",
|
||||||
|
"device_number": "12345",
|
||||||
|
}
|
||||||
|
mock_entry = MockConfigEntry(
|
||||||
|
domain="landisgyr_heat_meter",
|
||||||
|
title="LUGCUH50",
|
||||||
|
entry_id="987654321",
|
||||||
|
data=mock_entry_data,
|
||||||
|
)
|
||||||
|
assert mock_entry.data == mock_entry_data
|
||||||
|
assert mock_entry.version == 1
|
||||||
|
|
||||||
|
mock_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
# Create entity entry to migrate to new unique ID
|
||||||
|
registry = er.async_get(hass)
|
||||||
|
registry.async_get_or_create(
|
||||||
|
SENSOR_DOMAIN,
|
||||||
|
LANDISGYR_HEAT_METER_DOMAIN,
|
||||||
|
"landisgyr_heat_meter_987654321_measuring_range_m3ph",
|
||||||
|
suggested_object_id="heat_meter_measuring_range",
|
||||||
|
config_entry=mock_entry,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert await hass.config_entries.async_setup(mock_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert "landisgyr_heat_meter" in hass.config.components
|
||||||
|
|
||||||
|
# Check if entity unique id is migrated successfully
|
||||||
|
assert mock_entry.version == 2
|
||||||
|
entity = registry.async_get("sensor.heat_meter_measuring_range")
|
||||||
|
assert entity.unique_id == "12345_measuring_range_m3ph"
|
||||||
|
|
|
@ -42,7 +42,7 @@ class MockHeatMeterResponse:
|
||||||
meter_date_time: datetime.datetime
|
meter_date_time: datetime.datetime
|
||||||
|
|
||||||
|
|
||||||
@patch("homeassistant.components.landisgyr_heat_meter.HeatMeterService")
|
@patch("homeassistant.components.landisgyr_heat_meter.ultraheat_api.HeatMeterService")
|
||||||
async def test_create_sensors(mock_heat_meter, hass):
|
async def test_create_sensors(mock_heat_meter, hass):
|
||||||
"""Test sensor."""
|
"""Test sensor."""
|
||||||
entry_data = {
|
entry_data = {
|
||||||
|
@ -107,7 +107,7 @@ async def test_create_sensors(mock_heat_meter, hass):
|
||||||
assert entity_registry_entry.entity_category == EntityCategory.DIAGNOSTIC
|
assert entity_registry_entry.entity_category == EntityCategory.DIAGNOSTIC
|
||||||
|
|
||||||
|
|
||||||
@patch("homeassistant.components.landisgyr_heat_meter.HeatMeterService")
|
@patch("homeassistant.components.landisgyr_heat_meter.ultraheat_api.HeatMeterService")
|
||||||
async def test_restore_state(mock_heat_meter, hass):
|
async def test_restore_state(mock_heat_meter, hass):
|
||||||
"""Test sensor restore state."""
|
"""Test sensor restore state."""
|
||||||
# Home assistant is not running yet
|
# Home assistant is not running yet
|
||||||
|
@ -177,7 +177,6 @@ async def test_restore_state(mock_heat_meter, hass):
|
||||||
mock_entry.add_to_hass(hass)
|
mock_entry.add_to_hass(hass)
|
||||||
|
|
||||||
await hass.config_entries.async_setup(mock_entry.entry_id)
|
await hass.config_entries.async_setup(mock_entry.entry_id)
|
||||||
await async_setup_component(hass, HA_DOMAIN, {})
|
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
# restore from cache
|
# restore from cache
|
||||||
|
@ -195,6 +194,5 @@ async def test_restore_state(mock_heat_meter, hass):
|
||||||
|
|
||||||
state = hass.states.get("sensor.heat_meter_device_number")
|
state = hass.states.get("sensor.heat_meter_device_number")
|
||||||
assert state
|
assert state
|
||||||
print("STATE IS: ", state)
|
|
||||||
assert state.state == "devicenr_789"
|
assert state.state == "devicenr_789"
|
||||||
assert state.attributes.get(ATTR_STATE_CLASS) is None
|
assert state.attributes.get(ATTR_STATE_CLASS) is None
|
||||||
|
|
Loading…
Reference in New Issue