From aec861c1a4a44b0d90aba4608b238b2abeca1573 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 14 Mar 2015 12:38:30 -0700 Subject: [PATCH 1/6] Fixes for new release PyLint --- homeassistant/components/light/__init__.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/homeassistant/components/light/__init__.py b/homeassistant/components/light/__init__.py index 6e3bb7667f0..a0bcd742aa2 100644 --- a/homeassistant/components/light/__init__.py +++ b/homeassistant/components/light/__init__.py @@ -190,7 +190,6 @@ def setup(hass, config): if service.service == SERVICE_TURN_OFF: for light in target_lights: - # pylint: disable=star-args light.turn_off(**params) else: @@ -248,7 +247,6 @@ def setup(hass, config): params[ATTR_FLASH] = FLASH_LONG for light in target_lights: - # pylint: disable=star-args light.turn_on(**params) for light in target_lights: From 6da0257bb66150dfd75af902dfb519d5c875a4da Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 14 Mar 2015 19:13:03 -0700 Subject: [PATCH 2/6] Fix a config bug in Automation --- config/configuration.yaml.example | 3 ++- homeassistant/components/automation/__init__.py | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/configuration.yaml.example b/config/configuration.yaml.example index 51217c090bb..c758833d336 100644 --- a/config/configuration.yaml.example +++ b/config/configuration.yaml.example @@ -101,7 +101,8 @@ automation 2: time_seconds: 0 execute_service: notify.notify - service_data: {"message":"It's 4, time for beer!"} + service_data: + message: It's 4, time for beer! sensor: platform: systemmonitor diff --git a/homeassistant/components/automation/__init__.py b/homeassistant/components/automation/__init__.py index 33eef9fe3dc..6184d53ebf6 100644 --- a/homeassistant/components/automation/__init__.py +++ b/homeassistant/components/automation/__init__.py @@ -54,8 +54,7 @@ def _get_action(hass, config): if CONF_SERVICE in config: domain, service = split_entity_id(config[CONF_SERVICE]) - service_data = convert( - config.get(CONF_SERVICE_DATA), json.loads, {}) + service_data = config.get(CONF_SERVICE_DATA, {}) if not isinstance(service_data, dict): _LOGGER.error( From 8d7865160617054939cec5626e9b9fa82e6c9442 Mon Sep 17 00:00:00 2001 From: John Williams Date: Mon, 23 Mar 2015 20:18:07 +0000 Subject: [PATCH 3/6] Add python-version file to gitignore --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index a82763e1b6d..8c4eec4d180 100644 --- a/.gitignore +++ b/.gitignore @@ -62,4 +62,6 @@ nosetests.xml # Mr Developer .mr.developer.cfg .project -.pydevproject \ No newline at end of file +.pydevproject + +.python-version From fb6b514c340edf1708f5fd2013783456eba49361 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Correia?= Date: Wed, 15 Apr 2015 16:47:42 +0200 Subject: [PATCH 4/6] Adding simplistic support for Modbus sensor and switch (based on pymodbus) --- homeassistant/components/modbus.py | 99 +++++++++++++++++ homeassistant/components/sensor/modbus.py | 123 ++++++++++++++++++++++ homeassistant/components/switch/modbus.py | 109 +++++++++++++++++++ 3 files changed, 331 insertions(+) create mode 100644 homeassistant/components/modbus.py create mode 100644 homeassistant/components/sensor/modbus.py create mode 100644 homeassistant/components/switch/modbus.py diff --git a/homeassistant/components/modbus.py b/homeassistant/components/modbus.py new file mode 100644 index 00000000000..4cda6fcd20c --- /dev/null +++ b/homeassistant/components/modbus.py @@ -0,0 +1,99 @@ +__author__ = "Aurélien Correia" + +""" +components.modbus +~~~~~~~~~~~~~~~~~~~~~~~~~ +Modbus component, using pymodbus (python3 branch) + +typical declaration in configuration.yaml + +#Modbus TCP +modbus: + type: tcp + host: 127.0.0.1 + port: 2020 + +#Modbus RTU +modbus: + type: serial + method: rtu + port: /dev/ttyUSB0 + baudrate: 9600 + stopbits: 1 + bytesize: 8 + parity: N + +""" +import time +import logging + +from homeassistant.const import EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP +import homeassistant.loader as loader +from homeassistant.helpers import validate_config +import homeassistant.components as core + +# The domain of your component. Should be equal to the name of your component +DOMAIN = "modbus" + +# List of component names (string) your component depends upon +DEPENDENCIES = [] + +# Type of network +MEDIUM = "type" + +## if MEDIUM == "serial" +METHOD = "method" +SERIAL_PORT = "port" +BAUDRATE = "baudrate" +STOPBITS = "stopbits" +BYTESIZE = "bytesize" +PARITY = "parity" + +## if MEDIUM == "tcp" or "udp" +HOST = "host" +IP_PORT = "port" + +_LOGGER = logging.getLogger(__name__) + +NETWORK = None +TYPE = None + +def setup(hass, config): + """ Setup Modbus component. """ + + global TYPE + TYPE = config[DOMAIN][MEDIUM] + + # Connect to Modbus network + 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]) + elif TYPE == "tcp": + from pymodbus.client.sync import ModbusTcpClient as ModbusClient + NETWORK = 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]) + else: + return False + + def stop_modbus(event): + NETWORK.close() + + def start_modbus(event): + NETWORK.connect() + hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_modbus) + + hass.bus.listen_once(EVENT_HOMEASSISTANT_START, start_modbus) + + # Tells the bootstrapper that the component was succesfully initialized + return True diff --git a/homeassistant/components/sensor/modbus.py b/homeassistant/components/sensor/modbus.py new file mode 100644 index 00000000000..e186ee01fef --- /dev/null +++ b/homeassistant/components/sensor/modbus.py @@ -0,0 +1,123 @@ +__author__ = "Aurélien Correia" + +""" +Support for Modbus sensors. + +Configuration: +To use the Modbus sensors you will need to add something like the following to +your config/configuration.yaml + +sensor: + platform: modbus + slave: 1 + registers: + 16: + name: My integer sensor + unit: C + 24: + bits: + 0: + name: My boolean sensor + 2: + name: My other boolean sensor + +VARIABLES: + + - "slave" = slave number (ignored and can be omitted if not serial Modbus) + - "unit" = unit to attach to value (optional, ignored for boolean sensors) + - "registers" contains a list of relevant registers to read from + it can contain a "bits" section, listing relevant bits + + - each named register will create an integer sensor + - each named bit will create a boolean sensor +""" + +import logging + +import homeassistant.components.modbus as modbus +from homeassistant.helpers.entity import Entity +from homeassistant.const import ( + TEMP_CELCIUS, TEMP_FAHRENHEIT, + STATE_ON, STATE_OFF) + +_LOGGER = logging.getLogger(__name__) + +def setup_platform(hass, config, add_devices, discovery_info=None): + """ Read config and create Modbus devices """ + sensors = [] + slave = config.get("slave", None) + if modbus.TYPE == "serial" and not slave: + _LOGGER.error("No slave number provided for serial Modbus") + return False + registers = config.get("registers") + for regnum, register in registers.items(): + if register.get("name"): + sensors.append(ModbusSensor(register.get("name"), slave, regnum, None, register.get("unit"))) + if register.get("bits"): + bits = register.get("bits") + for bitnum, bit in bits.items(): + if bit.get("name"): + sensors.append(ModbusSensor(bit.get("name"), slave, regnum, bitnum)) + add_devices(sensors) + +class ModbusSensor(Entity): + """ Represents a Modbus Sensor """ + + def __init__(self, name, slave, register, bit=None, unit=None): + self._name = name + self.slave = int(slave) if slave else 1 + self.register = int(register) + self.bit = int(bit) if bit else None + self._value = None + self._unit = unit + + def __str__(self): + return "%s: %s" % (self.name, self.state) + + @property + def should_poll(self): + """ We should poll, because slaves are not allowed to initiate communication on Modbus networks""" + return True + + @property + def unique_id(self): + """ Returns a unique id. """ + return "MODBUS-SENSOR-{}-{}-{}".format(self.slave, self.register, self.bit) + + @property + def state(self): + """ Returns the state of the sensor. """ + if self.bit: + return STATE_ON if self._value else STATE_OFF + else: + return self._value + + @property + def name(self): + """ Get the name of the sensor. """ + return self._name + + @property + def unit_of_measurement(self): + """ Unit of measurement of this entity, if any. """ + if self._unit == "C": + return TEMP_CELCIUS + elif self._unit == "F": + return TEMP_FAHRENHEIT + else: + return self._unit + + @property + def state_attributes(self): + attr = super().state_attributes + return attr + + def update(self): + result = modbus.NETWORK.read_holding_registers(unit=self.slave,address=self.register,count=1) + val = 0 + for i, e in enumerate(result.registers): + val += e * (2**(i*16)) + if self.bit: + self._value = val & (0x0001 << self.bit) + else: + self._value = val diff --git a/homeassistant/components/switch/modbus.py b/homeassistant/components/switch/modbus.py new file mode 100644 index 00000000000..6f26d7997f3 --- /dev/null +++ b/homeassistant/components/switch/modbus.py @@ -0,0 +1,109 @@ +__author__ = "Aurélien Correia" + +""" +Support for Modbus switches. + +Configuration: +To use the Modbus switches you will need to add something like the following to +your config/configuration.yaml + +sensor: + platform: modbus + slave: 1 + registers: + 24: + bits: + 0: + name: My switch + 2: + name: My other switch + +VARIABLES: + + - "slave" = slave number (ignored and can be omitted if not serial Modbus) + - "registers" contains a list of relevant registers to read from + - it must contain a "bits" section, listing relevant bits + + - each named bit will create a switch +""" + +import logging + +import homeassistant.components.modbus as modbus +from homeassistant.helpers.entity import ToggleEntity + +_LOGGER = logging.getLogger(__name__) + +def setup_platform(hass, config, add_devices, discovery_info=None): + """ Read config and create Modbus devices """ + switches = [] + slave = config.get("slave", None) + if modbus.TYPE == "serial" and not slave: + _LOGGER.error("No slave number provided for serial Modbus") + return False + registers = config.get("registers") + for regnum, register in registers.items(): + bits = register.get("bits") + for bitnum, bit in bits.items(): + if bit.get("name"): + switches.append(ModbusSwitch(bit.get("name"), slave, regnum, bitnum)) + add_devices(switches) + +class ModbusSwitch(ToggleEntity): + """ Represents a Modbus Switch """ + + def __init__(self, name, slave, register, bit): + self._name = name + self.slave = int(slave) if slave else 1 + self.register = int(register) + self.bit = int(bit) + self._is_on = None + self.register_value = None + + def __str__(self): + return "%s: %s" % (self.name, self.state) + + @property + def should_poll(self): + """ We should poll, because slaves are not allowed to initiate communication on Modbus networks""" + return True + + @property + def unique_id(self): + """ Returns a unique id. """ + return "MODBUS-SWITCH-{}-{}-{}".format(self.slave, self.register, self.bit) + + @property + def is_on(self): + """ Returns True if switch is on. """ + return self._is_on + + @property + def name(self): + """ Get the name of the switch. """ + return self._name + + @property + def state_attributes(self): + attr = super().state_attributes + return attr + + def turn_on(self, **kwargs): + if self.register_value is None: + self.update() + val = self.register_value | (0x0001 << self.bit) + modbus.NETWORK.write_register(unit=self.slave,address=self.register,value=val) + + def turn_off(self, **kwargs): + if self.register_value is None: + self.update() + val = self.register_value & ~(0x0001 << self.bit) + modbus.NETWORK.write_register(unit=self.slave,address=self.register,value=val) + + def update(self): + result = modbus.NETWORK.read_holding_registers(unit=self.slave,address=self.register,count=1) + val = 0 + for i, e in enumerate(result.registers): + val += e * (2**(i*16)) + self.register_value = val + self._is_on = (val & (0x0001 << self.bit) > 0) From be9a1d6b27572fae6f973c7f49c636aedc44b77d Mon Sep 17 00:00:00 2001 From: wind-rider Date: Tue, 21 Apr 2015 00:12:39 +0200 Subject: [PATCH 5/6] Update CONTRIBUTING.md Updated icon list url --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0f1c2c658bb..973415c1866 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,7 +10,7 @@ After you finish adding support for your device: If you've added a component: - - update the file [`domain-icon.html`](https://github.com/balloob/home-assistant/blob/master/homeassistant/components/http/www_static/polymer/domain-icon.html) with an icon for your domain ([pick from this list](https://www.polymer-project.org/components/core-icons/demo.html)) + - update the file [`domain-icon.html`](https://github.com/balloob/home-assistant/blob/master/homeassistant/components/http/www_static/polymer/domain-icon.html) with an icon for your domain ([pick from this list](https://www.polymer-project.org/0.5/components/core-elements/demo.html#core-icon)) - update the demo component with two states that it provides - Add your component to home-assistant.conf.example From 4d6555441d707f4b2f6440cdb86ce16992fb724b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Correia?= Date: Tue, 21 Apr 2015 16:40:13 +0200 Subject: [PATCH 6/6] Passed Travis CI --- homeassistant/components/modbus.py | 39 ++++++++++++----------- homeassistant/components/sensor/modbus.py | 31 ++++++++++++------ homeassistant/components/switch/modbus.py | 32 +++++++++++++------ 3 files changed, 64 insertions(+), 38 deletions(-) diff --git a/homeassistant/components/modbus.py b/homeassistant/components/modbus.py index 4cda6fcd20c..97b0ec7405a 100644 --- a/homeassistant/components/modbus.py +++ b/homeassistant/components/modbus.py @@ -1,5 +1,3 @@ -__author__ = "Aurélien Correia" - """ components.modbus ~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -24,13 +22,10 @@ modbus: parity: N """ -import time import logging -from homeassistant.const import EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP -import homeassistant.loader as loader -from homeassistant.helpers import validate_config -import homeassistant.components as core +from homeassistant.const import (EVENT_HOMEASSISTANT_START, + EVENT_HOMEASSISTANT_STOP) # The domain of your component. Should be equal to the name of your component DOMAIN = "modbus" @@ -41,7 +36,7 @@ DEPENDENCIES = [] # Type of network MEDIUM = "type" -## if MEDIUM == "serial" +# if MEDIUM == "serial" METHOD = "method" SERIAL_PORT = "port" BAUDRATE = "baudrate" @@ -49,7 +44,7 @@ STOPBITS = "stopbits" BYTESIZE = "bytesize" PARITY = "parity" -## if MEDIUM == "tcp" or "udp" +# if MEDIUM == "tcp" or "udp" HOST = "host" IP_PORT = "port" @@ -58,38 +53,44 @@ _LOGGER = logging.getLogger(__name__) NETWORK = None TYPE = None + def setup(hass, config): """ Setup Modbus component. """ + # Modbus connection type + # pylint: disable=global-statement, import-error global TYPE TYPE = config[DOMAIN][MEDIUM] # 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]) + 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]) elif TYPE == "tcp": from pymodbus.client.sync import ModbusTcpClient as ModbusClient - NETWORK = ModbusClient( host=config[DOMAIN][HOST], - port=config[DOMAIN][IP_PORT]) + NETWORK = 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]) + NETWORK = ModbusClient(host=config[DOMAIN][HOST], + port=config[DOMAIN][IP_PORT]) else: return False def stop_modbus(event): + """ Stop Modbus service""" NETWORK.close() def start_modbus(event): + """ Start Modbus service""" NETWORK.connect() hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_modbus) diff --git a/homeassistant/components/sensor/modbus.py b/homeassistant/components/sensor/modbus.py index e186ee01fef..90593875a54 100644 --- a/homeassistant/components/sensor/modbus.py +++ b/homeassistant/components/sensor/modbus.py @@ -1,5 +1,3 @@ -__author__ = "Aurélien Correia" - """ Support for Modbus sensors. @@ -42,6 +40,7 @@ from homeassistant.const import ( _LOGGER = logging.getLogger(__name__) + def setup_platform(hass, config, add_devices, discovery_info=None): """ Read config and create Modbus devices """ sensors = [] @@ -52,15 +51,24 @@ def setup_platform(hass, config, add_devices, discovery_info=None): registers = config.get("registers") for regnum, register in registers.items(): if register.get("name"): - sensors.append(ModbusSensor(register.get("name"), slave, regnum, None, register.get("unit"))) + sensors.append(ModbusSensor(register.get("name"), + slave, + regnum, + None, + register.get("unit"))) if register.get("bits"): bits = register.get("bits") for bitnum, bit in bits.items(): if bit.get("name"): - sensors.append(ModbusSensor(bit.get("name"), slave, regnum, bitnum)) + sensors.append(ModbusSensor(bit.get("name"), + slave, + regnum, + bitnum)) add_devices(sensors) + class ModbusSensor(Entity): + # pylint: disable=too-many-arguments """ Represents a Modbus Sensor """ def __init__(self, name, slave, register, bit=None, unit=None): @@ -76,13 +84,16 @@ class ModbusSensor(Entity): @property def should_poll(self): - """ We should poll, because slaves are not allowed to initiate communication on Modbus networks""" + """ We should poll, because slaves are not allowed to + initiate communication on Modbus networks""" return True @property def unique_id(self): """ Returns a unique id. """ - return "MODBUS-SENSOR-{}-{}-{}".format(self.slave, self.register, self.bit) + return "MODBUS-SENSOR-{}-{}-{}".format(self.slave, + self.register, + self.bit) @property def state(self): @@ -113,10 +124,12 @@ class ModbusSensor(Entity): return attr def update(self): - result = modbus.NETWORK.read_holding_registers(unit=self.slave,address=self.register,count=1) + result = modbus.NETWORK.read_holding_registers(unit=self.slave, + address=self.register, + count=1) val = 0 - for i, e in enumerate(result.registers): - val += e * (2**(i*16)) + for i, res in enumerate(result.registers): + val += res * (2**(i*16)) if self.bit: self._value = val & (0x0001 << self.bit) else: diff --git a/homeassistant/components/switch/modbus.py b/homeassistant/components/switch/modbus.py index 6f26d7997f3..7e5e039337f 100644 --- a/homeassistant/components/switch/modbus.py +++ b/homeassistant/components/switch/modbus.py @@ -1,5 +1,3 @@ -__author__ = "Aurélien Correia" - """ Support for Modbus switches. @@ -34,6 +32,7 @@ from homeassistant.helpers.entity import ToggleEntity _LOGGER = logging.getLogger(__name__) + def setup_platform(hass, config, add_devices, discovery_info=None): """ Read config and create Modbus devices """ switches = [] @@ -46,9 +45,13 @@ def setup_platform(hass, config, add_devices, discovery_info=None): bits = register.get("bits") for bitnum, bit in bits.items(): if bit.get("name"): - switches.append(ModbusSwitch(bit.get("name"), slave, regnum, bitnum)) + switches.append(ModbusSwitch(bit.get("name"), + slave, + regnum, + bitnum)) add_devices(switches) + class ModbusSwitch(ToggleEntity): """ Represents a Modbus Switch """ @@ -65,13 +68,16 @@ class ModbusSwitch(ToggleEntity): @property def should_poll(self): - """ We should poll, because slaves are not allowed to initiate communication on Modbus networks""" + """ We should poll, because slaves are not allowed to + initiate communication on Modbus networks""" return True @property def unique_id(self): """ Returns a unique id. """ - return "MODBUS-SWITCH-{}-{}-{}".format(self.slave, self.register, self.bit) + return "MODBUS-SWITCH-{}-{}-{}".format(self.slave, + self.register, + self.bit) @property def is_on(self): @@ -92,18 +98,24 @@ class ModbusSwitch(ToggleEntity): if self.register_value is None: self.update() val = self.register_value | (0x0001 << self.bit) - modbus.NETWORK.write_register(unit=self.slave,address=self.register,value=val) + modbus.NETWORK.write_register(unit=self.slave, + address=self.register, + value=val) def turn_off(self, **kwargs): if self.register_value is None: self.update() val = self.register_value & ~(0x0001 << self.bit) - modbus.NETWORK.write_register(unit=self.slave,address=self.register,value=val) + modbus.NETWORK.write_register(unit=self.slave, + address=self.register, + value=val) def update(self): - result = modbus.NETWORK.read_holding_registers(unit=self.slave,address=self.register,count=1) + result = modbus.NETWORK.read_holding_registers(unit=self.slave, + address=self.register, + count=1) val = 0 - for i, e in enumerate(result.registers): - val += e * (2**(i*16)) + for i, res in enumerate(result.registers): + val += res * (2**(i*16)) self.register_value = val self._is_on = (val & (0x0001 << self.bit) > 0)