From 41bbcf060cde6eff0220f2360401c62ac28cb72e Mon Sep 17 00:00:00 2001 From: Stefan Lehmann Date: Sun, 8 Oct 2017 16:03:16 +0200 Subject: [PATCH] ads component with platforms switch, binary_sensor, light, sensor add locking poll sensors at startup update state of ads switch and light update ads requirements remove update() from constructors on ads platforms omit ads coverage ads catch read error when polling --- .coveragerc | 3 + homeassistant/components/ads.py | 77 +++++++++++-------- homeassistant/components/binary_sensor/ads.py | 49 +++++++++--- homeassistant/components/light/ads.py | 25 ++++-- homeassistant/components/sensor/ads.py | 66 +++++++++++++--- homeassistant/components/switch/ads.py | 15 +++- requirements_all.txt | 3 + 7 files changed, 177 insertions(+), 61 deletions(-) diff --git a/.coveragerc b/.coveragerc index 58bdac1ff31..d0a29acc897 100644 --- a/.coveragerc +++ b/.coveragerc @@ -11,6 +11,9 @@ omit = homeassistant/components/abode.py homeassistant/components/*/abode.py + homeassistant/components/ads.py + homeassistant/components/*/ads.py + homeassistant/components/alarmdecoder.py homeassistant/components/*/alarmdecoder.py diff --git a/homeassistant/components/ads.py b/homeassistant/components/ads.py index 16cd16bc181..4866e0ee53b 100644 --- a/homeassistant/components/ads.py +++ b/homeassistant/components/ads.py @@ -4,8 +4,8 @@ ADS Component. For more details about this component, please refer to the documentation. """ +import threading import struct -import time import logging import ctypes from collections import namedtuple @@ -13,10 +13,8 @@ import voluptuous as vol from homeassistant.const import CONF_DEVICE, CONF_PORT, CONF_IP_ADDRESS, \ EVENT_HOMEASSISTANT_STOP import homeassistant.helpers.config_validation as cv -import pyads -from pyads import PLCTYPE_BOOL, PLCTYPE_INT, PLCTYPE_UINT, PLCTYPE_BYTE -REQUIREMENTS = ['pyads'] +REQUIREMENTS = ['pyads==2.2.6'] _LOGGER = logging.getLogger(__name__) @@ -28,17 +26,17 @@ ADSTYPE_UINT = 'uint' ADSTYPE_BYTE = 'byte' ADSTYPE_BOOL = 'bool' -ADS_TYPEMAP = { - ADSTYPE_BOOL: PLCTYPE_BOOL, - ADSTYPE_BYTE: PLCTYPE_BYTE, - ADSTYPE_INT: PLCTYPE_INT, - ADSTYPE_UINT: PLCTYPE_UINT, -} - ADS_PLATFORMS = ['switch', 'binary_sensor', 'light'] DOMAIN = 'ads' +# config variable names +CONF_ADSVAR = 'adsvar' +CONF_ADSTYPE = 'adstype' +CONF_ADS_USE_NOTIFY = 'use_notify' +CONF_ADS_POLL_INTERVAL = 'poll_interval' +CONF_ADS_FACTOR = 'factor' + CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ vol.Required(CONF_DEVICE): cv.string, @@ -47,8 +45,12 @@ CONFIG_SCHEMA = vol.Schema({ }) }, extra=vol.ALLOW_EXTRA) +MAX_RETRIES = 5 +RETRY_SLEEPTIME_S = 0.1 + def setup(hass, config): + import pyads """ Set up the ADS component. """ _LOGGER.info('created ADS client') conf = config[DOMAIN] @@ -81,12 +83,29 @@ class AdsHub: """ Representation of a PyADS connection. """ def __init__(self, ads_client): + from pyads import PLCTYPE_BOOL, PLCTYPE_BYTE, PLCTYPE_INT, \ + PLCTYPE_UINT, ADSError + + self.ADS_TYPEMAP = { + ADSTYPE_BOOL: PLCTYPE_BOOL, + ADSTYPE_BYTE: PLCTYPE_BYTE, + ADSTYPE_INT: PLCTYPE_INT, + ADSTYPE_UINT: PLCTYPE_UINT, + } + + self.PLCTYPE_BOOL = PLCTYPE_BOOL + self.PLCTYPE_BYTE = PLCTYPE_BYTE + self.PLCTYPE_INT = PLCTYPE_INT + self.PLCTYPE_UINT = PLCTYPE_UINT + self.ADSError = ADSError + self._client = ads_client self._client.open() # all ADS devices are registered here self._devices = [] self._notification_items = {} + self._lock = threading.Lock() def shutdown(self, *args, **kwargs): _LOGGER.debug('Shutting down ADS') @@ -105,28 +124,23 @@ class AdsHub: self._devices.append(device) def write_by_name(self, name, value, plc_datatype): - return self._client.write_by_name(name, value, plc_datatype) + with self._lock: + return self._client.write_by_name(name, value, plc_datatype) def read_by_name(self, name, plc_datatype): - return self._client.read_by_name(name, plc_datatype) + with self._lock: + return self._client.read_by_name(name, plc_datatype) def add_device_notification(self, name, plc_datatype, callback): + from pyads import NotificationAttrib """ Add a notification to the ADS devices. """ - attr = pyads.NotificationAttrib(ctypes.sizeof(plc_datatype)) + attr = NotificationAttrib(ctypes.sizeof(plc_datatype)) - for i in range(5): - try: - hnotify, huser = self._client.add_device_notification( - name, attr, self._device_notification_callback - ) - hnotify = int(hnotify) - break - except pyads.pyads.ADSError: - _LOGGER.debug('Could not add notification for "{0}". Retrying...' - .format(name)) - time.sleep(0.1) - else: - return False + with self._lock: + hnotify, huser = self._client.add_device_notification( + name, attr, self._device_notification_callback + ) + hnotify = int(hnotify) _LOGGER.debug('Added Device Notification {0} for variable {1}' .format(hnotify, name)) @@ -136,6 +150,7 @@ class AdsHub: ) def _device_notification_callback(self, addr, notification, huser): + from pyads import PLCTYPE_BOOL, PLCTYPE_INT, PLCTYPE_BYTE, PLCTYPE_UINT contents = notification.contents hnotify = int(contents.hNotification) @@ -150,13 +165,13 @@ class AdsHub: return # parse data to desired datatype - if notification_item.plc_datatype == pyads.PLCTYPE_BOOL: + if notification_item.plc_datatype == PLCTYPE_BOOL: value = bool(struct.unpack('