Compare commits
5 commits
Author | SHA1 | Date | |
---|---|---|---|
|
3ea6d3b139 | ||
|
29c68f3615 | ||
|
182f8b33f7 | ||
|
a40eeefc8c | ||
|
e07ca1a1bc |
10 changed files with 227 additions and 22 deletions
|
@ -14,6 +14,7 @@ from homeassistant.components.climate import (
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
|
ATTR_ENTITY_ID,
|
||||||
ATTR_TEMPERATURE,
|
ATTR_TEMPERATURE,
|
||||||
CONF_IP_ADDRESS,
|
CONF_IP_ADDRESS,
|
||||||
CONF_USERNAME,
|
CONF_USERNAME,
|
||||||
|
@ -23,12 +24,16 @@ from homeassistant.const import (
|
||||||
from homeassistant.core import HomeAssistant, ServiceCall, callback
|
from homeassistant.core import HomeAssistant, ServiceCall, callback
|
||||||
from homeassistant.helpers import config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceInfo
|
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceInfo
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import (
|
||||||
|
AddEntitiesCallback,
|
||||||
|
async_get_current_platform,
|
||||||
|
)
|
||||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
ATTR_AWAY_TEMP,
|
ATTR_AWAY_TEMP,
|
||||||
ATTR_COMFORT_TEMP,
|
ATTR_COMFORT_TEMP,
|
||||||
|
ATTR_MAX_HEATING_POWER,
|
||||||
ATTR_ROOM_NAME,
|
ATTR_ROOM_NAME,
|
||||||
ATTR_SLEEP_TEMP,
|
ATTR_SLEEP_TEMP,
|
||||||
CLOUD,
|
CLOUD,
|
||||||
|
@ -38,6 +43,7 @@ from .const import (
|
||||||
MANUFACTURER,
|
MANUFACTURER,
|
||||||
MAX_TEMP,
|
MAX_TEMP,
|
||||||
MIN_TEMP,
|
MIN_TEMP,
|
||||||
|
SERVICE_MAX_HEATING_POWER,
|
||||||
SERVICE_SET_ROOM_TEMP,
|
SERVICE_SET_ROOM_TEMP,
|
||||||
)
|
)
|
||||||
from .coordinator import MillDataUpdateCoordinator
|
from .coordinator import MillDataUpdateCoordinator
|
||||||
|
@ -84,6 +90,21 @@ async def async_setup_entry(
|
||||||
DOMAIN, SERVICE_SET_ROOM_TEMP, set_room_temp, schema=SET_ROOM_TEMP_SCHEMA
|
DOMAIN, SERVICE_SET_ROOM_TEMP, set_room_temp, schema=SET_ROOM_TEMP_SCHEMA
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def max_heating_power(entity: MillHeater, service: ServiceCall) -> None:
|
||||||
|
"""Limit heating power."""
|
||||||
|
await mill_data_coordinator.mill_data_connection.max_heating_power(
|
||||||
|
entity.heater_id, service.data[ATTR_MAX_HEATING_POWER]
|
||||||
|
)
|
||||||
|
|
||||||
|
async_get_current_platform().async_register_entity_service(
|
||||||
|
SERVICE_MAX_HEATING_POWER,
|
||||||
|
schema={
|
||||||
|
vol.Required(ATTR_ENTITY_ID): cv.entity_id,
|
||||||
|
vol.Required(ATTR_MAX_HEATING_POWER): vol.Range(min=0, max=2000),
|
||||||
|
},
|
||||||
|
func=max_heating_power,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class MillHeater(CoordinatorEntity[MillDataUpdateCoordinator], ClimateEntity):
|
class MillHeater(CoordinatorEntity[MillDataUpdateCoordinator], ClimateEntity):
|
||||||
"""Representation of a Mill Thermostat device."""
|
"""Representation of a Mill Thermostat device."""
|
||||||
|
@ -111,7 +132,7 @@ class MillHeater(CoordinatorEntity[MillDataUpdateCoordinator], ClimateEntity):
|
||||||
|
|
||||||
self._available = False
|
self._available = False
|
||||||
|
|
||||||
self._id = heater.device_id
|
self.heater_id = heater.device_id
|
||||||
self._attr_unique_id = heater.device_id
|
self._attr_unique_id = heater.device_id
|
||||||
self._attr_device_info = DeviceInfo(
|
self._attr_device_info = DeviceInfo(
|
||||||
identifiers={(DOMAIN, heater.device_id)},
|
identifiers={(DOMAIN, heater.device_id)},
|
||||||
|
@ -127,7 +148,10 @@ class MillHeater(CoordinatorEntity[MillDataUpdateCoordinator], ClimateEntity):
|
||||||
if (temperature := kwargs.get(ATTR_TEMPERATURE)) is None:
|
if (temperature := kwargs.get(ATTR_TEMPERATURE)) is None:
|
||||||
return
|
return
|
||||||
await self.coordinator.mill_data_connection.set_heater_temp(
|
await self.coordinator.mill_data_connection.set_heater_temp(
|
||||||
self._id, float(temperature)
|
self.heater_id, float(temperature)
|
||||||
|
)
|
||||||
|
await self.coordinator.mill_data_connection.fetch_historic_energy_usage(
|
||||||
|
self.heater_id
|
||||||
)
|
)
|
||||||
await self.coordinator.async_request_refresh()
|
await self.coordinator.async_request_refresh()
|
||||||
|
|
||||||
|
@ -135,12 +159,12 @@ class MillHeater(CoordinatorEntity[MillDataUpdateCoordinator], ClimateEntity):
|
||||||
"""Set new target hvac mode."""
|
"""Set new target hvac mode."""
|
||||||
if hvac_mode == HVACMode.HEAT:
|
if hvac_mode == HVACMode.HEAT:
|
||||||
await self.coordinator.mill_data_connection.heater_control(
|
await self.coordinator.mill_data_connection.heater_control(
|
||||||
self._id, power_status=True
|
self.heater_id, power_status=True
|
||||||
)
|
)
|
||||||
await self.coordinator.async_request_refresh()
|
await self.coordinator.async_request_refresh()
|
||||||
elif hvac_mode == HVACMode.OFF:
|
elif hvac_mode == HVACMode.OFF:
|
||||||
await self.coordinator.mill_data_connection.heater_control(
|
await self.coordinator.mill_data_connection.heater_control(
|
||||||
self._id, power_status=False
|
self.heater_id, power_status=False
|
||||||
)
|
)
|
||||||
await self.coordinator.async_request_refresh()
|
await self.coordinator.async_request_refresh()
|
||||||
|
|
||||||
|
@ -152,7 +176,7 @@ class MillHeater(CoordinatorEntity[MillDataUpdateCoordinator], ClimateEntity):
|
||||||
@callback
|
@callback
|
||||||
def _handle_coordinator_update(self) -> None:
|
def _handle_coordinator_update(self) -> None:
|
||||||
"""Handle updated data from the coordinator."""
|
"""Handle updated data from the coordinator."""
|
||||||
self._update_attr(self.coordinator.data[self._id])
|
self._update_attr(self.coordinator.data[self.heater_id])
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
ATTR_AWAY_TEMP = "away_temp"
|
ATTR_AWAY_TEMP = "away_temp"
|
||||||
ATTR_COMFORT_TEMP = "comfort_temp"
|
ATTR_COMFORT_TEMP = "comfort_temp"
|
||||||
|
ATTR_MAX_HEATING_POWER = "max_heating_power"
|
||||||
ATTR_ROOM_NAME = "room_name"
|
ATTR_ROOM_NAME = "room_name"
|
||||||
ATTR_SLEEP_TEMP = "sleep_temp"
|
ATTR_SLEEP_TEMP = "sleep_temp"
|
||||||
BATTERY = "battery"
|
BATTERY = "battery"
|
||||||
|
@ -9,6 +10,8 @@ CLOUD = "Cloud"
|
||||||
CONNECTION_TYPE = "connection_type"
|
CONNECTION_TYPE = "connection_type"
|
||||||
CONSUMPTION_TODAY = "day_consumption"
|
CONSUMPTION_TODAY = "day_consumption"
|
||||||
CONSUMPTION_YEAR = "year_consumption"
|
CONSUMPTION_YEAR = "year_consumption"
|
||||||
|
CONTROL_SIGNAL = "control_signal"
|
||||||
|
CURRENT_POWER = "current_power"
|
||||||
DOMAIN = "mill"
|
DOMAIN = "mill"
|
||||||
ECO2 = "eco2"
|
ECO2 = "eco2"
|
||||||
HUMIDITY = "humidity"
|
HUMIDITY = "humidity"
|
||||||
|
@ -17,5 +20,6 @@ MANUFACTURER = "Mill"
|
||||||
MAX_TEMP = 35
|
MAX_TEMP = 35
|
||||||
MIN_TEMP = 5
|
MIN_TEMP = 5
|
||||||
SERVICE_SET_ROOM_TEMP = "set_room_temperature"
|
SERVICE_SET_ROOM_TEMP = "set_room_temperature"
|
||||||
|
SERVICE_MAX_HEATING_POWER = "max_heating_power"
|
||||||
TEMPERATURE = "current_temp"
|
TEMPERATURE = "current_temp"
|
||||||
TVOC = "tvoc"
|
TVOC = "tvoc"
|
||||||
|
|
|
@ -4,12 +4,22 @@ from __future__ import annotations
|
||||||
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import logging
|
import logging
|
||||||
|
from typing import cast
|
||||||
|
|
||||||
from mill import Mill
|
from mill import Heater, Mill
|
||||||
from mill_local import Mill as MillLocal
|
from mill_local import Mill as MillLocal
|
||||||
|
|
||||||
|
from homeassistant.components.recorder import get_instance
|
||||||
|
from homeassistant.components.recorder.models import StatisticData, StatisticMetaData
|
||||||
|
from homeassistant.components.recorder.statistics import (
|
||||||
|
async_add_external_statistics,
|
||||||
|
get_last_statistics,
|
||||||
|
statistics_during_period,
|
||||||
|
)
|
||||||
|
from homeassistant.const import UnitOfEnergy
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||||
|
from homeassistant.util import dt as dt_util, slugify
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
|
|
||||||
|
@ -28,11 +38,98 @@ class MillDataUpdateCoordinator(DataUpdateCoordinator):
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize global Mill data updater."""
|
"""Initialize global Mill data updater."""
|
||||||
self.mill_data_connection = mill_data_connection
|
self.mill_data_connection = mill_data_connection
|
||||||
|
self._last_stats_time = dt_util.utcnow() - timedelta(days=1)
|
||||||
|
|
||||||
super().__init__(
|
super().__init__(
|
||||||
hass,
|
hass,
|
||||||
_LOGGER,
|
_LOGGER,
|
||||||
name=DOMAIN,
|
name=DOMAIN,
|
||||||
update_method=mill_data_connection.fetch_heater_and_sensor_data,
|
|
||||||
update_interval=update_interval,
|
update_interval=update_interval,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def _async_update_data(self) -> dict:
|
||||||
|
"""Update data via API."""
|
||||||
|
data = await self.mill_data_connection.fetch_heater_and_sensor_data()
|
||||||
|
if isinstance(self.mill_data_connection, Mill):
|
||||||
|
await self._insert_statistics()
|
||||||
|
return data
|
||||||
|
|
||||||
|
async def _insert_statistics(self) -> None:
|
||||||
|
"""Insert Mill statistics."""
|
||||||
|
now = dt_util.utcnow()
|
||||||
|
if self._last_stats_time > now - timedelta(hours=1):
|
||||||
|
return
|
||||||
|
for dev_id, heater in self.mill_data_connection.devices.items():
|
||||||
|
if not isinstance(heater, Heater):
|
||||||
|
continue
|
||||||
|
statistic_id = f"{DOMAIN}:energy_{slugify(dev_id)}"
|
||||||
|
|
||||||
|
last_stats = await get_instance(self.hass).async_add_executor_job(
|
||||||
|
get_last_statistics, self.hass, 1, statistic_id, True, set()
|
||||||
|
)
|
||||||
|
|
||||||
|
if not last_stats or not last_stats.get(statistic_id):
|
||||||
|
hourly_data = (
|
||||||
|
await self.mill_data_connection.fetch_historic_energy_usage(dev_id)
|
||||||
|
)
|
||||||
|
hourly_data = dict(sorted(hourly_data.items(), key=lambda x: x[0]))
|
||||||
|
_sum = 0.0
|
||||||
|
last_stats_time = None
|
||||||
|
else:
|
||||||
|
hourly_data = (
|
||||||
|
await self.mill_data_connection.fetch_historic_energy_usage(
|
||||||
|
dev_id,
|
||||||
|
n_days=(
|
||||||
|
now
|
||||||
|
- dt_util.utc_from_timestamp(
|
||||||
|
last_stats[statistic_id][0]["start"]
|
||||||
|
)
|
||||||
|
).days
|
||||||
|
+ 2,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if not hourly_data:
|
||||||
|
return
|
||||||
|
hourly_data = dict(sorted(hourly_data.items(), key=lambda x: x[0]))
|
||||||
|
start_time = next(iter(hourly_data))
|
||||||
|
|
||||||
|
stats = await get_instance(self.hass).async_add_executor_job(
|
||||||
|
statistics_during_period,
|
||||||
|
self.hass,
|
||||||
|
start_time,
|
||||||
|
None,
|
||||||
|
{statistic_id},
|
||||||
|
"hour",
|
||||||
|
None,
|
||||||
|
{"sum", "state"},
|
||||||
|
)
|
||||||
|
stat = stats[statistic_id][0]
|
||||||
|
|
||||||
|
_sum = cast(float, stat["sum"])
|
||||||
|
last_stats_time = dt_util.utc_from_timestamp(stat["start"])
|
||||||
|
|
||||||
|
statistics = []
|
||||||
|
|
||||||
|
for start, state in hourly_data.items():
|
||||||
|
if state is None:
|
||||||
|
continue
|
||||||
|
if last_stats_time and (start < last_stats_time or start > now):
|
||||||
|
continue
|
||||||
|
_sum += state
|
||||||
|
statistics.append(
|
||||||
|
StatisticData(
|
||||||
|
start=start,
|
||||||
|
state=state,
|
||||||
|
sum=_sum,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
metadata = StatisticMetaData(
|
||||||
|
has_mean=False,
|
||||||
|
has_sum=True,
|
||||||
|
name=f"{heater.name}",
|
||||||
|
source=DOMAIN,
|
||||||
|
statistic_id=statistic_id,
|
||||||
|
unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||||
|
)
|
||||||
|
async_add_external_statistics(self.hass, metadata, statistics)
|
||||||
|
self._last_stats_time = now.replace(minute=0, second=0)
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
"services": {
|
"services": {
|
||||||
"set_room_temperature": {
|
"set_room_temperature": {
|
||||||
"service": "mdi:thermometer"
|
"service": "mdi:thermometer"
|
||||||
|
},
|
||||||
|
"max_heating_power": {
|
||||||
|
"service": "mdi:power"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
"name": "Mill",
|
"name": "Mill",
|
||||||
"codeowners": ["@danielhiversen"],
|
"codeowners": ["@danielhiversen"],
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
|
"dependencies": ["recorder"],
|
||||||
"documentation": "https://www.home-assistant.io/integrations/mill",
|
"documentation": "https://www.home-assistant.io/integrations/mill",
|
||||||
"iot_class": "local_polling",
|
"iot_class": "local_polling",
|
||||||
"loggers": ["mill", "mill_local"],
|
"loggers": ["mill", "mill_local"],
|
||||||
|
|
|
@ -33,6 +33,8 @@ from .const import (
|
||||||
CONNECTION_TYPE,
|
CONNECTION_TYPE,
|
||||||
CONSUMPTION_TODAY,
|
CONSUMPTION_TODAY,
|
||||||
CONSUMPTION_YEAR,
|
CONSUMPTION_YEAR,
|
||||||
|
CONTROL_SIGNAL,
|
||||||
|
CURRENT_POWER,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
ECO2,
|
ECO2,
|
||||||
HUMIDITY,
|
HUMIDITY,
|
||||||
|
@ -57,6 +59,19 @@ HEATER_SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
|
||||||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||||
),
|
),
|
||||||
|
SensorEntityDescription(
|
||||||
|
key=CURRENT_POWER,
|
||||||
|
translation_key="current_power",
|
||||||
|
device_class=SensorDeviceClass.POWER,
|
||||||
|
native_unit_of_measurement=UnitOfPower.WATT,
|
||||||
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
|
),
|
||||||
|
SensorEntityDescription(
|
||||||
|
key=CONTROL_SIGNAL,
|
||||||
|
translation_key="control_signal",
|
||||||
|
native_unit_of_measurement=PERCENTAGE,
|
||||||
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
|
SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
|
||||||
|
@ -94,6 +109,16 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
SOCKET_SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
|
||||||
|
SensorEntityDescription(
|
||||||
|
key=HUMIDITY,
|
||||||
|
device_class=SensorDeviceClass.HUMIDITY,
|
||||||
|
native_unit_of_measurement=PERCENTAGE,
|
||||||
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
|
),
|
||||||
|
*HEATER_SENSOR_TYPES,
|
||||||
|
)
|
||||||
|
|
||||||
LOCAL_SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
|
LOCAL_SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
|
||||||
SensorEntityDescription(
|
SensorEntityDescription(
|
||||||
key="control_signal",
|
key="control_signal",
|
||||||
|
@ -145,7 +170,9 @@ async def async_setup_entry(
|
||||||
)
|
)
|
||||||
for mill_device in mill_data_coordinator.data.values()
|
for mill_device in mill_data_coordinator.data.values()
|
||||||
for entity_description in (
|
for entity_description in (
|
||||||
HEATER_SENSOR_TYPES
|
SOCKET_SENSOR_TYPES
|
||||||
|
if isinstance(mill_device, mill.Socket)
|
||||||
|
else HEATER_SENSOR_TYPES
|
||||||
if isinstance(mill_device, mill.Heater)
|
if isinstance(mill_device, mill.Heater)
|
||||||
else SENSOR_TYPES
|
else SENSOR_TYPES
|
||||||
)
|
)
|
||||||
|
|
|
@ -23,3 +23,20 @@ set_room_temperature:
|
||||||
min: 0
|
min: 0
|
||||||
max: 100
|
max: 100
|
||||||
unit_of_measurement: "°"
|
unit_of_measurement: "°"
|
||||||
|
|
||||||
|
max_heating_power:
|
||||||
|
fields:
|
||||||
|
entity_id:
|
||||||
|
required: true
|
||||||
|
example: "climate.house"
|
||||||
|
selector:
|
||||||
|
entity:
|
||||||
|
integration: mill
|
||||||
|
domain: climate
|
||||||
|
max_heating_power:
|
||||||
|
required: true
|
||||||
|
selector:
|
||||||
|
number:
|
||||||
|
min: 0
|
||||||
|
max: 2000
|
||||||
|
unit_of_measurement: "W"
|
||||||
|
|
|
@ -53,6 +53,20 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"services": {
|
"services": {
|
||||||
|
"max_heating_power": {
|
||||||
|
"description": "Sets max power heater can use.",
|
||||||
|
"fields": {
|
||||||
|
"entity_id": {
|
||||||
|
"description": "Entity id of heater to change.",
|
||||||
|
"name": "Entity id"
|
||||||
|
},
|
||||||
|
"max_heating_power": {
|
||||||
|
"description": "Max heating power.",
|
||||||
|
"name": "Max heating power"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"name": "Set max heating power"
|
||||||
|
},
|
||||||
"set_room_temperature": {
|
"set_room_temperature": {
|
||||||
"name": "Set room temperature",
|
"name": "Set room temperature",
|
||||||
"description": "Sets Mill room temperatures.",
|
"description": "Sets Mill room temperatures.",
|
||||||
|
|
|
@ -4,6 +4,7 @@ from unittest.mock import patch
|
||||||
|
|
||||||
from homeassistant import config_entries
|
from homeassistant import config_entries
|
||||||
from homeassistant.components.mill.const import CLOUD, CONNECTION_TYPE, DOMAIN, LOCAL
|
from homeassistant.components.mill.const import CLOUD, CONNECTION_TYPE, DOMAIN, LOCAL
|
||||||
|
from homeassistant.components.recorder import Recorder
|
||||||
from homeassistant.const import CONF_IP_ADDRESS, CONF_PASSWORD, CONF_USERNAME
|
from homeassistant.const import CONF_IP_ADDRESS, CONF_PASSWORD, CONF_USERNAME
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.data_entry_flow import FlowResultType
|
from homeassistant.data_entry_flow import FlowResultType
|
||||||
|
@ -11,7 +12,7 @@ from homeassistant.data_entry_flow import FlowResultType
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
|
|
||||||
async def test_show_config_form(hass: HomeAssistant) -> None:
|
async def test_show_config_form(recorder_mock: Recorder, hass: HomeAssistant) -> None:
|
||||||
"""Test show configuration form."""
|
"""Test show configuration form."""
|
||||||
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}
|
||||||
|
@ -21,7 +22,7 @@ async def test_show_config_form(hass: HomeAssistant) -> None:
|
||||||
assert result["step_id"] == "user"
|
assert result["step_id"] == "user"
|
||||||
|
|
||||||
|
|
||||||
async def test_create_entry(hass: HomeAssistant) -> None:
|
async def test_create_entry(recorder_mock: Recorder, hass: HomeAssistant) -> None:
|
||||||
"""Test create entry from user input."""
|
"""Test create entry from user input."""
|
||||||
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}
|
||||||
|
@ -56,7 +57,9 @@ async def test_create_entry(hass: HomeAssistant) -> None:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async def test_flow_entry_already_exists(hass: HomeAssistant) -> None:
|
async def test_flow_entry_already_exists(
|
||||||
|
recorder_mock: Recorder, hass: HomeAssistant
|
||||||
|
) -> None:
|
||||||
"""Test user input for config_entry that already exists."""
|
"""Test user input for config_entry that already exists."""
|
||||||
|
|
||||||
test_data = {
|
test_data = {
|
||||||
|
@ -96,7 +99,7 @@ async def test_flow_entry_already_exists(hass: HomeAssistant) -> None:
|
||||||
assert result["reason"] == "already_configured"
|
assert result["reason"] == "already_configured"
|
||||||
|
|
||||||
|
|
||||||
async def test_connection_error(hass: HomeAssistant) -> None:
|
async def test_connection_error(recorder_mock: Recorder, hass: HomeAssistant) -> None:
|
||||||
"""Test connection error."""
|
"""Test connection error."""
|
||||||
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}
|
||||||
|
@ -125,7 +128,7 @@ async def test_connection_error(hass: HomeAssistant) -> None:
|
||||||
assert result["errors"] == {"base": "cannot_connect"}
|
assert result["errors"] == {"base": "cannot_connect"}
|
||||||
|
|
||||||
|
|
||||||
async def test_local_create_entry(hass: HomeAssistant) -> None:
|
async def test_local_create_entry(recorder_mock: Recorder, hass: HomeAssistant) -> None:
|
||||||
"""Test create entry from user input."""
|
"""Test create entry from user input."""
|
||||||
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}
|
||||||
|
@ -165,7 +168,9 @@ async def test_local_create_entry(hass: HomeAssistant) -> None:
|
||||||
assert result["data"] == test_data
|
assert result["data"] == test_data
|
||||||
|
|
||||||
|
|
||||||
async def test_local_flow_entry_already_exists(hass: HomeAssistant) -> None:
|
async def test_local_flow_entry_already_exists(
|
||||||
|
recorder_mock: Recorder, hass: HomeAssistant
|
||||||
|
) -> None:
|
||||||
"""Test user input for config_entry that already exists."""
|
"""Test user input for config_entry that already exists."""
|
||||||
|
|
||||||
test_data = {
|
test_data = {
|
||||||
|
@ -215,7 +220,9 @@ async def test_local_flow_entry_already_exists(hass: HomeAssistant) -> None:
|
||||||
assert result["reason"] == "already_configured"
|
assert result["reason"] == "already_configured"
|
||||||
|
|
||||||
|
|
||||||
async def test_local_connection_error(hass: HomeAssistant) -> None:
|
async def test_local_connection_error(
|
||||||
|
recorder_mock: Recorder, hass: HomeAssistant
|
||||||
|
) -> None:
|
||||||
"""Test connection error."""
|
"""Test connection error."""
|
||||||
|
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
|
|
@ -4,6 +4,7 @@ import asyncio
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
from homeassistant.components import mill
|
from homeassistant.components import mill
|
||||||
|
from homeassistant.components.recorder import Recorder
|
||||||
from homeassistant.config_entries import ConfigEntryState
|
from homeassistant.config_entries import ConfigEntryState
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
@ -11,7 +12,9 @@ from homeassistant.setup import async_setup_component
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
|
|
||||||
async def test_setup_with_cloud_config(hass: HomeAssistant) -> None:
|
async def test_setup_with_cloud_config(
|
||||||
|
recorder_mock: Recorder, hass: HomeAssistant
|
||||||
|
) -> None:
|
||||||
"""Test setup of cloud config."""
|
"""Test setup of cloud config."""
|
||||||
entry = MockConfigEntry(
|
entry = MockConfigEntry(
|
||||||
domain=mill.DOMAIN,
|
domain=mill.DOMAIN,
|
||||||
|
@ -31,7 +34,9 @@ async def test_setup_with_cloud_config(hass: HomeAssistant) -> None:
|
||||||
assert len(mock_connect.mock_calls) == 1
|
assert len(mock_connect.mock_calls) == 1
|
||||||
|
|
||||||
|
|
||||||
async def test_setup_with_cloud_config_fails(hass: HomeAssistant) -> None:
|
async def test_setup_with_cloud_config_fails(
|
||||||
|
recorder_mock: Recorder, hass: HomeAssistant
|
||||||
|
) -> None:
|
||||||
"""Test setup of cloud config."""
|
"""Test setup of cloud config."""
|
||||||
entry = MockConfigEntry(
|
entry = MockConfigEntry(
|
||||||
domain=mill.DOMAIN,
|
domain=mill.DOMAIN,
|
||||||
|
@ -47,7 +52,9 @@ async def test_setup_with_cloud_config_fails(hass: HomeAssistant) -> None:
|
||||||
assert entry.state is ConfigEntryState.SETUP_RETRY
|
assert entry.state is ConfigEntryState.SETUP_RETRY
|
||||||
|
|
||||||
|
|
||||||
async def test_setup_with_cloud_config_times_out(hass: HomeAssistant) -> None:
|
async def test_setup_with_cloud_config_times_out(
|
||||||
|
recorder_mock: Recorder, hass: HomeAssistant
|
||||||
|
) -> None:
|
||||||
"""Test setup of cloud config will retry if timed out."""
|
"""Test setup of cloud config will retry if timed out."""
|
||||||
entry = MockConfigEntry(
|
entry = MockConfigEntry(
|
||||||
domain=mill.DOMAIN,
|
domain=mill.DOMAIN,
|
||||||
|
@ -63,7 +70,9 @@ async def test_setup_with_cloud_config_times_out(hass: HomeAssistant) -> None:
|
||||||
assert entry.state is ConfigEntryState.SETUP_RETRY
|
assert entry.state is ConfigEntryState.SETUP_RETRY
|
||||||
|
|
||||||
|
|
||||||
async def test_setup_with_old_cloud_config(hass: HomeAssistant) -> None:
|
async def test_setup_with_old_cloud_config(
|
||||||
|
recorder_mock: Recorder, hass: HomeAssistant
|
||||||
|
) -> None:
|
||||||
"""Test setup of old cloud config."""
|
"""Test setup of old cloud config."""
|
||||||
entry = MockConfigEntry(
|
entry = MockConfigEntry(
|
||||||
domain=mill.DOMAIN,
|
domain=mill.DOMAIN,
|
||||||
|
@ -82,7 +91,9 @@ async def test_setup_with_old_cloud_config(hass: HomeAssistant) -> None:
|
||||||
assert len(mock_connect.mock_calls) == 1
|
assert len(mock_connect.mock_calls) == 1
|
||||||
|
|
||||||
|
|
||||||
async def test_setup_with_local_config(hass: HomeAssistant) -> None:
|
async def test_setup_with_local_config(
|
||||||
|
recorder_mock: Recorder, hass: HomeAssistant
|
||||||
|
) -> None:
|
||||||
"""Test setup of local config."""
|
"""Test setup of local config."""
|
||||||
entry = MockConfigEntry(
|
entry = MockConfigEntry(
|
||||||
domain=mill.DOMAIN,
|
domain=mill.DOMAIN,
|
||||||
|
@ -119,7 +130,7 @@ async def test_setup_with_local_config(hass: HomeAssistant) -> None:
|
||||||
assert len(mock_connect.mock_calls) == 1
|
assert len(mock_connect.mock_calls) == 1
|
||||||
|
|
||||||
|
|
||||||
async def test_unload_entry(hass: HomeAssistant) -> None:
|
async def test_unload_entry(recorder_mock: Recorder, hass: HomeAssistant) -> None:
|
||||||
"""Test removing mill client."""
|
"""Test removing mill client."""
|
||||||
entry = MockConfigEntry(
|
entry = MockConfigEntry(
|
||||||
domain=mill.DOMAIN,
|
domain=mill.DOMAIN,
|
||||||
|
|
Loading…
Add table
Reference in a new issue