core/homeassistant/components/modbus.py

205 lines
6.1 KiB
Python

"""
Support for Modbus.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/modbus/
"""
import logging
import threading
import voluptuous as vol
from homeassistant.const import (
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP,
CONF_HOST, CONF_METHOD, CONF_PORT)
import homeassistant.helpers.config_validation as cv
DOMAIN = "modbus"
REQUIREMENTS = ['https://github.com/bashwork/pymodbus/archive/'
'd7fc4f1cc975631e0a9011390e8017f64b612661.zip#pymodbus==1.2.0']
# Type of network
CONF_BAUDRATE = "baudrate"
CONF_BYTESIZE = "bytesize"
CONF_STOPBITS = "stopbits"
CONF_TYPE = "type"
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',
}
ETHERNET_SCHEMA = {
vol.Required(CONF_HOST): cv.string,
vol.Required(CONF_PORT): cv.positive_int,
vol.Required(CONF_TYPE): vol.Any('tcp', 'udp'),
}
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Any(SERIAL_SCHEMA, ETHERNET_SCHEMA)
}, extra=vol.ALLOW_EXTRA)
_LOGGER = logging.getLogger(__name__)
SERVICE_WRITE_REGISTER = "write_register"
ATTR_ADDRESS = "address"
ATTR_UNIT = "unit"
ATTR_VALUE = "value"
SERVICE_WRITE_REGISTER_SCHEMA = vol.Schema({
vol.Required(ATTR_UNIT): cv.positive_int,
vol.Required(ATTR_ADDRESS): cv.positive_int,
vol.Required(ATTR_VALUE): vol.All(cv.ensure_list, [cv.positive_int])
})
HUB = None
def setup(hass, config):
"""Setup Modbus component."""
# Modbus connection type
# pylint: disable=global-statement, import-error
client_type = config[DOMAIN][CONF_TYPE]
# Connect to Modbus network
# pylint: disable=global-statement, 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])
elif client_type == "tcp":
from pymodbus.client.sync import ModbusTcpClient as ModbusClient
client = ModbusClient(host=config[DOMAIN][CONF_HOST],
port=config[DOMAIN][CONF_PORT])
elif client_type == "udp":
from pymodbus.client.sync import ModbusUdpClient as ModbusClient
client = ModbusClient(host=config[DOMAIN][CONF_HOST],
port=config[DOMAIN][CONF_PORT])
else:
return False
global HUB
HUB = ModbusHub(client)
def stop_modbus(event):
"""Stop Modbus service."""
HUB.close()
def start_modbus(event):
"""Start Modbus service."""
HUB.connect()
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_modbus)
# Register services for modbus
hass.services.register(DOMAIN, SERVICE_WRITE_REGISTER, write_register,
schema=SERVICE_WRITE_REGISTER_SCHEMA)
def write_register(service):
"""Write modbus registers."""
unit = int(float(service.data.get(ATTR_UNIT)))
address = int(float(service.data.get(ATTR_ADDRESS)))
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)))
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:
kwargs = {'unit': unit} if unit else {}
return self._client.read_coils(
address,
count,
**kwargs)
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)
def read_holding_registers(self, unit, address, count):
"""Read holding registers."""
with self._lock:
kwargs = {'unit': unit} if unit else {}
return self._client.read_holding_registers(
address,
count,
**kwargs)
def write_coil(self, unit, address, value):
"""Write coil."""
with self._lock:
kwargs = {'unit': unit} if unit else {}
self._client.write_coil(
address,
value,
**kwargs)
def write_register(self, unit, address, value):
"""Write register."""
with self._lock:
kwargs = {'unit': unit} if unit else {}
self._client.write_register(
address,
value,
**kwargs)
def write_registers(self, unit, address, values):
"""Write registers."""
with self._lock:
kwargs = {'unit': unit} if unit else {}
self._client.write_registers(
address,
values,
**kwargs)