Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
288bb62
Support of current temp scale and offset
illia-piskurov Aug 21, 2025
3d222d0
Merge branch 'dev' into curr_temp_scale_and_offset
janiversen Aug 21, 2025
6feb103
Try another way
illia-piskurov Aug 22, 2025
5061afb
Separated offset and scale
illia-piskurov Sep 1, 2025
ab50e43
Removed mistakes
illia-piskurov Sep 2, 2025
29b3202
Change in sensor.py
illia-piskurov Sep 2, 2025
2fd3da3
Merge branch 'home-assistant:dev' into curr_temp_scale_and_offset
crug80 Sep 5, 2025
818221d
rev2-crug
crug80 Sep 5, 2025
148002c
Merge branch 'dev' into curr_temp_scale_and_offset
crug80 Sep 5, 2025
57af459
Merge branch 'dev' into curr_temp_scale_and_offset
crug80 Sep 8, 2025
ad3da15
Merge branch 'dev' into curr_temp_scale_and_offset
janiversen Sep 8, 2025
33fec8d
floats
crug80 Sep 8, 2025
a8da718
fix requests
crug80 Sep 9, 2025
cf5662d
Merge branch 'dev' into curr_temp_scale_and_offset
crug80 Sep 9, 2025
0cd6f6a
fix requests 2
crug80 Sep 9, 2025
e45ce85
Merge branch 'home-assistant:dev' into curr_temp_scale_and_offset
crug80 Sep 12, 2025
91366b5
Merge branch 'dev' into curr_temp_scale_and_offset
janiversen Sep 13, 2025
c9ae098
fix requests 3
crug80 Sep 13, 2025
30ef8e5
Merge branch 'dev' into curr_temp_scale_and_offset
crug80 Sep 16, 2025
cab1082
Merge branch 'dev' into curr_temp_scale_and_offset
crug80 Sep 22, 2025
2b650a2
fix ruff
crug80 Sep 22, 2025
010f556
Merge branch 'dev' into curr_temp_scale_and_offset
illia-piskurov Sep 24, 2025
82a53ed
Merge branch 'dev' into curr_temp_scale_and_offset
illia-piskurov Sep 30, 2025
7188f6c
Merge branch 'dev' into curr_temp_scale_and_offset
crug80 Oct 8, 2025
de33a03
Merge branch 'dev' into curr_temp_scale_and_offset
crug80 Oct 19, 2025
f7d30ef
Merge branch 'dev' into curr_temp_scale_and_offset
frenck Oct 19, 2025
bea9139
Update homeassistant/components/modbus/validators.py
crug80 Oct 20, 2025
ae53aef
Merge branch 'dev' into curr_temp_scale_and_offset
illia-piskurov Oct 30, 2025
9032dbf
Merge branch 'home-assistant:dev' into curr_temp_scale_and_offset
crug80 Oct 30, 2025
3496c7c
fix emontnemery request
crug80 Oct 30, 2025
c56ef49
Merge branch 'dev' into curr_temp_scale_and_offset
crug80 Oct 30, 2025
d60deca
Merge branch 'dev' into curr_temp_scale_and_offset
crug80 Oct 31, 2025
4153de7
Merge branch 'dev' into curr_temp_scale_and_offset
emontnemery Nov 3, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions homeassistant/components/modbus/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@
CONF_BYTESIZE,
CONF_CLIMATES,
CONF_COLOR_TEMP_REGISTER,
CONF_CURRENT_TEMP_OFFSET,
CONF_CURRENT_TEMP_SCALE,
CONF_DATA_TYPE,
CONF_DEVICE_ADDRESS,
CONF_FAN_MODE_AUTO,
Expand Down Expand Up @@ -137,6 +139,8 @@
CONF_SWING_MODE_SWING_VERT,
CONF_SWING_MODE_VALUES,
CONF_TARGET_TEMP,
CONF_TARGET_TEMP_OFFSET,
CONF_TARGET_TEMP_SCALE,
CONF_TARGET_TEMP_WRITE_REGISTERS,
CONF_VERIFY,
CONF_VIRTUAL_COUNT,
Expand Down Expand Up @@ -273,6 +277,10 @@
vol.Optional(CONF_TEMPERATURE_UNIT, default=DEFAULT_TEMP_UNIT): cv.string,
vol.Exclusive(CONF_HVAC_ONOFF_COIL, "hvac_onoff_type"): cv.positive_int,
vol.Exclusive(CONF_HVAC_ONOFF_REGISTER, "hvac_onoff_type"): cv.positive_int,
vol.Inclusive(CONF_CURRENT_TEMP_SCALE, "current_temp"): vol.Coerce(float),
Comment thread
illia-piskurov marked this conversation as resolved.
Outdated
vol.Inclusive(CONF_CURRENT_TEMP_OFFSET, "current_temp"): vol.Coerce(float),
vol.Inclusive(CONF_TARGET_TEMP_SCALE, "target_temp"): vol.Coerce(float),
vol.Inclusive(CONF_TARGET_TEMP_OFFSET, "target_temp"): vol.Coerce(float),
vol.Optional(
CONF_HVAC_ON_VALUE, default=DEFAULT_HVAC_ON_VALUE
): cv.positive_int,
Expand Down
81 changes: 67 additions & 14 deletions homeassistant/components/modbus/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@
CALL_TYPE_WRITE_REGISTER,
CALL_TYPE_WRITE_REGISTERS,
CONF_CLIMATES,
CONF_CURRENT_TEMP_OFFSET,
CONF_CURRENT_TEMP_SCALE,
CONF_FAN_MODE_AUTO,
CONF_FAN_MODE_DIFFUSE,
CONF_FAN_MODE_FOCUS,
Expand Down Expand Up @@ -97,6 +99,8 @@
CONF_SWING_MODE_SWING_VERT,
CONF_SWING_MODE_VALUES,
CONF_TARGET_TEMP,
CONF_TARGET_TEMP_OFFSET,
CONF_TARGET_TEMP_SCALE,
CONF_TARGET_TEMP_WRITE_REGISTERS,
CONF_WRITE_REGISTERS,
DataType,
Expand Down Expand Up @@ -301,6 +305,20 @@ def __init__(
else:
self._hvac_onoff_coil = None

if CONF_CURRENT_TEMP_SCALE in config and CONF_CURRENT_TEMP_OFFSET in config:
self._current_temp_scale = config[CONF_CURRENT_TEMP_SCALE]
self._current_temp_offset = config[CONF_CURRENT_TEMP_OFFSET]
Comment thread
illia-piskurov marked this conversation as resolved.
Outdated
else:
self._current_temp_scale = None
self._current_temp_offset = None

if CONF_TARGET_TEMP_SCALE in config and CONF_TARGET_TEMP_OFFSET in config:
Comment thread
illia-piskurov marked this conversation as resolved.
Outdated
self._target_temp_scale = config[CONF_TARGET_TEMP_SCALE]
self._target_temp_offset = config[CONF_TARGET_TEMP_OFFSET]
else:
self._target_temp_scale = None
self._target_temp_offset = None

async def async_added_to_hass(self) -> None:
"""Handle entity which will be added."""
await self.async_base_added_to_hass()
Expand Down Expand Up @@ -412,9 +430,14 @@ async def async_set_swing_mode(self, swing_mode: str) -> None:

async def async_set_temperature(self, **kwargs: Any) -> None:
"""Set new target temperature."""
target_temperature = (
float(kwargs[ATTR_TEMPERATURE]) - self._offset
) / self._scale
if self._target_temp_scale is not None and self._target_temp_offset is not None:
Comment thread
illia-piskurov marked this conversation as resolved.
Outdated
target_temperature = (
float(kwargs[ATTR_TEMPERATURE]) - self._target_temp_offset
) / self._target_temp_scale
else:
target_temperature = (
float(kwargs[ATTR_TEMPERATURE]) - self._offset
) / self._scale
if self._data_type in (
DataType.INT16,
DataType.INT32,
Expand Down Expand Up @@ -467,16 +490,42 @@ async def async_set_temperature(self, **kwargs: Any) -> None:

async def _async_update(self) -> None:
"""Update Target & Current Temperature."""
self._attr_target_temperature = await self._async_read_register(
CALL_TYPE_REGISTER_HOLDING,
self._target_temperature_register[
HVACMODE_TO_TARG_TEMP_REG_INDEX_ARRAY[self._attr_hvac_mode]
],
)
if self._target_temp_scale is not None and self._target_temp_offset is not None:
target_temp_register_value = await self._async_read_register(
CALL_TYPE_REGISTER_HOLDING,
self._target_temperature_register[
HVACMODE_TO_TARG_TEMP_REG_INDEX_ARRAY[self._attr_hvac_mode]
],
skip_transform=True,
)
self._attr_target_temperature = (
self._target_temp_scale * target_temp_register_value
+ self._target_temp_offset
)
else:
self._attr_target_temperature = await self._async_read_register(
CALL_TYPE_REGISTER_HOLDING,
self._target_temperature_register[
HVACMODE_TO_TARG_TEMP_REG_INDEX_ARRAY[self._attr_hvac_mode]
],
)

if (
self._current_temp_scale is not None
and self._current_temp_offset is not None
):
current_temp_register_value = await self._async_read_register(
self._input_type, self._address, skip_transform=True
)
self._attr_current_temperature = (
self._current_temp_scale * current_temp_register_value
+ self._current_temp_offset
)
else:
self._attr_current_temperature = await self._async_read_register(
self._input_type, self._address
)

self._attr_current_temperature = await self._async_read_register(
self._input_type, self._address
)
# Read the HVAC mode register if defined
if self._hvac_mode_register is not None:
hvac_mode = await self._async_read_register(
Expand Down Expand Up @@ -562,7 +611,11 @@ async def _async_update(self) -> None:
self._attr_hvac_mode = HVACMode.OFF

async def _async_read_register(
self, register_type: str, register: int, raw: bool | None = False
self,
register_type: str,
register: int,
raw: bool | None = False,
skip_transform: bool | None = False,
) -> float | None:
"""Read register using the Modbus hub slave."""
result = await self._hub.async_pb_call(
Expand All @@ -579,7 +632,7 @@ async def _async_read_register(
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, skip_transform)
if not self._value:
self._attr_available = False
return None
Expand Down
4 changes: 4 additions & 0 deletions homeassistant/components/modbus/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
CONF_CLIMATES = "climates"
CONF_BRIGHTNESS_REGISTER = "brightness_address"
CONF_COLOR_TEMP_REGISTER = "color_temp_address"
CONF_CURRENT_TEMP_SCALE = "current_temp_scale"
CONF_CURRENT_TEMP_OFFSET = "current_temp_offset"
CONF_TARGET_TEMP_SCALE = "target_temp_scale"
CONF_TARGET_TEMP_OFFSET = "target_temp_offset"
CONF_DATA_TYPE = "data_type"
CONF_DEVICE_ADDRESS = "device_address"
CONF_FANS = "fans"
Expand Down
16 changes: 11 additions & 5 deletions homeassistant/components/modbus/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,9 @@ def _swap_registers(self, registers: list[int], slave_count: int) -> list[int]:
registers.reverse()
return registers

def __process_raw_value(self, entry: float | str | bytes) -> str | None:
def __process_raw_value(
self, entry: float | str | bytes, skip_transform: bool | None = False
) -> str | None:
Comment thread
illia-piskurov marked this conversation as resolved.
"""Process value from sensor with NaN handling, scaling, offset, min/max etc."""
if self._nan_value and entry in (self._nan_value, -self._nan_value):
return None
Expand All @@ -265,7 +267,9 @@ def __process_raw_value(self, entry: float | str | bytes) -> str | None:
if entry != entry: # noqa: PLR0124
# NaN float detection replace with None
return None
val: float | int = self._scale * entry + self._offset
val: float | int = cast(float | int, entry)
if not skip_transform:
val = self._scale * entry + self._offset
if self._min_value is not None and val < self._min_value:
val = self._min_value
if self._max_value is not None and val > self._max_value:
Expand All @@ -276,7 +280,9 @@ def __process_raw_value(self, entry: float | str | bytes) -> str | None:
return str(round(val))
return f"{float(val):.{self._precision}f}"

def unpack_structure_result(self, registers: list[int]) -> str | None:
def unpack_structure_result(
self, registers: list[int], skip_transform: bool | None = False
) -> str | None:
"""Convert registers to proper result."""

if self._swap:
Expand All @@ -298,15 +304,15 @@ def unpack_structure_result(self, registers: list[int]) -> str | None:
# Apply scale, precision, limits to floats and ints
v_result = []
for entry in val:
v_temp = self.__process_raw_value(entry)
v_temp = self.__process_raw_value(entry, skip_transform)
if self._data_type != DataType.CUSTOM:
v_result.append(str(v_temp))
else:
v_result.append(str(v_temp) if v_temp is not None else "0")
return ",".join(map(str, v_result))

# Apply scale, precision, limits to floats and ints
return self.__process_raw_value(val[0])
return self.__process_raw_value(val[0], skip_transform)


class BaseSwitch(BasePlatform, ToggleEntity, RestoreEntity):
Expand Down
Loading