Skip to content

Commit

Permalink
thread safe modbus (#3188)
Browse files Browse the repository at this point in the history
  • Loading branch information
persandstrom authored and balloob committed Sep 7, 2016
1 parent 7aafa30 commit d53d8f5
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 31 deletions.
84 changes: 69 additions & 15 deletions homeassistant/components/modbus.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
https://home-assistant.io/components/modbus/
"""
import logging
import threading

from homeassistant.const import (
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP)
Expand Down Expand Up @@ -37,7 +38,7 @@
ATTR_UNIT = "unit"
ATTR_VALUE = "value"

NETWORK = None
HUB = None
TYPE = None


Expand All @@ -50,34 +51,36 @@ def setup(hass, config):

# Connect to Modbus network
# pylint: disable=global-statement, import-error
global NETWORK

if TYPE == "serial":
from pymodbus.client.sync import ModbusSerialClient as ModbusClient
NETWORK = ModbusClient(method=config[DOMAIN][METHOD],
port=config[DOMAIN][SERIAL_PORT],
baudrate=config[DOMAIN][BAUDRATE],
stopbits=config[DOMAIN][STOPBITS],
bytesize=config[DOMAIN][BYTESIZE],
parity=config[DOMAIN][PARITY])
client = ModbusClient(method=config[DOMAIN][METHOD],
port=config[DOMAIN][SERIAL_PORT],
baudrate=config[DOMAIN][BAUDRATE],
stopbits=config[DOMAIN][STOPBITS],
bytesize=config[DOMAIN][BYTESIZE],
parity=config[DOMAIN][PARITY])
elif TYPE == "tcp":
from pymodbus.client.sync import ModbusTcpClient as ModbusClient
NETWORK = ModbusClient(host=config[DOMAIN][HOST],
port=config[DOMAIN][IP_PORT])
client = ModbusClient(host=config[DOMAIN][HOST],
port=config[DOMAIN][IP_PORT])
elif TYPE == "udp":
from pymodbus.client.sync import ModbusUdpClient as ModbusClient
NETWORK = ModbusClient(host=config[DOMAIN][HOST],
port=config[DOMAIN][IP_PORT])
client = ModbusClient(host=config[DOMAIN][HOST],
port=config[DOMAIN][IP_PORT])
else:
return False

global HUB
HUB = ModbusHub(client)

def stop_modbus(event):
"""Stop Modbus service."""
NETWORK.close()
HUB.close()

def start_modbus(event):
"""Start Modbus service."""
NETWORK.connect()
HUB.connect()
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_modbus)

# Register services for modbus
Expand All @@ -88,8 +91,59 @@ def write_register(service):
unit = int(float(service.data.get(ATTR_UNIT)))
address = int(float(service.data.get(ATTR_ADDRESS)))
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)

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)
7 changes: 3 additions & 4 deletions homeassistant/components/sensor/modbus.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,11 @@ def unit_of_measurement(self):
def update(self):
"""Update the state of the sensor."""
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]
else:
result = modbus.NETWORK.read_holding_registers(
unit=self.slave, address=self.register,
count=1)
result = modbus.HUB.read_holding_registers(
self.slave, self.register, 1)
val = 0
for i, res in enumerate(result.registers):
val += res * (2**(i*16))
Expand Down
21 changes: 9 additions & 12 deletions homeassistant/components/switch/modbus.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,36 +90,33 @@ def turn_on(self, **kwargs):
self.update()

if self._coil:
modbus.NETWORK.write_coil(self.register, True)
modbus.HUB.write_coil(self.slave, self.register, True)
else:
val = self.register_value | (0x0001 << self.bit)
modbus.NETWORK.write_register(unit=self.slave,
address=self.register,
value=val)
modbus.HUB.write_register(self.slave, self.register, val)

def turn_off(self, **kwargs):
"""Set switch off."""
if self.register_value is None:
self.update()

if self._coil:
modbus.NETWORK.write_coil(self.register, False)
modbus.HUB.write_coil(self.slave, self.register, False)
else:
val = self.register_value & ~(0x0001 << self.bit)
modbus.NETWORK.write_register(unit=self.slave,
address=self.register,
value=val)
modbus.HUB.write_register(self.slave, self.register, val)

def update(self):
"""Update the state of the switch."""
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._is_on = self.register_value
else:
result = modbus.NETWORK.read_holding_registers(
unit=self.slave, address=self.register,
count=1)
result = modbus.HUB.read_holding_registers(
self.slave,
self.register,
1)
val = 0
for i, res in enumerate(result.registers):
val += res * (2**(i*16))
Expand Down

0 comments on commit d53d8f5

Please sign in to comment.