diff --git a/homeassistant/components/modbus/climate.py b/homeassistant/components/modbus/climate.py index 75a1f846c76..36871c68d9b 100644 --- a/homeassistant/components/modbus/climate.py +++ b/homeassistant/components/modbus/climate.py @@ -33,7 +33,12 @@ from .const import ( CONF_MIN_TEMP, CONF_STEP, CONF_TARGET_TEMP, - DEFAULT_STRUCT_FORMAT, + DATA_TYPE_INT16, + DATA_TYPE_INT32, + DATA_TYPE_INT64, + DATA_TYPE_UINT16, + DATA_TYPE_UINT32, + DATA_TYPE_UINT64, MODBUS_DOMAIN, ) from .modbus import ModbusHub @@ -145,16 +150,28 @@ class ModbusThermostat(BaseStructPlatform, RestoreEntity, ClimateEntity): """Set new target temperature.""" if ATTR_TEMPERATURE not in kwargs: return - target_temperature = int( - (kwargs.get(ATTR_TEMPERATURE) - self._offset) / self._scale - ) - byte_string = struct.pack(self._structure, target_temperature) - struct_string = f">{DEFAULT_STRUCT_FORMAT[self._data_type]}" - register_value = struct.unpack(struct_string, byte_string)[0] + target_temperature = ( + float(kwargs.get(ATTR_TEMPERATURE)) - self._offset + ) / self._scale + if self._data_type in [ + DATA_TYPE_INT16, + DATA_TYPE_INT32, + DATA_TYPE_INT64, + DATA_TYPE_UINT16, + DATA_TYPE_UINT32, + DATA_TYPE_UINT64, + ]: + target_temperature = int(target_temperature) + as_bytes = struct.pack(self._structure, target_temperature) + raw_regs = [ + int.from_bytes(as_bytes[i : i + 2], "big") + for i in range(0, len(as_bytes), 2) + ] + registers = self._swap_registers(raw_regs) result = await self._hub.async_pymodbus_call( self._slave, self._target_temperature_register, - register_value, + registers, CALL_TYPE_WRITE_REGISTERS, ) self._available = result is not None diff --git a/tests/components/modbus/test_climate.py b/tests/components/modbus/test_climate.py index b58822644be..b872f4fe302 100644 --- a/tests/components/modbus/test_climate.py +++ b/tests/components/modbus/test_climate.py @@ -3,7 +3,15 @@ import pytest from homeassistant.components.climate import DOMAIN as CLIMATE_DOMAIN from homeassistant.components.climate.const import HVAC_MODE_AUTO -from homeassistant.components.modbus.const import CONF_CLIMATES, CONF_TARGET_TEMP +from homeassistant.components.modbus.const import ( + CONF_CLIMATES, + CONF_DATA_TYPE, + CONF_TARGET_TEMP, + DATA_TYPE_FLOAT32, + DATA_TYPE_FLOAT64, + DATA_TYPE_INT16, + DATA_TYPE_INT32, +) from homeassistant.const import ( ATTR_TEMPERATURE, CONF_ADDRESS, @@ -110,6 +118,46 @@ async def test_service_climate_update(hass, mock_pymodbus): assert hass.states.get(ENTITY_ID).state == "auto" +@pytest.mark.parametrize( + "data_type, temperature, result", + [ + (DATA_TYPE_INT16, 35, [0x00]), + (DATA_TYPE_INT32, 36, [0x00, 0x00]), + (DATA_TYPE_FLOAT32, 37.5, [0x00, 0x00]), + (DATA_TYPE_FLOAT64, "39", [0x00, 0x00, 0x00, 0x00]), + ], +) +async def test_service_climate_set_temperature( + hass, data_type, temperature, result, mock_pymodbus +): + """Run test for service homeassistant.update_entity.""" + config = { + CONF_CLIMATES: [ + { + CONF_NAME: CLIMATE_NAME, + CONF_TARGET_TEMP: 117, + CONF_ADDRESS: 117, + CONF_SLAVE: 10, + CONF_DATA_TYPE: data_type, + } + ] + } + mock_pymodbus.read_holding_registers.return_value = ReadResult(result) + await prepare_service_update( + hass, + config, + ) + await hass.services.async_call( + CLIMATE_DOMAIN, + "set_temperature", + { + "entity_id": ENTITY_ID, + ATTR_TEMPERATURE: temperature, + }, + blocking=True, + ) + + test_value = State(ENTITY_ID, 35) test_value.attributes = {ATTR_TEMPERATURE: 37}