Add modbus min/max values (#86131)
* modbus min/max values Co-authored-by: jan iversen <jancasacondor@gmail.com>pull/86056/head^2
parent
9f9873b39a
commit
8bff95014c
|
@ -78,7 +78,9 @@ from .const import ( # noqa: F401
|
|||
CONF_INPUT_TYPE,
|
||||
CONF_LAZY_ERROR,
|
||||
CONF_MAX_TEMP,
|
||||
CONF_MAX_VALUE,
|
||||
CONF_MIN_TEMP,
|
||||
CONF_MIN_VALUE,
|
||||
CONF_MSG_WAIT,
|
||||
CONF_PARITY,
|
||||
CONF_PRECISION,
|
||||
|
@ -104,6 +106,7 @@ from .const import ( # noqa: F401
|
|||
CONF_TARGET_TEMP,
|
||||
CONF_VERIFY,
|
||||
CONF_WRITE_TYPE,
|
||||
CONF_ZERO_SUPPRESS,
|
||||
DEFAULT_HUB,
|
||||
DEFAULT_SCAN_INTERVAL,
|
||||
DEFAULT_TEMP_UNIT,
|
||||
|
@ -288,6 +291,9 @@ SENSOR_SCHEMA = vol.All(
|
|||
vol.Optional(CONF_STATE_CLASS): SENSOR_STATE_CLASSES_SCHEMA,
|
||||
vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string,
|
||||
vol.Optional(CONF_SLAVE_COUNT, default=0): cv.positive_int,
|
||||
vol.Optional(CONF_MIN_VALUE): number_validator,
|
||||
vol.Optional(CONF_MAX_VALUE): number_validator,
|
||||
vol.Optional(CONF_ZERO_SUPPRESS): number_validator,
|
||||
}
|
||||
),
|
||||
)
|
||||
|
|
|
@ -44,6 +44,8 @@ from .const import (
|
|||
CONF_DATA_TYPE,
|
||||
CONF_INPUT_TYPE,
|
||||
CONF_LAZY_ERROR,
|
||||
CONF_MAX_VALUE,
|
||||
CONF_MIN_VALUE,
|
||||
CONF_PRECISION,
|
||||
CONF_SCALE,
|
||||
CONF_STATE_OFF,
|
||||
|
@ -54,6 +56,7 @@ from .const import (
|
|||
CONF_SWAP_WORD_BYTE,
|
||||
CONF_VERIFY,
|
||||
CONF_WRITE_TYPE,
|
||||
CONF_ZERO_SUPPRESS,
|
||||
SIGNAL_START_ENTITY,
|
||||
SIGNAL_STOP_ENTITY,
|
||||
DataType,
|
||||
|
@ -92,6 +95,18 @@ class BasePlatform(Entity):
|
|||
self._lazy_error_count = entry[CONF_LAZY_ERROR]
|
||||
self._lazy_errors = self._lazy_error_count
|
||||
|
||||
def get_optional_numeric_config(config_name: str) -> int | float | None:
|
||||
if (val := entry.get(config_name)) is None:
|
||||
return None
|
||||
assert isinstance(
|
||||
val, (float, int)
|
||||
), f"Expected float or int but {config_name} was {type(val)}"
|
||||
return val
|
||||
|
||||
self._min_value = get_optional_numeric_config(CONF_MIN_VALUE)
|
||||
self._max_value = get_optional_numeric_config(CONF_MAX_VALUE)
|
||||
self._zero_suppress = get_optional_numeric_config(CONF_ZERO_SUPPRESS)
|
||||
|
||||
@abstractmethod
|
||||
async def async_update(self, now: datetime | None = None) -> None:
|
||||
"""Virtual function to be overwritten."""
|
||||
|
@ -162,6 +177,17 @@ class BaseStructPlatform(BasePlatform, RestoreEntity):
|
|||
registers.reverse()
|
||||
return registers
|
||||
|
||||
def __process_raw_value(self, entry: float | int) -> float | int:
|
||||
"""Process value from sensor with scaling, offset, min/max etc."""
|
||||
val: float | int = self._scale * entry + self._offset
|
||||
if self._min_value is not None and val < self._min_value:
|
||||
return self._min_value
|
||||
if self._max_value is not None and val > self._max_value:
|
||||
return self._max_value
|
||||
if self._zero_suppress is not None and abs(val) <= self._zero_suppress:
|
||||
return 0
|
||||
return val
|
||||
|
||||
def unpack_structure_result(self, registers: list[int]) -> str | None:
|
||||
"""Convert registers to proper result."""
|
||||
|
||||
|
@ -181,10 +207,10 @@ class BaseStructPlatform(BasePlatform, RestoreEntity):
|
|||
# If unpack() returns a tuple greater than 1, don't try to process the value.
|
||||
# Instead, return the values of unpack(...) separated by commas.
|
||||
if len(val) > 1:
|
||||
# Apply scale and precision to floats and ints
|
||||
# Apply scale, precision, limits to floats and ints
|
||||
v_result = []
|
||||
for entry in val:
|
||||
v_temp = self._scale * entry + self._offset
|
||||
v_temp = self.__process_raw_value(entry)
|
||||
|
||||
# We could convert int to float, and the code would still work; however
|
||||
# we lose some precision, and unit tests will fail. Therefore, we do
|
||||
|
@ -195,8 +221,8 @@ class BaseStructPlatform(BasePlatform, RestoreEntity):
|
|||
v_result.append(f"{float(v_temp):.{self._precision}f}")
|
||||
return ",".join(map(str, v_result))
|
||||
|
||||
# Apply scale and precision to floats and ints
|
||||
val_result: float | int = self._scale * val[0] + self._offset
|
||||
# Apply scale, precision, limits to floats and ints
|
||||
val_result = self.__process_raw_value(val[0])
|
||||
|
||||
# We could convert int to float, and the code would still work; however
|
||||
# we lose some precision, and unit tests will fail. Therefore, we do
|
||||
|
|
|
@ -26,7 +26,9 @@ CONF_INPUTS = "inputs"
|
|||
CONF_INPUT_TYPE = "input_type"
|
||||
CONF_LAZY_ERROR = "lazy_error_count"
|
||||
CONF_MAX_TEMP = "max_temp"
|
||||
CONF_MAX_VALUE = "max_value"
|
||||
CONF_MIN_TEMP = "min_temp"
|
||||
CONF_MIN_VALUE = "min_value"
|
||||
CONF_MSG_WAIT = "message_wait_milliseconds"
|
||||
CONF_PARITY = "parity"
|
||||
CONF_REGISTER = "register"
|
||||
|
@ -67,6 +69,7 @@ CONF_VERIFY = "verify"
|
|||
CONF_VERIFY_REGISTER = "verify_register"
|
||||
CONF_VERIFY_STATE = "verify_state"
|
||||
CONF_WRITE_TYPE = "write_type"
|
||||
CONF_ZERO_SUPPRESS = "zero_suppress"
|
||||
|
||||
RTUOVERTCP = "rtuovertcp"
|
||||
SERIAL = "serial"
|
||||
|
|
|
@ -7,6 +7,8 @@ from homeassistant.components.modbus.const import (
|
|||
CONF_DATA_TYPE,
|
||||
CONF_INPUT_TYPE,
|
||||
CONF_LAZY_ERROR,
|
||||
CONF_MAX_VALUE,
|
||||
CONF_MIN_VALUE,
|
||||
CONF_PRECISION,
|
||||
CONF_SCALE,
|
||||
CONF_SLAVE_COUNT,
|
||||
|
@ -15,6 +17,7 @@ from homeassistant.components.modbus.const import (
|
|||
CONF_SWAP_NONE,
|
||||
CONF_SWAP_WORD,
|
||||
CONF_SWAP_WORD_BYTE,
|
||||
CONF_ZERO_SUPPRESS,
|
||||
MODBUS_DOMAIN,
|
||||
DataType,
|
||||
)
|
||||
|
@ -535,6 +538,42 @@ async def test_config_wrong_struct_sensor(hass, error_message, mock_modbus, capl
|
|||
False,
|
||||
str(int(0x04030201)),
|
||||
),
|
||||
(
|
||||
{
|
||||
CONF_DATA_TYPE: DataType.INT32,
|
||||
CONF_MAX_VALUE: int(0x02010400),
|
||||
},
|
||||
[0x0201, 0x0403],
|
||||
False,
|
||||
str(int(0x02010400)),
|
||||
),
|
||||
(
|
||||
{
|
||||
CONF_DATA_TYPE: DataType.INT32,
|
||||
CONF_MIN_VALUE: int(0x02010404),
|
||||
},
|
||||
[0x0201, 0x0403],
|
||||
False,
|
||||
str(int(0x02010404)),
|
||||
),
|
||||
(
|
||||
{
|
||||
CONF_DATA_TYPE: DataType.INT32,
|
||||
CONF_ZERO_SUPPRESS: int(0x00000001),
|
||||
},
|
||||
[0x0000, 0x0002],
|
||||
False,
|
||||
str(int(0x00000002)),
|
||||
),
|
||||
(
|
||||
{
|
||||
CONF_DATA_TYPE: DataType.INT32,
|
||||
CONF_ZERO_SUPPRESS: int(0x00000002),
|
||||
},
|
||||
[0x0000, 0x0002],
|
||||
False,
|
||||
str(int(0)),
|
||||
),
|
||||
(
|
||||
{
|
||||
CONF_INPUT_TYPE: CALL_TYPE_REGISTER_INPUT,
|
||||
|
|
Loading…
Reference in New Issue