mirror of
https://github.com/home-assistant/core.git
synced 2025-07-22 20:57:21 +00:00
thread safe modbus (#3188)
This commit is contained in:
parent
7aafa309c9
commit
d53d8f5ea9
@ -5,6 +5,7 @@ For more details about this component, please refer to the documentation at
|
|||||||
https://home-assistant.io/components/modbus/
|
https://home-assistant.io/components/modbus/
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
import threading
|
||||||
|
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP)
|
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP)
|
||||||
@ -37,7 +38,7 @@ ATTR_ADDRESS = "address"
|
|||||||
ATTR_UNIT = "unit"
|
ATTR_UNIT = "unit"
|
||||||
ATTR_VALUE = "value"
|
ATTR_VALUE = "value"
|
||||||
|
|
||||||
NETWORK = None
|
HUB = None
|
||||||
TYPE = None
|
TYPE = None
|
||||||
|
|
||||||
|
|
||||||
@ -50,34 +51,36 @@ def setup(hass, config):
|
|||||||
|
|
||||||
# Connect to Modbus network
|
# Connect to Modbus network
|
||||||
# pylint: disable=global-statement, import-error
|
# pylint: disable=global-statement, import-error
|
||||||
global NETWORK
|
|
||||||
|
|
||||||
if TYPE == "serial":
|
if TYPE == "serial":
|
||||||
from pymodbus.client.sync import ModbusSerialClient as ModbusClient
|
from pymodbus.client.sync import ModbusSerialClient as ModbusClient
|
||||||
NETWORK = ModbusClient(method=config[DOMAIN][METHOD],
|
client = ModbusClient(method=config[DOMAIN][METHOD],
|
||||||
port=config[DOMAIN][SERIAL_PORT],
|
port=config[DOMAIN][SERIAL_PORT],
|
||||||
baudrate=config[DOMAIN][BAUDRATE],
|
baudrate=config[DOMAIN][BAUDRATE],
|
||||||
stopbits=config[DOMAIN][STOPBITS],
|
stopbits=config[DOMAIN][STOPBITS],
|
||||||
bytesize=config[DOMAIN][BYTESIZE],
|
bytesize=config[DOMAIN][BYTESIZE],
|
||||||
parity=config[DOMAIN][PARITY])
|
parity=config[DOMAIN][PARITY])
|
||||||
elif TYPE == "tcp":
|
elif TYPE == "tcp":
|
||||||
from pymodbus.client.sync import ModbusTcpClient as ModbusClient
|
from pymodbus.client.sync import ModbusTcpClient as ModbusClient
|
||||||
NETWORK = ModbusClient(host=config[DOMAIN][HOST],
|
client = ModbusClient(host=config[DOMAIN][HOST],
|
||||||
port=config[DOMAIN][IP_PORT])
|
port=config[DOMAIN][IP_PORT])
|
||||||
elif TYPE == "udp":
|
elif TYPE == "udp":
|
||||||
from pymodbus.client.sync import ModbusUdpClient as ModbusClient
|
from pymodbus.client.sync import ModbusUdpClient as ModbusClient
|
||||||
NETWORK = ModbusClient(host=config[DOMAIN][HOST],
|
client = ModbusClient(host=config[DOMAIN][HOST],
|
||||||
port=config[DOMAIN][IP_PORT])
|
port=config[DOMAIN][IP_PORT])
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
global HUB
|
||||||
|
HUB = ModbusHub(client)
|
||||||
|
|
||||||
def stop_modbus(event):
|
def stop_modbus(event):
|
||||||
"""Stop Modbus service."""
|
"""Stop Modbus service."""
|
||||||
NETWORK.close()
|
HUB.close()
|
||||||
|
|
||||||
def start_modbus(event):
|
def start_modbus(event):
|
||||||
"""Start Modbus service."""
|
"""Start Modbus service."""
|
||||||
NETWORK.connect()
|
HUB.connect()
|
||||||
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_modbus)
|
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_modbus)
|
||||||
|
|
||||||
# Register services for modbus
|
# Register services for modbus
|
||||||
@ -88,8 +91,59 @@ def setup(hass, config):
|
|||||||
unit = int(float(service.data.get(ATTR_UNIT)))
|
unit = int(float(service.data.get(ATTR_UNIT)))
|
||||||
address = int(float(service.data.get(ATTR_ADDRESS)))
|
address = int(float(service.data.get(ATTR_ADDRESS)))
|
||||||
value = int(float(service.data.get(ATTR_VALUE)))
|
value = int(float(service.data.get(ATTR_VALUE)))
|
||||||
NETWORK.write_register(address, value, unit=unit)
|
HUB.write_register(unit, address, value)
|
||||||
|
|
||||||
hass.bus.listen_once(EVENT_HOMEASSISTANT_START, start_modbus)
|
hass.bus.listen_once(EVENT_HOMEASSISTANT_START, start_modbus)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class ModbusHub(object):
|
||||||
|
"""Thread safe wrapper class for pymodbus."""
|
||||||
|
|
||||||
|
def __init__(self, modbus_client):
|
||||||
|
"""Initialize the modbus hub."""
|
||||||
|
self._client = modbus_client
|
||||||
|
self._lock = threading.Lock()
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
"""Disconnect client."""
|
||||||
|
with self._lock:
|
||||||
|
self._client.close()
|
||||||
|
|
||||||
|
def connect(self):
|
||||||
|
"""Connect client."""
|
||||||
|
with self._lock:
|
||||||
|
self._client.connect()
|
||||||
|
|
||||||
|
def read_coils(self, unit, address, count):
|
||||||
|
"""Read coils."""
|
||||||
|
with self._lock:
|
||||||
|
return self._client.read_coils(
|
||||||
|
address,
|
||||||
|
count,
|
||||||
|
unit=unit)
|
||||||
|
|
||||||
|
def read_holding_registers(self, unit, address, count):
|
||||||
|
"""Read holding registers."""
|
||||||
|
with self._lock:
|
||||||
|
return self._client.read_holding_registers(
|
||||||
|
address,
|
||||||
|
count,
|
||||||
|
unit=unit)
|
||||||
|
|
||||||
|
def write_coil(self, unit, address, value):
|
||||||
|
"""Write coil."""
|
||||||
|
with self._lock:
|
||||||
|
self._client.write_coil(
|
||||||
|
address,
|
||||||
|
value,
|
||||||
|
unit=unit)
|
||||||
|
|
||||||
|
def write_register(self, unit, address, value):
|
||||||
|
"""Write register."""
|
||||||
|
with self._lock:
|
||||||
|
self._client.write_register(
|
||||||
|
address,
|
||||||
|
value,
|
||||||
|
unit=unit)
|
||||||
|
@ -114,12 +114,11 @@ class ModbusSensor(Entity):
|
|||||||
def update(self):
|
def update(self):
|
||||||
"""Update the state of the sensor."""
|
"""Update the state of the sensor."""
|
||||||
if self._coil:
|
if self._coil:
|
||||||
result = modbus.NETWORK.read_coils(self.register, 1)
|
result = modbus.HUB.read_coils(self.slave, self.register, 1)
|
||||||
self._value = result.bits[0]
|
self._value = result.bits[0]
|
||||||
else:
|
else:
|
||||||
result = modbus.NETWORK.read_holding_registers(
|
result = modbus.HUB.read_holding_registers(
|
||||||
unit=self.slave, address=self.register,
|
self.slave, self.register, 1)
|
||||||
count=1)
|
|
||||||
val = 0
|
val = 0
|
||||||
for i, res in enumerate(result.registers):
|
for i, res in enumerate(result.registers):
|
||||||
val += res * (2**(i*16))
|
val += res * (2**(i*16))
|
||||||
|
@ -90,12 +90,10 @@ class ModbusSwitch(ToggleEntity):
|
|||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
if self._coil:
|
if self._coil:
|
||||||
modbus.NETWORK.write_coil(self.register, True)
|
modbus.HUB.write_coil(self.slave, self.register, True)
|
||||||
else:
|
else:
|
||||||
val = self.register_value | (0x0001 << self.bit)
|
val = self.register_value | (0x0001 << self.bit)
|
||||||
modbus.NETWORK.write_register(unit=self.slave,
|
modbus.HUB.write_register(self.slave, self.register, val)
|
||||||
address=self.register,
|
|
||||||
value=val)
|
|
||||||
|
|
||||||
def turn_off(self, **kwargs):
|
def turn_off(self, **kwargs):
|
||||||
"""Set switch off."""
|
"""Set switch off."""
|
||||||
@ -103,23 +101,22 @@ class ModbusSwitch(ToggleEntity):
|
|||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
if self._coil:
|
if self._coil:
|
||||||
modbus.NETWORK.write_coil(self.register, False)
|
modbus.HUB.write_coil(self.slave, self.register, False)
|
||||||
else:
|
else:
|
||||||
val = self.register_value & ~(0x0001 << self.bit)
|
val = self.register_value & ~(0x0001 << self.bit)
|
||||||
modbus.NETWORK.write_register(unit=self.slave,
|
modbus.HUB.write_register(self.slave, self.register, val)
|
||||||
address=self.register,
|
|
||||||
value=val)
|
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
"""Update the state of the switch."""
|
"""Update the state of the switch."""
|
||||||
if self._coil:
|
if self._coil:
|
||||||
result = modbus.NETWORK.read_coils(self.register, 1)
|
result = modbus.HUB.read_coils(self.slave, self.register, 1)
|
||||||
self.register_value = result.bits[0]
|
self.register_value = result.bits[0]
|
||||||
self._is_on = self.register_value
|
self._is_on = self.register_value
|
||||||
else:
|
else:
|
||||||
result = modbus.NETWORK.read_holding_registers(
|
result = modbus.HUB.read_holding_registers(
|
||||||
unit=self.slave, address=self.register,
|
self.slave,
|
||||||
count=1)
|
self.register,
|
||||||
|
1)
|
||||||
val = 0
|
val = 0
|
||||||
for i, res in enumerate(result.registers):
|
for i, res in enumerate(result.registers):
|
||||||
val += res * (2**(i*16))
|
val += res * (2**(i*16))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user