core/homeassistant/components/modbus/__init__.py

231 lines
7.2 KiB
Python
Raw Normal View History

"""
2016-03-08 16:55:57 +00:00
Support for Modbus.
2015-08-06 15:53:49 +00:00
2015-10-21 19:11:18 +00:00
For more details about this component, please refer to the documentation at
2015-11-09 12:12:18 +00:00
https://home-assistant.io/components/modbus/
"""
import logging
2016-09-07 01:21:38 +00:00
import threading
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
2016-02-19 05:27:50 +00:00
from homeassistant.const import (
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP,
CONF_HOST, CONF_METHOD, CONF_PORT, CONF_TYPE, CONF_TIMEOUT, ATTR_STATE)
DOMAIN = 'modbus'
2019-01-30 11:57:14 +00:00
REQUIREMENTS = ['pymodbus==1.5.2']
# Type of network
CONF_BAUDRATE = 'baudrate'
CONF_BYTESIZE = 'bytesize'
CONF_STOPBITS = 'stopbits'
CONF_PARITY = 'parity'
SERIAL_SCHEMA = {
vol.Required(CONF_BAUDRATE): cv.positive_int,
vol.Required(CONF_BYTESIZE): vol.Any(5, 6, 7, 8),
vol.Required(CONF_METHOD): vol.Any('rtu', 'ascii'),
vol.Required(CONF_PORT): cv.string,
vol.Required(CONF_PARITY): vol.Any('E', 'O', 'N'),
vol.Required(CONF_STOPBITS): vol.Any(1, 2),
vol.Required(CONF_TYPE): 'serial',
vol.Optional(CONF_TIMEOUT, default=3): cv.socket_timeout,
}
ETHERNET_SCHEMA = {
vol.Required(CONF_HOST): cv.string,
vol.Required(CONF_PORT): cv.port,
vol.Required(CONF_TYPE): vol.Any('tcp', 'udp', 'rtuovertcp'),
vol.Optional(CONF_TIMEOUT, default=3): cv.socket_timeout,
}
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Any(SERIAL_SCHEMA, ETHERNET_SCHEMA)
}, extra=vol.ALLOW_EXTRA)
_LOGGER = logging.getLogger(__name__)
SERVICE_WRITE_REGISTER = 'write_register'
SERVICE_WRITE_COIL = 'write_coil'
2016-08-25 08:20:08 +00:00
ATTR_ADDRESS = 'address'
ATTR_UNIT = 'unit'
ATTR_VALUE = 'value'
2016-08-25 08:20:08 +00:00
SERVICE_WRITE_REGISTER_SCHEMA = vol.Schema({
vol.Required(ATTR_UNIT): cv.positive_int,
vol.Required(ATTR_ADDRESS): cv.positive_int,
2017-02-02 09:23:13 +00:00
vol.Required(ATTR_VALUE): vol.All(cv.ensure_list, [cv.positive_int])
})
SERVICE_WRITE_COIL_SCHEMA = vol.Schema({
vol.Required(ATTR_UNIT): cv.positive_int,
vol.Required(ATTR_ADDRESS): cv.positive_int,
vol.Required(ATTR_STATE): cv.boolean
})
2016-09-07 01:21:38 +00:00
HUB = None
2015-04-21 14:40:13 +00:00
def setup(hass, config):
"""Set up Modbus component."""
2015-04-21 14:40:13 +00:00
# Modbus connection type
client_type = config[DOMAIN][CONF_TYPE]
# Connect to Modbus network
# pylint: disable=import-error
if client_type == 'serial':
from pymodbus.client.sync import ModbusSerialClient as ModbusClient
client = ModbusClient(method=config[DOMAIN][CONF_METHOD],
port=config[DOMAIN][CONF_PORT],
baudrate=config[DOMAIN][CONF_BAUDRATE],
stopbits=config[DOMAIN][CONF_STOPBITS],
bytesize=config[DOMAIN][CONF_BYTESIZE],
parity=config[DOMAIN][CONF_PARITY],
timeout=config[DOMAIN][CONF_TIMEOUT])
elif client_type == 'rtuovertcp':
from pymodbus.client.sync import ModbusTcpClient as ModbusClient
from pymodbus.transaction import ModbusRtuFramer as ModbusFramer
client = ModbusClient(host=config[DOMAIN][CONF_HOST],
port=config[DOMAIN][CONF_PORT],
framer=ModbusFramer,
timeout=config[DOMAIN][CONF_TIMEOUT])
elif client_type == 'tcp':
from pymodbus.client.sync import ModbusTcpClient as ModbusClient
client = ModbusClient(host=config[DOMAIN][CONF_HOST],
port=config[DOMAIN][CONF_PORT],
timeout=config[DOMAIN][CONF_TIMEOUT])
elif client_type == 'udp':
from pymodbus.client.sync import ModbusUdpClient as ModbusClient
client = ModbusClient(host=config[DOMAIN][CONF_HOST],
port=config[DOMAIN][CONF_PORT],
timeout=config[DOMAIN][CONF_TIMEOUT])
else:
return False
2016-09-07 01:21:38 +00:00
global HUB
HUB = ModbusHub(client)
def stop_modbus(event):
2016-03-08 16:55:57 +00:00
"""Stop Modbus service."""
2016-09-07 01:21:38 +00:00
HUB.close()
def start_modbus(event):
2016-03-08 16:55:57 +00:00
"""Start Modbus service."""
2016-09-07 01:21:38 +00:00
HUB.connect()
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_modbus)
2016-08-25 08:20:08 +00:00
# Register services for modbus
hass.services.register(
DOMAIN, SERVICE_WRITE_REGISTER, write_register,
schema=SERVICE_WRITE_REGISTER_SCHEMA)
hass.services.register(
DOMAIN, SERVICE_WRITE_COIL, write_coil,
schema=SERVICE_WRITE_COIL_SCHEMA)
2016-08-25 08:20:08 +00:00
def write_register(service):
2016-09-10 15:17:28 +00:00
"""Write modbus registers."""
2016-08-25 08:20:08 +00:00
unit = int(float(service.data.get(ATTR_UNIT)))
address = int(float(service.data.get(ATTR_ADDRESS)))
2016-09-10 15:17:28 +00:00
value = service.data.get(ATTR_VALUE)
if isinstance(value, list):
HUB.write_registers(
unit,
address,
[int(float(i)) for i in value])
else:
HUB.write_register(
unit,
address,
int(float(value)))
2016-08-25 08:20:08 +00:00
def write_coil(service):
"""Write modbus coil."""
unit = service.data.get(ATTR_UNIT)
address = service.data.get(ATTR_ADDRESS)
state = service.data.get(ATTR_STATE)
HUB.write_coil(unit, address, state)
hass.bus.listen_once(EVENT_HOMEASSISTANT_START, start_modbus)
return True
2016-09-07 01:21:38 +00:00
class ModbusHub:
2016-09-07 01:21:38 +00:00
"""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:
kwargs = {'unit': unit} if unit else {}
2016-09-07 01:21:38 +00:00
return self._client.read_coils(
address,
count,
**kwargs)
2016-09-07 01:21:38 +00:00
def read_input_registers(self, unit, address, count):
"""Read input registers."""
with self._lock:
kwargs = {'unit': unit} if unit else {}
return self._client.read_input_registers(
address,
count,
**kwargs)
2016-09-07 01:21:38 +00:00
def read_holding_registers(self, unit, address, count):
"""Read holding registers."""
with self._lock:
kwargs = {'unit': unit} if unit else {}
2016-09-07 01:21:38 +00:00
return self._client.read_holding_registers(
address,
count,
**kwargs)
2016-09-07 01:21:38 +00:00
def write_coil(self, unit, address, value):
"""Write coil."""
with self._lock:
kwargs = {'unit': unit} if unit else {}
2016-09-07 01:21:38 +00:00
self._client.write_coil(
address,
value,
**kwargs)
2016-09-07 01:21:38 +00:00
def write_register(self, unit, address, value):
"""Write register."""
with self._lock:
kwargs = {'unit': unit} if unit else {}
2016-09-07 01:21:38 +00:00
self._client.write_register(
address,
value,
**kwargs)
2016-09-10 15:17:28 +00:00
def write_registers(self, unit, address, values):
"""Write registers."""
with self._lock:
kwargs = {'unit': unit} if unit else {}
2016-09-10 15:17:28 +00:00
self._client.write_registers(
address,
values,
**kwargs)