Add Maxcube unit tests (#47872)

* Simplify maxcube integration

Device objects returned by maxcube-api dependency are stable, so
we do not need to resolve from the device address every time.

Also, refactor and unify how maxcube integration sets temperature & mode.

* Add tests for maxcube component

* Use homeassistant.util.utcnow to retrieve current time

* Revert "Simplify maxcube integration"

This reverts commit 84d231d5bd.

* Make test pass again after rolling back integration changes
pull/48403/head
Unai 2021-03-27 12:42:23 +01:00 committed by GitHub
parent 0d595a2845
commit 4a353efdfb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 514 additions and 1 deletions

View File

@ -563,7 +563,6 @@ omit =
homeassistant/components/map/*
homeassistant/components/mastodon/notify.py
homeassistant/components/matrix/*
homeassistant/components/maxcube/*
homeassistant/components/mcp23017/*
homeassistant/components/media_extractor/*
homeassistant/components/mediaroom/media_player.py

View File

@ -472,6 +472,9 @@ logi_circle==0.2.2
# homeassistant.components.luftdaten
luftdaten==0.6.4
# homeassistant.components.maxcube
maxcube-api==0.4.1
# homeassistant.components.mythicbeastsdns
mbddns==0.1.2

View File

@ -0,0 +1,110 @@
"""Tests for EQ3 Max! component."""
from unittest.mock import create_autospec, patch
from maxcube.device import MAX_DEVICE_MODE_AUTOMATIC, MAX_DEVICE_MODE_MANUAL
from maxcube.room import MaxRoom
from maxcube.thermostat import MaxThermostat
from maxcube.wallthermostat import MaxWallThermostat
from maxcube.windowshutter import MaxWindowShutter
import pytest
from homeassistant.components.maxcube import DOMAIN
from homeassistant.setup import async_setup_component
@pytest.fixture
def room():
"""Create a test MAX! room."""
r = MaxRoom()
r.id = 1
r.name = "TestRoom"
return r
@pytest.fixture
def thermostat():
"""Create test MAX! thermostat."""
t = create_autospec(MaxThermostat)
t.name = "TestThermostat"
t.serial = "AABBCCDD01"
t.rf_address = "abc1"
t.room_id = 1
t.is_thermostat.return_value = True
t.is_wallthermostat.return_value = False
t.is_windowshutter.return_value = False
t.mode = MAX_DEVICE_MODE_AUTOMATIC
t.comfort_temperature = 19.0
t.eco_temperature = 14.0
t.target_temperature = 20.5
t.actual_temperature = 19.0
t.max_temperature = None
t.min_temperature = None
t.valve_position = 25 # 25%
return t
@pytest.fixture
def wallthermostat():
"""Create test MAX! wall thermostat."""
t = create_autospec(MaxWallThermostat)
t.name = "TestWallThermostat"
t.serial = "AABBCCDD02"
t.rf_address = "abc2"
t.room_id = 1
t.is_thermostat.return_value = False
t.is_wallthermostat.return_value = True
t.is_windowshutter.return_value = False
t.mode = MAX_DEVICE_MODE_MANUAL
t.comfort_temperature = 19.0
t.eco_temperature = 14.0
t.target_temperature = 4.5
t.actual_temperature = 19.0
t.max_temperature = 29.0
t.min_temperature = 4.5
return t
@pytest.fixture
def windowshutter():
"""Create test MAX! window shutter."""
shutter = create_autospec(MaxWindowShutter)
shutter.name = "TestShutter"
shutter.serial = "AABBCCDD03"
shutter.rf_address = "abc3"
shutter.room_id = 1
shutter.is_open = True
shutter.is_thermostat.return_value = False
shutter.is_wallthermostat.return_value = False
shutter.is_windowshutter.return_value = True
return shutter
@pytest.fixture
def hass_config():
"""Return test HASS configuration."""
return {
DOMAIN: {
"gateways": [
{
"host": "1.2.3.4",
}
]
}
}
@pytest.fixture
async def cube(hass, hass_config, room, thermostat, wallthermostat, windowshutter):
"""Build and setup a cube mock with a single room and some devices."""
with patch("homeassistant.components.maxcube.MaxCube") as mock:
cube = mock.return_value
cube.rooms = [room]
cube.devices = [thermostat, wallthermostat, windowshutter]
cube.room_by_id.return_value = room
cube.devices_by_room.return_value = [thermostat, wallthermostat, windowshutter]
cube.device_by_rf.side_effect = {d.rf_address: d for d in cube.devices}.get
assert await async_setup_component(hass, DOMAIN, hass_config)
await hass.async_block_till_done()
gateway = hass_config[DOMAIN]["gateways"][0]
mock.assert_called_with(gateway["host"], gateway.get("port", 62910))
return cube

View File

@ -0,0 +1,37 @@
"""Test EQ3 Max! Window Shutters."""
from datetime import timedelta
from maxcube.cube import MaxCube
from maxcube.windowshutter import MaxWindowShutter
from homeassistant.components.binary_sensor import DEVICE_CLASS_WINDOW
from homeassistant.const import (
ATTR_DEVICE_CLASS,
ATTR_FRIENDLY_NAME,
STATE_OFF,
STATE_ON,
)
from homeassistant.util import utcnow
from tests.common import async_fire_time_changed
ENTITY_ID = "binary_sensor.testroom_testshutter"
async def test_window_shuttler(hass, cube: MaxCube, windowshutter: MaxWindowShutter):
"""Test a successful setup with a shuttler device."""
async_fire_time_changed(hass, utcnow() + timedelta(minutes=5))
await hass.async_block_till_done()
state = hass.states.get(ENTITY_ID)
assert state is not None
assert state.state == STATE_ON
assert state.attributes.get(ATTR_FRIENDLY_NAME) == "TestRoom TestShutter"
assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_WINDOW
windowshutter.is_open = False
async_fire_time_changed(hass, utcnow() + timedelta(minutes=5))
await hass.async_block_till_done()
state = hass.states.get(ENTITY_ID)
assert state.state == STATE_OFF

View File

@ -0,0 +1,364 @@
"""Test EQ3 Max! Thermostats."""
from datetime import timedelta
from maxcube.cube import MaxCube
from maxcube.device import (
MAX_DEVICE_MODE_AUTOMATIC,
MAX_DEVICE_MODE_BOOST,
MAX_DEVICE_MODE_MANUAL,
MAX_DEVICE_MODE_VACATION,
)
from maxcube.thermostat import MaxThermostat
from maxcube.wallthermostat import MaxWallThermostat
from homeassistant.components.climate.const import (
ATTR_CURRENT_TEMPERATURE,
ATTR_HVAC_ACTION,
ATTR_HVAC_MODE,
ATTR_HVAC_MODES,
ATTR_MAX_TEMP,
ATTR_MIN_TEMP,
ATTR_PRESET_MODE,
ATTR_PRESET_MODES,
CURRENT_HVAC_HEAT,
CURRENT_HVAC_IDLE,
CURRENT_HVAC_OFF,
DOMAIN as CLIMATE_DOMAIN,
HVAC_MODE_AUTO,
HVAC_MODE_HEAT,
HVAC_MODE_OFF,
PRESET_AWAY,
PRESET_BOOST,
PRESET_COMFORT,
PRESET_ECO,
PRESET_NONE,
SERVICE_SET_HVAC_MODE,
SERVICE_SET_PRESET_MODE,
SERVICE_SET_TEMPERATURE,
)
from homeassistant.components.maxcube.climate import (
MAX_TEMPERATURE,
MIN_TEMPERATURE,
OFF_TEMPERATURE,
ON_TEMPERATURE,
PRESET_ON,
SUPPORT_FLAGS,
)
from homeassistant.const import (
ATTR_ENTITY_ID,
ATTR_FRIENDLY_NAME,
ATTR_SUPPORTED_FEATURES,
ATTR_TEMPERATURE,
)
from homeassistant.util import utcnow
from tests.common import async_fire_time_changed
ENTITY_ID = "climate.testroom_testthermostat"
WALL_ENTITY_ID = "climate.testroom_testwallthermostat"
VALVE_POSITION = "valve_position"
async def test_setup_thermostat(hass, cube: MaxCube):
"""Test a successful setup of a thermostat device."""
state = hass.states.get(ENTITY_ID)
assert state.state == HVAC_MODE_AUTO
assert state.attributes.get(ATTR_FRIENDLY_NAME) == "TestRoom TestThermostat"
assert state.attributes.get(ATTR_HVAC_ACTION) == CURRENT_HVAC_HEAT
assert state.attributes.get(ATTR_HVAC_MODES) == [
HVAC_MODE_OFF,
HVAC_MODE_AUTO,
HVAC_MODE_HEAT,
]
assert state.attributes.get(ATTR_PRESET_MODES) == [
PRESET_NONE,
PRESET_BOOST,
PRESET_COMFORT,
PRESET_ECO,
PRESET_AWAY,
PRESET_ON,
]
assert state.attributes.get(ATTR_PRESET_MODE) == PRESET_NONE
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == SUPPORT_FLAGS
assert state.attributes.get(ATTR_MAX_TEMP) == MAX_TEMPERATURE
assert state.attributes.get(ATTR_MIN_TEMP) == 5.0
assert state.attributes.get(ATTR_CURRENT_TEMPERATURE) == 19.0
assert state.attributes.get(ATTR_TEMPERATURE) == 20.5
assert state.attributes.get(VALVE_POSITION) == 25
async def test_setup_wallthermostat(hass, cube: MaxCube):
"""Test a successful setup of a wall thermostat device."""
state = hass.states.get(WALL_ENTITY_ID)
assert state.state == HVAC_MODE_OFF
assert state.attributes.get(ATTR_FRIENDLY_NAME) == "TestRoom TestWallThermostat"
assert state.attributes.get(ATTR_HVAC_ACTION) == CURRENT_HVAC_HEAT
assert state.attributes.get(ATTR_PRESET_MODE) == PRESET_NONE
assert state.attributes.get(ATTR_MAX_TEMP) == 29.0
assert state.attributes.get(ATTR_MIN_TEMP) == 5.0
assert state.attributes.get(ATTR_CURRENT_TEMPERATURE) == 19.0
assert state.attributes.get(ATTR_TEMPERATURE) is None
async def test_thermostat_set_hvac_mode_off(
hass, cube: MaxCube, thermostat: MaxThermostat
):
"""Turn off thermostat."""
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_HVAC_MODE,
{ATTR_ENTITY_ID: ENTITY_ID, ATTR_HVAC_MODE: HVAC_MODE_OFF},
blocking=True,
)
cube.set_temperature_mode.assert_called_once_with(
thermostat, OFF_TEMPERATURE, MAX_DEVICE_MODE_MANUAL
)
thermostat.mode = MAX_DEVICE_MODE_MANUAL
thermostat.target_temperature = OFF_TEMPERATURE
thermostat.valve_position = 0
async_fire_time_changed(hass, utcnow() + timedelta(minutes=5))
await hass.async_block_till_done()
state = hass.states.get(ENTITY_ID)
assert state.state == HVAC_MODE_OFF
assert state.attributes.get(ATTR_TEMPERATURE) is None
assert state.attributes.get(ATTR_HVAC_ACTION) == CURRENT_HVAC_OFF
assert state.attributes.get(VALVE_POSITION) == 0
wall_state = hass.states.get(WALL_ENTITY_ID)
assert wall_state.attributes.get(ATTR_HVAC_ACTION) == CURRENT_HVAC_OFF
async def test_thermostat_set_hvac_mode_heat(
hass, cube: MaxCube, thermostat: MaxThermostat
):
"""Set hvac mode to heat."""
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_HVAC_MODE,
{ATTR_ENTITY_ID: ENTITY_ID, ATTR_HVAC_MODE: HVAC_MODE_HEAT},
blocking=True,
)
cube.set_temperature_mode.assert_called_once_with(
thermostat, 20.5, MAX_DEVICE_MODE_MANUAL
)
thermostat.mode = MAX_DEVICE_MODE_MANUAL
async_fire_time_changed(hass, utcnow() + timedelta(minutes=5))
await hass.async_block_till_done()
await hass.async_block_till_done()
state = hass.states.get(ENTITY_ID)
assert state.state == HVAC_MODE_HEAT
async def test_thermostat_set_temperature(
hass, cube: MaxCube, thermostat: MaxThermostat
):
"""Set hvac mode to heat."""
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_TEMPERATURE,
{ATTR_ENTITY_ID: ENTITY_ID, ATTR_TEMPERATURE: 10.0},
blocking=True,
)
cube.set_target_temperature.assert_called_once_with(thermostat, 10.0)
thermostat.target_temperature = 10.0
thermostat.valve_position = 0
async_fire_time_changed(hass, utcnow() + timedelta(minutes=5))
await hass.async_block_till_done()
state = hass.states.get(ENTITY_ID)
assert state.state == HVAC_MODE_AUTO
assert state.attributes.get(ATTR_TEMPERATURE) == 10.0
assert state.attributes.get(ATTR_HVAC_ACTION) == CURRENT_HVAC_IDLE
async def test_thermostat_set_preset_on(hass, cube: MaxCube, thermostat: MaxThermostat):
"""Set preset mode to on."""
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_PRESET_MODE,
{ATTR_ENTITY_ID: ENTITY_ID, ATTR_PRESET_MODE: PRESET_ON},
blocking=True,
)
cube.set_temperature_mode.assert_called_once_with(
thermostat, ON_TEMPERATURE, MAX_DEVICE_MODE_MANUAL
)
thermostat.mode = MAX_DEVICE_MODE_MANUAL
thermostat.target_temperature = ON_TEMPERATURE
async_fire_time_changed(hass, utcnow() + timedelta(minutes=5))
await hass.async_block_till_done()
state = hass.states.get(ENTITY_ID)
assert state.state == HVAC_MODE_HEAT
assert state.attributes.get(ATTR_TEMPERATURE) is None
assert state.attributes.get(ATTR_PRESET_MODE) == PRESET_ON
async def test_thermostat_set_preset_comfort(
hass, cube: MaxCube, thermostat: MaxThermostat
):
"""Set preset mode to comfort."""
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_PRESET_MODE,
{ATTR_ENTITY_ID: ENTITY_ID, ATTR_PRESET_MODE: PRESET_COMFORT},
blocking=True,
)
cube.set_temperature_mode.assert_called_once_with(
thermostat, thermostat.comfort_temperature, MAX_DEVICE_MODE_MANUAL
)
thermostat.mode = MAX_DEVICE_MODE_MANUAL
thermostat.target_temperature = thermostat.comfort_temperature
async_fire_time_changed(hass, utcnow() + timedelta(minutes=5))
await hass.async_block_till_done()
state = hass.states.get(ENTITY_ID)
assert state.state == HVAC_MODE_HEAT
assert state.attributes.get(ATTR_TEMPERATURE) == thermostat.comfort_temperature
assert state.attributes.get(ATTR_PRESET_MODE) == PRESET_COMFORT
async def test_thermostat_set_preset_eco(
hass, cube: MaxCube, thermostat: MaxThermostat
):
"""Set preset mode to eco."""
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_PRESET_MODE,
{ATTR_ENTITY_ID: ENTITY_ID, ATTR_PRESET_MODE: PRESET_ECO},
blocking=True,
)
cube.set_temperature_mode.assert_called_once_with(
thermostat, thermostat.eco_temperature, MAX_DEVICE_MODE_MANUAL
)
thermostat.mode = MAX_DEVICE_MODE_MANUAL
thermostat.target_temperature = thermostat.eco_temperature
async_fire_time_changed(hass, utcnow() + timedelta(minutes=5))
await hass.async_block_till_done()
state = hass.states.get(ENTITY_ID)
assert state.state == HVAC_MODE_HEAT
assert state.attributes.get(ATTR_TEMPERATURE) == thermostat.eco_temperature
assert state.attributes.get(ATTR_PRESET_MODE) == PRESET_ECO
async def test_thermostat_set_preset_away(
hass, cube: MaxCube, thermostat: MaxThermostat
):
"""Set preset mode to away."""
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_PRESET_MODE,
{ATTR_ENTITY_ID: ENTITY_ID, ATTR_PRESET_MODE: PRESET_AWAY},
blocking=True,
)
cube.set_temperature_mode.assert_called_once_with(
thermostat, None, MAX_DEVICE_MODE_VACATION
)
thermostat.mode = MAX_DEVICE_MODE_VACATION
thermostat.target_temperature = thermostat.eco_temperature
async_fire_time_changed(hass, utcnow() + timedelta(minutes=5))
await hass.async_block_till_done()
state = hass.states.get(ENTITY_ID)
assert state.state == HVAC_MODE_HEAT
assert state.attributes.get(ATTR_TEMPERATURE) == thermostat.eco_temperature
assert state.attributes.get(ATTR_PRESET_MODE) == PRESET_AWAY
async def test_thermostat_set_preset_boost(
hass, cube: MaxCube, thermostat: MaxThermostat
):
"""Set preset mode to boost."""
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_PRESET_MODE,
{ATTR_ENTITY_ID: ENTITY_ID, ATTR_PRESET_MODE: PRESET_BOOST},
blocking=True,
)
cube.set_temperature_mode.assert_called_once_with(
thermostat, None, MAX_DEVICE_MODE_BOOST
)
thermostat.mode = MAX_DEVICE_MODE_BOOST
thermostat.target_temperature = thermostat.eco_temperature
async_fire_time_changed(hass, utcnow() + timedelta(minutes=5))
await hass.async_block_till_done()
state = hass.states.get(ENTITY_ID)
assert state.state == HVAC_MODE_AUTO
assert state.attributes.get(ATTR_TEMPERATURE) == thermostat.eco_temperature
assert state.attributes.get(ATTR_PRESET_MODE) == PRESET_BOOST
async def test_thermostat_set_preset_none(
hass, cube: MaxCube, thermostat: MaxThermostat
):
"""Set preset mode to boost."""
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_PRESET_MODE,
{ATTR_ENTITY_ID: ENTITY_ID, ATTR_PRESET_MODE: PRESET_NONE},
blocking=True,
)
cube.set_temperature_mode.assert_called_once_with(
thermostat, None, MAX_DEVICE_MODE_AUTOMATIC
)
async def test_wallthermostat_set_hvac_mode_heat(
hass, cube: MaxCube, wallthermostat: MaxWallThermostat
):
"""Set wall thermostat hvac mode to heat."""
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_HVAC_MODE,
{ATTR_ENTITY_ID: WALL_ENTITY_ID, ATTR_HVAC_MODE: HVAC_MODE_HEAT},
blocking=True,
)
cube.set_temperature_mode.assert_called_once_with(
wallthermostat, MIN_TEMPERATURE, MAX_DEVICE_MODE_MANUAL
)
wallthermostat.target_temperature = MIN_TEMPERATURE
async_fire_time_changed(hass, utcnow() + timedelta(minutes=5))
await hass.async_block_till_done()
state = hass.states.get(WALL_ENTITY_ID)
assert state.state == HVAC_MODE_HEAT
assert state.attributes.get(ATTR_TEMPERATURE) == MIN_TEMPERATURE
async def test_wallthermostat_set_hvac_mode_auto(
hass, cube: MaxCube, wallthermostat: MaxWallThermostat
):
"""Set wall thermostat hvac mode to auto."""
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_HVAC_MODE,
{ATTR_ENTITY_ID: WALL_ENTITY_ID, ATTR_HVAC_MODE: HVAC_MODE_AUTO},
blocking=True,
)
cube.set_temperature_mode.assert_called_once_with(
wallthermostat, None, MAX_DEVICE_MODE_AUTOMATIC
)
wallthermostat.mode = MAX_DEVICE_MODE_AUTOMATIC
wallthermostat.target_temperature = 23.0
async_fire_time_changed(hass, utcnow() + timedelta(minutes=5))
await hass.async_block_till_done()
state = hass.states.get(WALL_ENTITY_ID)
assert state.state == HVAC_MODE_AUTO
assert state.attributes.get(ATTR_TEMPERATURE) == 23.0