forked from home-assistant/core
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'dev' of https://github.com/home-assistant/home-assistant …
…into dev * 'dev' of https://github.com/home-assistant/home-assistant: Meraki AP Device tracker (home-assistant#10971) Update frontend to 20171206.0 Allow disabling the LEDs on TP-Link smart plugs (home-assistant#10980) Revert pychromecast update (home-assistant#10989) Reload closest store on api menu request (home-assistant#10962) Allow chime to work for wink siren/chime (home-assistant#10961) Upgrade tellduslive library version (closes home-assistant#10922) (home-assistant#10950) Fix linksys_ap.py by inheriting DeviceScanner (home-assistant#10947) Require FF43 for latest js (home-assistant#10941) Version bump to 0.59.2 Require FF43 for latest js (home-assistant#10941) Revert pychromecast update (home-assistant#10989) Allow chime to work for wink siren/chime (home-assistant#10961) Add option to set default hide if away for new devices (home-assistant#10762) Use new build path for dev translations (home-assistant#10937) Generic thermostat initial_operation_mode (home-assistant#10690) Add Ziggo Mediabox XL media_player (home-assistant#10514) Gearbest sensor (home-assistant#10556) Reload closest store on api menu request (home-assistant#10962) Add ADS component (home-assistant#10142)
- Loading branch information
Showing
26 changed files
with
1,341 additions
and
48 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,217 @@ | ||
""" | ||
ADS Component. | ||
For more details about this component, please refer to the documentation. | ||
https://home-assistant.io/components/ads/ | ||
""" | ||
import os | ||
import threading | ||
import struct | ||
import logging | ||
import ctypes | ||
from collections import namedtuple | ||
import voluptuous as vol | ||
from homeassistant.const import CONF_DEVICE, CONF_PORT, CONF_IP_ADDRESS, \ | ||
EVENT_HOMEASSISTANT_STOP | ||
from homeassistant.config import load_yaml_config_file | ||
import homeassistant.helpers.config_validation as cv | ||
|
||
REQUIREMENTS = ['pyads==2.2.6'] | ||
|
||
_LOGGER = logging.getLogger(__name__) | ||
|
||
DATA_ADS = 'data_ads' | ||
|
||
# Supported Types | ||
ADSTYPE_INT = 'int' | ||
ADSTYPE_UINT = 'uint' | ||
ADSTYPE_BYTE = 'byte' | ||
ADSTYPE_BOOL = 'bool' | ||
|
||
DOMAIN = 'ads' | ||
|
||
# config variable names | ||
CONF_ADS_VAR = 'adsvar' | ||
CONF_ADS_VAR_BRIGHTNESS = 'adsvar_brightness' | ||
CONF_ADS_TYPE = 'adstype' | ||
CONF_ADS_FACTOR = 'factor' | ||
CONF_ADS_VALUE = 'value' | ||
|
||
SERVICE_WRITE_DATA_BY_NAME = 'write_data_by_name' | ||
|
||
CONFIG_SCHEMA = vol.Schema({ | ||
DOMAIN: vol.Schema({ | ||
vol.Required(CONF_DEVICE): cv.string, | ||
vol.Required(CONF_PORT): cv.port, | ||
vol.Optional(CONF_IP_ADDRESS): cv.string, | ||
}) | ||
}, extra=vol.ALLOW_EXTRA) | ||
|
||
SCHEMA_SERVICE_WRITE_DATA_BY_NAME = vol.Schema({ | ||
vol.Required(CONF_ADS_VAR): cv.string, | ||
vol.Required(CONF_ADS_TYPE): vol.In([ADSTYPE_INT, ADSTYPE_UINT, | ||
ADSTYPE_BYTE]), | ||
vol.Required(CONF_ADS_VALUE): cv.match_all | ||
}) | ||
|
||
|
||
def setup(hass, config): | ||
"""Set up the ADS component.""" | ||
import pyads | ||
conf = config[DOMAIN] | ||
|
||
# get ads connection parameters from config | ||
net_id = conf.get(CONF_DEVICE) | ||
ip_address = conf.get(CONF_IP_ADDRESS) | ||
port = conf.get(CONF_PORT) | ||
|
||
# create a new ads connection | ||
client = pyads.Connection(net_id, port, ip_address) | ||
|
||
# add some constants to AdsHub | ||
AdsHub.ADS_TYPEMAP = { | ||
ADSTYPE_BOOL: pyads.PLCTYPE_BOOL, | ||
ADSTYPE_BYTE: pyads.PLCTYPE_BYTE, | ||
ADSTYPE_INT: pyads.PLCTYPE_INT, | ||
ADSTYPE_UINT: pyads.PLCTYPE_UINT, | ||
} | ||
|
||
AdsHub.PLCTYPE_BOOL = pyads.PLCTYPE_BOOL | ||
AdsHub.PLCTYPE_BYTE = pyads.PLCTYPE_BYTE | ||
AdsHub.PLCTYPE_INT = pyads.PLCTYPE_INT | ||
AdsHub.PLCTYPE_UINT = pyads.PLCTYPE_UINT | ||
AdsHub.ADSError = pyads.ADSError | ||
|
||
# connect to ads client and try to connect | ||
try: | ||
ads = AdsHub(client) | ||
except pyads.pyads.ADSError: | ||
_LOGGER.error( | ||
'Could not connect to ADS host (netid=%s, port=%s)', net_id, port | ||
) | ||
return False | ||
|
||
# add ads hub to hass data collection, listen to shutdown | ||
hass.data[DATA_ADS] = ads | ||
hass.bus.listen(EVENT_HOMEASSISTANT_STOP, ads.shutdown) | ||
|
||
def handle_write_data_by_name(call): | ||
"""Write a value to the connected ADS device.""" | ||
ads_var = call.data.get(CONF_ADS_VAR) | ||
ads_type = call.data.get(CONF_ADS_TYPE) | ||
value = call.data.get(CONF_ADS_VALUE) | ||
|
||
try: | ||
ads.write_by_name(ads_var, value, ads.ADS_TYPEMAP[ads_type]) | ||
except pyads.ADSError as err: | ||
_LOGGER.error(err) | ||
|
||
# load descriptions from services.yaml | ||
descriptions = load_yaml_config_file( | ||
os.path.join(os.path.dirname(__file__), 'services.yaml')) | ||
|
||
hass.services.register( | ||
DOMAIN, SERVICE_WRITE_DATA_BY_NAME, handle_write_data_by_name, | ||
descriptions[SERVICE_WRITE_DATA_BY_NAME], | ||
schema=SCHEMA_SERVICE_WRITE_DATA_BY_NAME | ||
) | ||
|
||
return True | ||
|
||
|
||
# tuple to hold data needed for notification | ||
NotificationItem = namedtuple( | ||
'NotificationItem', 'hnotify huser name plc_datatype callback' | ||
) | ||
|
||
|
||
class AdsHub: | ||
"""Representation of a PyADS connection.""" | ||
|
||
def __init__(self, ads_client): | ||
"""Initialize the ADS Hub.""" | ||
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): | ||
"""Shutdown ADS connection.""" | ||
_LOGGER.debug('Shutting down ADS') | ||
for notification_item in self._notification_items.values(): | ||
self._client.del_device_notification( | ||
notification_item.hnotify, | ||
notification_item.huser | ||
) | ||
_LOGGER.debug( | ||
'Deleting device notification %d, %d', | ||
notification_item.hnotify, notification_item.huser | ||
) | ||
self._client.close() | ||
|
||
def register_device(self, device): | ||
"""Register a new device.""" | ||
self._devices.append(device) | ||
|
||
def write_by_name(self, name, value, plc_datatype): | ||
"""Write a value to the device.""" | ||
with self._lock: | ||
return self._client.write_by_name(name, value, plc_datatype) | ||
|
||
def read_by_name(self, name, plc_datatype): | ||
"""Read a value from the device.""" | ||
with self._lock: | ||
return self._client.read_by_name(name, plc_datatype) | ||
|
||
def add_device_notification(self, name, plc_datatype, callback): | ||
"""Add a notification to the ADS devices.""" | ||
from pyads import NotificationAttrib | ||
attr = NotificationAttrib(ctypes.sizeof(plc_datatype)) | ||
|
||
with self._lock: | ||
hnotify, huser = self._client.add_device_notification( | ||
name, attr, self._device_notification_callback | ||
) | ||
hnotify = int(hnotify) | ||
|
||
_LOGGER.debug( | ||
'Added Device Notification %d for variable %s', hnotify, name | ||
) | ||
|
||
self._notification_items[hnotify] = NotificationItem( | ||
hnotify, huser, name, plc_datatype, callback | ||
) | ||
|
||
def _device_notification_callback(self, addr, notification, huser): | ||
"""Handle device notifications.""" | ||
contents = notification.contents | ||
|
||
hnotify = int(contents.hNotification) | ||
_LOGGER.debug('Received Notification %d', hnotify) | ||
data = contents.data | ||
|
||
try: | ||
notification_item = self._notification_items[hnotify] | ||
except KeyError: | ||
_LOGGER.debug('Unknown Device Notification handle: %d', hnotify) | ||
return | ||
|
||
# parse data to desired datatype | ||
if notification_item.plc_datatype == self.PLCTYPE_BOOL: | ||
value = bool(struct.unpack('<?', bytearray(data)[:1])[0]) | ||
elif notification_item.plc_datatype == self.PLCTYPE_INT: | ||
value = struct.unpack('<h', bytearray(data)[:2])[0] | ||
elif notification_item.plc_datatype == self.PLCTYPE_BYTE: | ||
value = struct.unpack('<B', bytearray(data)[:1])[0] | ||
elif notification_item.plc_datatype == self.PLCTYPE_UINT: | ||
value = struct.unpack('<H', bytearray(data)[:2])[0] | ||
else: | ||
value = bytearray(data) | ||
_LOGGER.warning('No callback available for this datatype.') | ||
|
||
# execute callback | ||
notification_item.callback(notification_item.name, value) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
# Describes the format for available ADS services | ||
|
||
write_data_by_name: | ||
description: Write a value to the connected ADS device. | ||
|
||
fields: | ||
adsvar: | ||
description: The name of the variable to write to. | ||
example: '.global_var' | ||
adstype: | ||
description: The data type of the variable to write to. | ||
example: 'int' | ||
value: | ||
description: The value to write to the variable. | ||
example: 1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
""" | ||
Support for ADS binary sensors. | ||
For more details about this platform, please refer to the documentation. | ||
https://home-assistant.io/components/binary_sensor.ads/ | ||
""" | ||
import asyncio | ||
import logging | ||
import voluptuous as vol | ||
from homeassistant.components.binary_sensor import BinarySensorDevice, \ | ||
PLATFORM_SCHEMA, DEVICE_CLASSES_SCHEMA | ||
from homeassistant.components.ads import DATA_ADS, CONF_ADS_VAR | ||
from homeassistant.const import CONF_NAME, CONF_DEVICE_CLASS | ||
import homeassistant.helpers.config_validation as cv | ||
|
||
|
||
_LOGGER = logging.getLogger(__name__) | ||
|
||
DEPENDENCIES = ['ads'] | ||
DEFAULT_NAME = 'ADS binary sensor' | ||
|
||
|
||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ | ||
vol.Required(CONF_ADS_VAR): cv.string, | ||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, | ||
vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA, | ||
}) | ||
|
||
|
||
def setup_platform(hass, config, add_devices, discovery_info=None): | ||
"""Set up the Binary Sensor platform for ADS.""" | ||
ads_hub = hass.data.get(DATA_ADS) | ||
|
||
ads_var = config.get(CONF_ADS_VAR) | ||
name = config.get(CONF_NAME) | ||
device_class = config.get(CONF_DEVICE_CLASS) | ||
|
||
ads_sensor = AdsBinarySensor(ads_hub, name, ads_var, device_class) | ||
add_devices([ads_sensor]) | ||
|
||
|
||
class AdsBinarySensor(BinarySensorDevice): | ||
"""Representation of ADS binary sensors.""" | ||
|
||
def __init__(self, ads_hub, name, ads_var, device_class): | ||
"""Initialize AdsBinarySensor entity.""" | ||
self._name = name | ||
self._state = False | ||
self._device_class = device_class or 'moving' | ||
self._ads_hub = ads_hub | ||
self.ads_var = ads_var | ||
|
||
@asyncio.coroutine | ||
def async_added_to_hass(self): | ||
"""Register device notification.""" | ||
def update(name, value): | ||
"""Handle device notifications.""" | ||
_LOGGER.debug('Variable %s changed its value to %d', | ||
name, value) | ||
self._state = value | ||
self.schedule_update_ha_state() | ||
|
||
self.hass.async_add_job( | ||
self._ads_hub.add_device_notification, | ||
self.ads_var, self._ads_hub.PLCTYPE_BOOL, update | ||
) | ||
|
||
@property | ||
def name(self): | ||
"""Return the default name of the binary sensor.""" | ||
return self._name | ||
|
||
@property | ||
def device_class(self): | ||
"""Return the device class.""" | ||
return self._device_class | ||
|
||
@property | ||
def is_on(self): | ||
"""Return if the binary sensor is on.""" | ||
return self._state | ||
|
||
@property | ||
def should_poll(self): | ||
"""Return False because entity pushes its state to HA.""" | ||
return False |
Oops, something went wrong.