Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 6 additions & 0 deletions homeassistant/components/modbus/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,12 @@ def read_coils(self, unit, address, count):
kwargs = {"unit": unit} if unit else {}
return self._client.read_coils(address, count, **kwargs)

def read_discrete_inputs(self, unit, address, count):
"""Read discrete inputs."""
with self._lock:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We normally don't want sync locks in our thread pool. We shouldn't address this in this PR, but it would be something to improve for the future.

It would be ok to replace the sync lock with an asyncio lock. That would require changing the context around the affected methods from sync to async.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I will take a look at it someday. I try to rework Modbus one step at a time. It's not the most perfect integration, but it gets better.

kwargs = {"unit": unit} if unit else {}
return self._client.read_discrete_inputs(address, count, **kwargs)

def read_input_registers(self, unit, address, count):
"""Read input registers."""
with self._lock:
Expand Down
85 changes: 54 additions & 31 deletions homeassistant/components/modbus/binary_sensor.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Support for Modbus Coil sensors."""
"""Support for Modbus Coil and Discrete Input sensors."""
import logging
from typing import Optional

Expand All @@ -16,52 +16,72 @@

_LOGGER = logging.getLogger(__name__)

CONF_COIL = "coil"
CONF_COILS = "coils"

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{
vol.Required(CONF_COILS): [
{
vol.Required(CONF_COIL): cv.positive_int,
vol.Required(CONF_NAME): cv.string,
vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA,
vol.Optional(CONF_HUB, default=DEFAULT_HUB): cv.string,
vol.Optional(CONF_SLAVE): cv.positive_int,
}
]
}
CONF_DEPRECATED_COIL = "coil"
CONF_DEPRECATED_COILS = "coils"

CONF_INPUTS = "inputs"
CONF_INPUT_TYPE = "input_type"
CONF_ADDRESS = "address"

INPUT_TYPE_COIL = "coil"
INPUT_TYPE_DISCRETE = "discrete_input"

PLATFORM_SCHEMA = vol.All(
cv.deprecated(CONF_DEPRECATED_COILS, CONF_INPUTS),
PLATFORM_SCHEMA.extend(
{
vol.Required(CONF_INPUTS): [
vol.All(
cv.deprecated(CONF_DEPRECATED_COIL, CONF_ADDRESS),
vol.Schema(
Comment thread
vzahradnik marked this conversation as resolved.
{
vol.Required(CONF_ADDRESS): cv.positive_int,
vol.Required(CONF_NAME): cv.string,
vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA,
vol.Optional(CONF_HUB, default=DEFAULT_HUB): cv.string,
vol.Optional(CONF_SLAVE): cv.positive_int,
vol.Optional(
CONF_INPUT_TYPE, default=INPUT_TYPE_COIL
): vol.In([INPUT_TYPE_COIL, INPUT_TYPE_DISCRETE]),
}
),
)
]
}
),
)


def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up the Modbus binary sensors."""
sensors = []
for coil in config.get(CONF_COILS):
hub = hass.data[MODBUS_DOMAIN][coil.get(CONF_HUB)]
for entry in config.get(CONF_INPUTS):
hub = hass.data[MODBUS_DOMAIN][entry.get(CONF_HUB)]
sensors.append(
ModbusCoilSensor(
ModbusBinarySensor(
hub,
coil.get(CONF_NAME),
coil.get(CONF_SLAVE),
coil.get(CONF_COIL),
coil.get(CONF_DEVICE_CLASS),
entry.get(CONF_NAME),
entry.get(CONF_SLAVE),
entry.get(CONF_ADDRESS),
entry.get(CONF_DEVICE_CLASS),
entry.get(CONF_INPUT_TYPE),
)
)

add_entities(sensors)


class ModbusCoilSensor(BinarySensorDevice):
"""Modbus coil sensor."""
class ModbusBinarySensor(BinarySensorDevice):
"""Modbus binary sensor."""

def __init__(self, hub, name, slave, coil, device_class):
"""Initialize the Modbus coil sensor."""
def __init__(self, hub, name, slave, address, device_class, input_type):
"""Initialize the Modbus binary sensor."""
self._hub = hub
self._name = name
self._slave = int(slave) if slave else None
self._coil = int(coil)
self._address = int(address)
self._device_class = device_class
self._input_type = input_type
self._value = None

@property
Expand All @@ -81,13 +101,16 @@ def device_class(self) -> Optional[str]:

def update(self):
"""Update the state of the sensor."""
result = self._hub.read_coils(self._slave, self._coil, 1)
if self._input_type == INPUT_TYPE_COIL:
result = self._hub.read_coils(self._slave, self._address, 1)
else:
result = self._hub.read_discrete_inputs(self._slave, self._address, 1)
try:
self._value = result.bits[0]
except AttributeError:
_LOGGER.error(
"No response from hub %s, slave %s, coil %s",
"No response from hub %s, slave %s, address %s",
self._hub.name,
self._slave,
self._coil,
self._address,
)