Add mode control for Modbus climate entities (#73906)
* Add support for Modbus HVAC control registers
This commit is contained in:
parent
eb93372cd6
commit
ae7eb9cef9
4 changed files with 351 additions and 22 deletions
|
@ -9,6 +9,7 @@ import voluptuous as vol
|
||||||
from homeassistant.components.binary_sensor import (
|
from homeassistant.components.binary_sensor import (
|
||||||
DEVICE_CLASSES_SCHEMA as BINARY_SENSOR_DEVICE_CLASSES_SCHEMA,
|
DEVICE_CLASSES_SCHEMA as BINARY_SENSOR_DEVICE_CLASSES_SCHEMA,
|
||||||
)
|
)
|
||||||
|
from homeassistant.components.climate import HVACMode
|
||||||
from homeassistant.components.cover import (
|
from homeassistant.components.cover import (
|
||||||
DEVICE_CLASSES_SCHEMA as COVER_DEVICE_CLASSES_SCHEMA,
|
DEVICE_CLASSES_SCHEMA as COVER_DEVICE_CLASSES_SCHEMA,
|
||||||
)
|
)
|
||||||
|
@ -65,6 +66,9 @@ from .const import ( # noqa: F401
|
||||||
CONF_DATA_TYPE,
|
CONF_DATA_TYPE,
|
||||||
CONF_FANS,
|
CONF_FANS,
|
||||||
CONF_HUB,
|
CONF_HUB,
|
||||||
|
CONF_HVAC_MODE_REGISTER,
|
||||||
|
CONF_HVAC_MODE_VALUES,
|
||||||
|
CONF_HVAC_ONOFF_REGISTER,
|
||||||
CONF_INPUT_TYPE,
|
CONF_INPUT_TYPE,
|
||||||
CONF_LAZY_ERROR,
|
CONF_LAZY_ERROR,
|
||||||
CONF_MAX_TEMP,
|
CONF_MAX_TEMP,
|
||||||
|
@ -218,6 +222,21 @@ CLIMATE_SCHEMA = vol.All(
|
||||||
vol.Optional(CONF_MIN_TEMP, default=5): cv.positive_int,
|
vol.Optional(CONF_MIN_TEMP, default=5): cv.positive_int,
|
||||||
vol.Optional(CONF_STEP, default=0.5): vol.Coerce(float),
|
vol.Optional(CONF_STEP, default=0.5): vol.Coerce(float),
|
||||||
vol.Optional(CONF_TEMPERATURE_UNIT, default=DEFAULT_TEMP_UNIT): cv.string,
|
vol.Optional(CONF_TEMPERATURE_UNIT, default=DEFAULT_TEMP_UNIT): cv.string,
|
||||||
|
vol.Optional(CONF_HVAC_ONOFF_REGISTER): cv.positive_int,
|
||||||
|
vol.Optional(CONF_HVAC_MODE_REGISTER): vol.Maybe(
|
||||||
|
{
|
||||||
|
CONF_ADDRESS: cv.positive_int,
|
||||||
|
CONF_HVAC_MODE_VALUES: {
|
||||||
|
vol.Optional(HVACMode.OFF.value): cv.positive_int,
|
||||||
|
vol.Optional(HVACMode.HEAT.value): cv.positive_int,
|
||||||
|
vol.Optional(HVACMode.COOL.value): cv.positive_int,
|
||||||
|
vol.Optional(HVACMode.HEAT_COOL.value): cv.positive_int,
|
||||||
|
vol.Optional(HVACMode.AUTO.value): cv.positive_int,
|
||||||
|
vol.Optional(HVACMode.DRY.value): cv.positive_int,
|
||||||
|
vol.Optional(HVACMode.FAN_ONLY.value): cv.positive_int,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
),
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
@ -3,7 +3,7 @@ from __future__ import annotations
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import struct
|
import struct
|
||||||
from typing import Any
|
from typing import Any, cast
|
||||||
|
|
||||||
from homeassistant.components.climate import (
|
from homeassistant.components.climate import (
|
||||||
ClimateEntity,
|
ClimateEntity,
|
||||||
|
@ -12,6 +12,7 @@ from homeassistant.components.climate import (
|
||||||
)
|
)
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_TEMPERATURE,
|
ATTR_TEMPERATURE,
|
||||||
|
CONF_ADDRESS,
|
||||||
CONF_NAME,
|
CONF_NAME,
|
||||||
CONF_TEMPERATURE_UNIT,
|
CONF_TEMPERATURE_UNIT,
|
||||||
PRECISION_TENTHS,
|
PRECISION_TENTHS,
|
||||||
|
@ -31,6 +32,9 @@ from .const import (
|
||||||
CALL_TYPE_WRITE_REGISTER,
|
CALL_TYPE_WRITE_REGISTER,
|
||||||
CALL_TYPE_WRITE_REGISTERS,
|
CALL_TYPE_WRITE_REGISTERS,
|
||||||
CONF_CLIMATES,
|
CONF_CLIMATES,
|
||||||
|
CONF_HVAC_MODE_REGISTER,
|
||||||
|
CONF_HVAC_MODE_VALUES,
|
||||||
|
CONF_HVAC_ONOFF_REGISTER,
|
||||||
CONF_MAX_TEMP,
|
CONF_MAX_TEMP,
|
||||||
CONF_MIN_TEMP,
|
CONF_MIN_TEMP,
|
||||||
CONF_STEP,
|
CONF_STEP,
|
||||||
|
@ -63,8 +67,6 @@ async def async_setup_platform(
|
||||||
class ModbusThermostat(BaseStructPlatform, RestoreEntity, ClimateEntity):
|
class ModbusThermostat(BaseStructPlatform, RestoreEntity, ClimateEntity):
|
||||||
"""Representation of a Modbus Thermostat."""
|
"""Representation of a Modbus Thermostat."""
|
||||||
|
|
||||||
_attr_hvac_mode = HVACMode.AUTO
|
|
||||||
_attr_hvac_modes = [HVACMode.AUTO]
|
|
||||||
_attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE
|
_attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
|
@ -90,6 +92,33 @@ class ModbusThermostat(BaseStructPlatform, RestoreEntity, ClimateEntity):
|
||||||
self._attr_target_temperature_step = config[CONF_TARGET_TEMP]
|
self._attr_target_temperature_step = config[CONF_TARGET_TEMP]
|
||||||
self._attr_target_temperature_step = config[CONF_STEP]
|
self._attr_target_temperature_step = config[CONF_STEP]
|
||||||
|
|
||||||
|
if CONF_HVAC_MODE_REGISTER in config:
|
||||||
|
mode_config = config[CONF_HVAC_MODE_REGISTER]
|
||||||
|
self._hvac_mode_register = mode_config[CONF_ADDRESS]
|
||||||
|
self._attr_hvac_modes = cast(list[HVACMode], [])
|
||||||
|
self._attr_hvac_mode = None
|
||||||
|
self._hvac_mode_mapping: list[tuple[int, HVACMode]] = []
|
||||||
|
mode_value_config = mode_config[CONF_HVAC_MODE_VALUES]
|
||||||
|
for hvac_mode in HVACMode:
|
||||||
|
if hvac_mode.value in mode_value_config:
|
||||||
|
self._hvac_mode_mapping.append(
|
||||||
|
(mode_value_config[hvac_mode.value], hvac_mode)
|
||||||
|
)
|
||||||
|
self._attr_hvac_modes.append(hvac_mode)
|
||||||
|
|
||||||
|
else:
|
||||||
|
# No HVAC modes defined
|
||||||
|
self._hvac_mode_register = None
|
||||||
|
self._attr_hvac_mode = HVACMode.AUTO
|
||||||
|
self._attr_hvac_modes = [HVACMode.AUTO]
|
||||||
|
|
||||||
|
if CONF_HVAC_ONOFF_REGISTER in config:
|
||||||
|
self._hvac_onoff_register = config[CONF_HVAC_ONOFF_REGISTER]
|
||||||
|
if HVACMode.OFF not in self._attr_hvac_modes:
|
||||||
|
self._attr_hvac_modes.append(HVACMode.OFF)
|
||||||
|
else:
|
||||||
|
self._hvac_onoff_register = None
|
||||||
|
|
||||||
async def async_added_to_hass(self) -> None:
|
async def async_added_to_hass(self) -> None:
|
||||||
"""Handle entity which will be added."""
|
"""Handle entity which will be added."""
|
||||||
await self.async_base_added_to_hass()
|
await self.async_base_added_to_hass()
|
||||||
|
@ -99,8 +128,28 @@ class ModbusThermostat(BaseStructPlatform, RestoreEntity, ClimateEntity):
|
||||||
|
|
||||||
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
|
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
|
||||||
"""Set new target hvac mode."""
|
"""Set new target hvac mode."""
|
||||||
# Home Assistant expects this method.
|
if self._hvac_onoff_register is not None:
|
||||||
# We'll keep it here to avoid getting exceptions.
|
# Turn HVAC Off by writing 0 to the On/Off register, or 1 otherwise.
|
||||||
|
await self._hub.async_pymodbus_call(
|
||||||
|
self._slave,
|
||||||
|
self._hvac_onoff_register,
|
||||||
|
0 if hvac_mode == HVACMode.OFF else 1,
|
||||||
|
CALL_TYPE_WRITE_REGISTER,
|
||||||
|
)
|
||||||
|
|
||||||
|
if self._hvac_mode_register is not None:
|
||||||
|
# Write a value to the mode register for the desired mode.
|
||||||
|
for value, mode in self._hvac_mode_mapping:
|
||||||
|
if mode == hvac_mode:
|
||||||
|
await self._hub.async_pymodbus_call(
|
||||||
|
self._slave,
|
||||||
|
self._hvac_mode_register,
|
||||||
|
value,
|
||||||
|
CALL_TYPE_WRITE_REGISTER,
|
||||||
|
)
|
||||||
|
break
|
||||||
|
|
||||||
|
await self.async_update()
|
||||||
|
|
||||||
async def async_set_temperature(self, **kwargs: Any) -> None:
|
async def async_set_temperature(self, **kwargs: Any) -> None:
|
||||||
"""Set new target temperature."""
|
"""Set new target temperature."""
|
||||||
|
@ -158,11 +207,36 @@ class ModbusThermostat(BaseStructPlatform, RestoreEntity, ClimateEntity):
|
||||||
self._attr_current_temperature = await self._async_read_register(
|
self._attr_current_temperature = await self._async_read_register(
|
||||||
self._input_type, self._address
|
self._input_type, self._address
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Read the mode register if defined
|
||||||
|
if self._hvac_mode_register is not None:
|
||||||
|
hvac_mode = await self._async_read_register(
|
||||||
|
CALL_TYPE_REGISTER_HOLDING, self._hvac_mode_register, raw=True
|
||||||
|
)
|
||||||
|
|
||||||
|
# Translate the value received
|
||||||
|
if hvac_mode is not None:
|
||||||
|
self._attr_hvac_mode = None
|
||||||
|
for value, mode in self._hvac_mode_mapping:
|
||||||
|
if hvac_mode == value:
|
||||||
|
self._attr_hvac_mode = mode
|
||||||
|
break
|
||||||
|
|
||||||
|
# Read th on/off register if defined. If the value in this
|
||||||
|
# register is "OFF", it will take precedence over the value
|
||||||
|
# in the mode register.
|
||||||
|
if self._hvac_onoff_register is not None:
|
||||||
|
onoff = await self._async_read_register(
|
||||||
|
CALL_TYPE_REGISTER_HOLDING, self._hvac_onoff_register, raw=True
|
||||||
|
)
|
||||||
|
if onoff == 0:
|
||||||
|
self._attr_hvac_mode = HVACMode.OFF
|
||||||
|
|
||||||
self._call_active = False
|
self._call_active = False
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
async def _async_read_register(
|
async def _async_read_register(
|
||||||
self, register_type: str, register: int
|
self, register_type: str, register: int, raw: bool | None = False
|
||||||
) -> float | None:
|
) -> float | None:
|
||||||
"""Read register using the Modbus hub slave."""
|
"""Read register using the Modbus hub slave."""
|
||||||
result = await self._hub.async_pymodbus_call(
|
result = await self._hub.async_pymodbus_call(
|
||||||
|
@ -177,6 +251,14 @@ class ModbusThermostat(BaseStructPlatform, RestoreEntity, ClimateEntity):
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
self._lazy_errors = self._lazy_error_count
|
self._lazy_errors = self._lazy_error_count
|
||||||
|
|
||||||
|
if raw:
|
||||||
|
# Return the raw value read from the register, do not change
|
||||||
|
# the object's state
|
||||||
|
self._attr_available = True
|
||||||
|
return int(result.registers[0])
|
||||||
|
|
||||||
|
# The regular handling of the value
|
||||||
self._value = self.unpack_structure_result(result.registers)
|
self._value = self.unpack_structure_result(result.registers)
|
||||||
if not self._value:
|
if not self._value:
|
||||||
self._attr_available = False
|
self._attr_available = False
|
||||||
|
|
|
@ -53,6 +53,9 @@ CONF_SWAP_NONE = "none"
|
||||||
CONF_SWAP_WORD = "word"
|
CONF_SWAP_WORD = "word"
|
||||||
CONF_SWAP_WORD_BYTE = "word_byte"
|
CONF_SWAP_WORD_BYTE = "word_byte"
|
||||||
CONF_TARGET_TEMP = "target_temp_register"
|
CONF_TARGET_TEMP = "target_temp_register"
|
||||||
|
CONF_HVAC_MODE_REGISTER = "hvac_mode_register"
|
||||||
|
CONF_HVAC_MODE_VALUES = "values"
|
||||||
|
CONF_HVAC_ONOFF_REGISTER = "hvac_onoff_register"
|
||||||
CONF_VERIFY = "verify"
|
CONF_VERIFY = "verify"
|
||||||
CONF_VERIFY_REGISTER = "verify_register"
|
CONF_VERIFY_REGISTER = "verify_register"
|
||||||
CONF_VERIFY_STATE = "verify_state"
|
CONF_VERIFY_STATE = "verify_state"
|
||||||
|
|
|
@ -1,10 +1,18 @@
|
||||||
"""The tests for the Modbus climate component."""
|
"""The tests for the Modbus climate component."""
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components.climate import DOMAIN as CLIMATE_DOMAIN, HVACMode
|
from homeassistant.components.climate import DOMAIN as CLIMATE_DOMAIN
|
||||||
|
from homeassistant.components.climate.const import (
|
||||||
|
ATTR_HVAC_MODE,
|
||||||
|
ATTR_HVAC_MODES,
|
||||||
|
HVACMode,
|
||||||
|
)
|
||||||
from homeassistant.components.modbus.const import (
|
from homeassistant.components.modbus.const import (
|
||||||
CONF_CLIMATES,
|
CONF_CLIMATES,
|
||||||
CONF_DATA_TYPE,
|
CONF_DATA_TYPE,
|
||||||
|
CONF_HVAC_MODE_REGISTER,
|
||||||
|
CONF_HVAC_MODE_VALUES,
|
||||||
|
CONF_HVAC_ONOFF_REGISTER,
|
||||||
CONF_LAZY_ERROR,
|
CONF_LAZY_ERROR,
|
||||||
CONF_TARGET_TEMP,
|
CONF_TARGET_TEMP,
|
||||||
MODBUS_DOMAIN,
|
MODBUS_DOMAIN,
|
||||||
|
@ -52,6 +60,40 @@ ENTITY_ID = f"{CLIMATE_DOMAIN}.{TEST_ENTITY_NAME}".replace(" ", "_")
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
CONF_CLIMATES: [
|
||||||
|
{
|
||||||
|
CONF_NAME: TEST_ENTITY_NAME,
|
||||||
|
CONF_TARGET_TEMP: 117,
|
||||||
|
CONF_ADDRESS: 117,
|
||||||
|
CONF_SLAVE: 10,
|
||||||
|
CONF_HVAC_ONOFF_REGISTER: 12,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CONF_CLIMATES: [
|
||||||
|
{
|
||||||
|
CONF_NAME: TEST_ENTITY_NAME,
|
||||||
|
CONF_TARGET_TEMP: 117,
|
||||||
|
CONF_ADDRESS: 117,
|
||||||
|
CONF_SLAVE: 10,
|
||||||
|
CONF_HVAC_ONOFF_REGISTER: 12,
|
||||||
|
CONF_HVAC_MODE_REGISTER: {
|
||||||
|
CONF_ADDRESS: 11,
|
||||||
|
CONF_HVAC_MODE_VALUES: {
|
||||||
|
HVACMode.OFF.value: 0,
|
||||||
|
HVACMode.HEAT.value: 1,
|
||||||
|
HVACMode.COOL.value: 2,
|
||||||
|
HVACMode.HEAT_COOL.value: 3,
|
||||||
|
HVACMode.DRY.value: 4,
|
||||||
|
HVACMode.FAN_ONLY.value: 5,
|
||||||
|
HVACMode.AUTO.value: 6,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
async def test_config_climate(hass, mock_modbus):
|
async def test_config_climate(hass, mock_modbus):
|
||||||
|
@ -59,6 +101,62 @@ async def test_config_climate(hass, mock_modbus):
|
||||||
assert CLIMATE_DOMAIN in hass.config.components
|
assert CLIMATE_DOMAIN in hass.config.components
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"do_config",
|
||||||
|
[
|
||||||
|
{
|
||||||
|
CONF_CLIMATES: [
|
||||||
|
{
|
||||||
|
CONF_NAME: TEST_ENTITY_NAME,
|
||||||
|
CONF_TARGET_TEMP: 117,
|
||||||
|
CONF_ADDRESS: 117,
|
||||||
|
CONF_SLAVE: 10,
|
||||||
|
CONF_HVAC_MODE_REGISTER: {
|
||||||
|
CONF_ADDRESS: 11,
|
||||||
|
CONF_HVAC_MODE_VALUES: {
|
||||||
|
HVACMode.OFF.value: 0,
|
||||||
|
HVACMode.HEAT.value: 1,
|
||||||
|
HVACMode.COOL.value: 2,
|
||||||
|
HVACMode.HEAT_COOL.value: 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_config_hvac_mode_register(hass, mock_modbus):
|
||||||
|
"""Run configuration test for mode register."""
|
||||||
|
state = hass.states.get(ENTITY_ID)
|
||||||
|
assert HVACMode.OFF in state.attributes[ATTR_HVAC_MODES]
|
||||||
|
assert HVACMode.HEAT in state.attributes[ATTR_HVAC_MODES]
|
||||||
|
assert HVACMode.COOL in state.attributes[ATTR_HVAC_MODES]
|
||||||
|
assert HVACMode.HEAT_COOL in state.attributes[ATTR_HVAC_MODES]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"do_config",
|
||||||
|
[
|
||||||
|
{
|
||||||
|
CONF_CLIMATES: [
|
||||||
|
{
|
||||||
|
CONF_NAME: TEST_ENTITY_NAME,
|
||||||
|
CONF_TARGET_TEMP: 117,
|
||||||
|
CONF_ADDRESS: 117,
|
||||||
|
CONF_SLAVE: 10,
|
||||||
|
CONF_HVAC_ONOFF_REGISTER: 11,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_config_hvac_onoff_register(hass, mock_modbus):
|
||||||
|
"""Run configuration test for On/Off register."""
|
||||||
|
state = hass.states.get(ENTITY_ID)
|
||||||
|
assert HVACMode.OFF in state.attributes[ATTR_HVAC_MODES]
|
||||||
|
assert HVACMode.AUTO in state.attributes[ATTR_HVAC_MODES]
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"do_config",
|
"do_config",
|
||||||
[
|
[
|
||||||
|
@ -90,28 +188,93 @@ async def test_temperature_climate(hass, expected, mock_do_cycle):
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"do_config",
|
"do_config,result,register_words",
|
||||||
[
|
[
|
||||||
{
|
(
|
||||||
CONF_CLIMATES: [
|
{
|
||||||
{
|
CONF_CLIMATES: [
|
||||||
CONF_NAME: TEST_ENTITY_NAME,
|
{
|
||||||
CONF_TARGET_TEMP: 117,
|
CONF_NAME: TEST_ENTITY_NAME,
|
||||||
CONF_ADDRESS: 117,
|
CONF_TARGET_TEMP: 117,
|
||||||
CONF_SLAVE: 10,
|
CONF_ADDRESS: 117,
|
||||||
CONF_SCAN_INTERVAL: 0,
|
CONF_SLAVE: 10,
|
||||||
CONF_DATA_TYPE: DataType.INT32,
|
CONF_SCAN_INTERVAL: 0,
|
||||||
}
|
CONF_DATA_TYPE: DataType.INT32,
|
||||||
]
|
CONF_HVAC_MODE_REGISTER: {
|
||||||
},
|
CONF_ADDRESS: 118,
|
||||||
|
CONF_HVAC_MODE_VALUES: {
|
||||||
|
HVACMode.COOL.value: 0,
|
||||||
|
HVACMode.HEAT.value: 1,
|
||||||
|
HVACMode.DRY.value: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
HVACMode.COOL,
|
||||||
|
[0x00],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{
|
||||||
|
CONF_CLIMATES: [
|
||||||
|
{
|
||||||
|
CONF_NAME: TEST_ENTITY_NAME,
|
||||||
|
CONF_TARGET_TEMP: 117,
|
||||||
|
CONF_ADDRESS: 117,
|
||||||
|
CONF_SLAVE: 10,
|
||||||
|
CONF_SCAN_INTERVAL: 0,
|
||||||
|
CONF_DATA_TYPE: DataType.INT32,
|
||||||
|
CONF_HVAC_MODE_REGISTER: {
|
||||||
|
CONF_ADDRESS: 118,
|
||||||
|
CONF_HVAC_MODE_VALUES: {
|
||||||
|
HVACMode.COOL.value: 0,
|
||||||
|
HVACMode.HEAT.value: 1,
|
||||||
|
HVACMode.DRY.value: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
HVACMode.HEAT,
|
||||||
|
[0x01],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{
|
||||||
|
CONF_CLIMATES: [
|
||||||
|
{
|
||||||
|
CONF_NAME: TEST_ENTITY_NAME,
|
||||||
|
CONF_TARGET_TEMP: 117,
|
||||||
|
CONF_ADDRESS: 117,
|
||||||
|
CONF_SLAVE: 10,
|
||||||
|
CONF_SCAN_INTERVAL: 0,
|
||||||
|
CONF_DATA_TYPE: DataType.INT32,
|
||||||
|
CONF_HVAC_MODE_REGISTER: {
|
||||||
|
CONF_ADDRESS: 118,
|
||||||
|
CONF_HVAC_MODE_VALUES: {
|
||||||
|
HVACMode.COOL.value: 0,
|
||||||
|
HVACMode.HEAT.value: 2,
|
||||||
|
HVACMode.DRY.value: 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CONF_HVAC_ONOFF_REGISTER: 119,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
HVACMode.OFF,
|
||||||
|
[0x00],
|
||||||
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
async def test_service_climate_update(hass, mock_modbus, mock_ha):
|
async def test_service_climate_update(
|
||||||
|
hass, mock_modbus, mock_ha, result, register_words
|
||||||
|
):
|
||||||
"""Run test for service homeassistant.update_entity."""
|
"""Run test for service homeassistant.update_entity."""
|
||||||
|
mock_modbus.read_holding_registers.return_value = ReadResult(register_words)
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
"homeassistant", "update_entity", {"entity_id": ENTITY_ID}, blocking=True
|
"homeassistant", "update_entity", {"entity_id": ENTITY_ID}, blocking=True
|
||||||
)
|
)
|
||||||
assert hass.states.get(ENTITY_ID).state == "auto"
|
await hass.async_block_till_done()
|
||||||
|
assert hass.states.get(ENTITY_ID).state == result
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
|
@ -195,6 +358,68 @@ async def test_service_climate_set_temperature(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"hvac_mode, result, do_config",
|
||||||
|
[
|
||||||
|
(
|
||||||
|
HVACMode.COOL,
|
||||||
|
[0x00],
|
||||||
|
{
|
||||||
|
CONF_CLIMATES: [
|
||||||
|
{
|
||||||
|
CONF_NAME: TEST_ENTITY_NAME,
|
||||||
|
CONF_TARGET_TEMP: 117,
|
||||||
|
CONF_ADDRESS: 117,
|
||||||
|
CONF_SLAVE: 10,
|
||||||
|
CONF_HVAC_MODE_REGISTER: {
|
||||||
|
CONF_ADDRESS: 118,
|
||||||
|
CONF_HVAC_MODE_VALUES: {
|
||||||
|
HVACMode.COOL.value: 1,
|
||||||
|
HVACMode.HEAT.value: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
HVACMode.HEAT,
|
||||||
|
[0x00],
|
||||||
|
{
|
||||||
|
CONF_CLIMATES: [
|
||||||
|
{
|
||||||
|
CONF_NAME: TEST_ENTITY_NAME,
|
||||||
|
CONF_TARGET_TEMP: 117,
|
||||||
|
CONF_ADDRESS: 117,
|
||||||
|
CONF_SLAVE: 10,
|
||||||
|
CONF_HVAC_MODE_REGISTER: {
|
||||||
|
CONF_ADDRESS: 118,
|
||||||
|
CONF_HVAC_MODE_VALUES: {
|
||||||
|
HVACMode.COOL.value: 1,
|
||||||
|
HVACMode.HEAT.value: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CONF_HVAC_ONOFF_REGISTER: 119,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_service_set_mode(hass, hvac_mode, result, mock_modbus, mock_ha):
|
||||||
|
"""Test set mode."""
|
||||||
|
mock_modbus.read_holding_registers.return_value = ReadResult(result)
|
||||||
|
await hass.services.async_call(
|
||||||
|
CLIMATE_DOMAIN,
|
||||||
|
"set_hvac_mode",
|
||||||
|
{
|
||||||
|
"entity_id": ENTITY_ID,
|
||||||
|
ATTR_HVAC_MODE: hvac_mode,
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
test_value = State(ENTITY_ID, 35)
|
test_value = State(ENTITY_ID, 35)
|
||||||
test_value.attributes = {ATTR_TEMPERATURE: 37}
|
test_value.attributes = {ATTR_TEMPERATURE: 37}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue