diff --git a/homeassistant/components/binary_sensor/tcp.py b/homeassistant/components/binary_sensor/tcp.py index 4e6e75e3555..056a59fbdf1 100644 --- a/homeassistant/components/binary_sensor/tcp.py +++ b/homeassistant/components/binary_sensor/tcp.py @@ -6,10 +6,11 @@ Provides a binary_sensor which gets its values from a TCP socket. import logging from homeassistant.components.binary_sensor import BinarySensorDevice -from homeassistant.components import tcp +from homeassistant.components.tcp import DOMAIN, CONF_VALUE_ON +from homeassistant.components.sensor.tcp import Sensor -DEPENDENCIES = [tcp.DOMAIN] +DEPENDENCIES = [DOMAIN] _LOGGER = logging.getLogger(__name__) @@ -21,10 +22,10 @@ def setup_platform(hass, config, add_entities, discovery_info=None): add_entities((BinarySensor(config),)) -class BinarySensor(tcp.TCPEntity, BinarySensorDevice): +class BinarySensor(Sensor, BinarySensorDevice): """ A binary sensor which is on when its state == CONF_VALUE_ON. """ - required = (tcp.CONF_VALUE_ON,) + required = (CONF_VALUE_ON,) @property def is_on(self): - return self._state == self._config[tcp.CONF_VALUE_ON] + return self._state == self._config[CONF_VALUE_ON] diff --git a/homeassistant/components/sensor/tcp.py b/homeassistant/components/sensor/tcp.py index 53e91c6c728..b45f35434af 100644 --- a/homeassistant/components/sensor/tcp.py +++ b/homeassistant/components/sensor/tcp.py @@ -4,17 +4,117 @@ homeassistant.components.sensor.tcp Provides a sensor which gets its values from a TCP socket. """ import logging +import socket +import re +from select import select -from homeassistant.components import tcp +from homeassistant.const import CONF_NAME, CONF_HOST +from homeassistant.helpers.entity import Entity +from homeassistant.components.tcp import ( + DOMAIN, CONF_PORT, CONF_TIMEOUT, CONF_PAYLOAD, CONF_UNIT, CONF_VALUE_REGEX, + CONF_VALUE_ON, CONF_BUFFER_SIZE, DEFAULT_TIMEOUT, DEFAULT_BUFFER_SIZE +) -DEPENDENCIES = [tcp.DOMAIN] +DEPENDENCIES = [DOMAIN] _LOGGER = logging.getLogger(__name__) def setup_platform(hass, config, add_entities, discovery_info=None): """ Create the Sensor. """ - if not tcp.TCPEntity.validate_config(config): + if not Sensor.validate_config(config): return False - add_entities((tcp.TCPEntity(config),)) + add_entities((Sensor(config),)) + + +class Sensor(Entity): + """ Sensor Entity which gets its value from a TCP socket. """ + required = tuple() + + def __init__(self, config): + """ Set all the config values if they exist and get initial state. """ + self._config = { + CONF_NAME: config.get(CONF_NAME), + CONF_HOST: config[CONF_HOST], + CONF_PORT: config[CONF_PORT], + CONF_TIMEOUT: config.get(CONF_TIMEOUT, DEFAULT_TIMEOUT), + CONF_PAYLOAD: config[CONF_PAYLOAD], + CONF_UNIT: config.get(CONF_UNIT), + CONF_VALUE_REGEX: config.get(CONF_VALUE_REGEX), + CONF_VALUE_ON: config.get(CONF_VALUE_ON), + CONF_BUFFER_SIZE: config.get( + CONF_BUFFER_SIZE, DEFAULT_BUFFER_SIZE), + } + self._state = None + self.update() + + @classmethod + def validate_config(cls, config): + """ Ensure the config has all of the necessary values. """ + always_required = (CONF_HOST, CONF_PORT, CONF_PAYLOAD) + for key in always_required + tuple(cls.required): + if key not in config: + _LOGGER.error( + "You must provide %r to create any TCP entity.", key) + return False + return True + + @property + def name(self): + name = self._config[CONF_NAME] + if name is not None: + return name + return super(Sensor, self).name + + @property + def state(self): + return self._state + + @property + def unit_of_measurement(self): + return self._config[CONF_UNIT] + + def update(self): + """ Get the latest value for this sensor. """ + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + try: + sock.connect((self._config[CONF_HOST], self._config[CONF_PORT])) + except socket.error as err: + _LOGGER.error( + "Unable to connect to %s on port %s: %s", + self._config[CONF_HOST], self._config[CONF_PORT], err) + return + try: + sock.send(self._config[CONF_PAYLOAD].encode()) + except socket.error as err: + _LOGGER.error( + "Unable to send payload %r to %s on port %s: %s", + self._config[CONF_PAYLOAD], self._config[CONF_HOST], + self._config[CONF_PORT], err) + return + readable, _, _ = select([sock], [], [], self._config[CONF_TIMEOUT]) + if not readable: + _LOGGER.warning( + "Timeout (%s second(s)) waiting for a response after sending " + "%r to %s on port %s.", + self._config[CONF_TIMEOUT], self._config[CONF_PAYLOAD], + self._config[CONF_HOST], self._config[CONF_PORT]) + return + value = sock.recv(self._config[CONF_BUFFER_SIZE]).decode() + if self._config[CONF_VALUE_REGEX] is not None: + match = re.match(self._config[CONF_VALUE_REGEX], value) + if match is None: + _LOGGER.warning( + "Unable to match value using value_regex of %r: %r", + self._config[CONF_VALUE_REGEX], value) + return + try: + self._state = match.groups()[0] + except IndexError: + _LOGGER.error( + "You must include a capture group in the regex for %r: %r", + self.name, self._config[CONF_VALUE_REGEX]) + return + return + self._state = value diff --git a/homeassistant/components/tcp.py b/homeassistant/components/tcp.py index 89c39e9f0db..9b09ceaf68e 100644 --- a/homeassistant/components/tcp.py +++ b/homeassistant/components/tcp.py @@ -3,15 +3,6 @@ homeassistant.components.tcp ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A generic TCP socket component. """ -import logging -import socket -import re -from select import select - -from homeassistant.const import CONF_NAME, CONF_HOST -from homeassistant.helpers.entity import Entity - - DOMAIN = "tcp" CONF_PORT = "port" @@ -25,101 +16,7 @@ CONF_BUFFER_SIZE = "buffer_size" DEFAULT_TIMEOUT = 10 DEFAULT_BUFFER_SIZE = 1024 -_LOGGER = logging.getLogger(__name__) - def setup(hass, config): """ Nothing to do! """ return True - - -class TCPEntity(Entity): - """ Generic Entity which gets its value from a TCP socket. """ - required = tuple() - - def __init__(self, config): - """ Set all the config values if they exist and get initial state. """ - self._config = { - CONF_NAME: config.get(CONF_NAME), - CONF_HOST: config[CONF_HOST], - CONF_PORT: config[CONF_PORT], - CONF_TIMEOUT: config.get(CONF_TIMEOUT, DEFAULT_TIMEOUT), - CONF_PAYLOAD: config[CONF_PAYLOAD], - CONF_UNIT: config.get(CONF_UNIT), - CONF_VALUE_REGEX: config.get(CONF_VALUE_REGEX), - CONF_VALUE_ON: config.get(CONF_VALUE_ON), - CONF_BUFFER_SIZE: config.get( - CONF_BUFFER_SIZE, DEFAULT_BUFFER_SIZE), - } - self._state = None - self.update() - - @classmethod - def validate_config(cls, config): - """ Ensure the config has all of the necessary values. """ - always_required = (CONF_HOST, CONF_PORT, CONF_PAYLOAD) - for key in always_required + tuple(cls.required): - if key not in config: - _LOGGER.error( - "You must provide %r to create any TCP entity.", key) - return False - return True - - @property - def name(self): - name = self._config[CONF_NAME] - if name is not None: - return name - return super(TCPEntity, self).name - - @property - def state(self): - return self._state - - @property - def unit_of_measurement(self): - return self._config[CONF_UNIT] - - def update(self): - """ Get the latest value for this sensor. """ - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - try: - sock.connect((self._config[CONF_HOST], self._config[CONF_PORT])) - except socket.error as err: - _LOGGER.error( - "Unable to connect to %s on port %s: %s", - self._config[CONF_HOST], self._config[CONF_PORT], err) - return - try: - sock.send(self._config[CONF_PAYLOAD].encode()) - except socket.error as err: - _LOGGER.error( - "Unable to send payload %r to %s on port %s: %s", - self._config[CONF_PAYLOAD], self._config[CONF_HOST], - self._config[CONF_PORT], err) - return - readable, _, _ = select([sock], [], [], self._config[CONF_TIMEOUT]) - if not readable: - _LOGGER.warning( - "Timeout (%s second(s)) waiting for a response after sending " - "%r to %s on port %s.", - self._config[CONF_TIMEOUT], self._config[CONF_PAYLOAD], - self._config[CONF_HOST], self._config[CONF_PORT]) - return - value = sock.recv(self._config[CONF_BUFFER_SIZE]).decode() - if self._config[CONF_VALUE_REGEX] is not None: - match = re.match(self._config[CONF_VALUE_REGEX], value) - if match is None: - _LOGGER.warning( - "Unable to match value using value_regex of %r: %r", - self._config[CONF_VALUE_REGEX], value) - return - try: - self._state = match.groups()[0] - except IndexError: - _LOGGER.error( - "You must include a capture group in the regex for %r: %r", - self.name, self._config[CONF_VALUE_REGEX]) - return - return - self._state = value