Merge pull request #11921 from home-assistant/release-0-62

0.62
This commit is contained in:
Paulus Schoutsen 2018-01-26 21:07:57 -08:00 committed by GitHub
commit 5bde72d490
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
322 changed files with 9662 additions and 7205 deletions

View File

@ -97,6 +97,9 @@ omit =
homeassistant/components/homematic/__init__.py
homeassistant/components/*/homematic.py
homeassistant/components/ihc/*
homeassistant/components/*/ihc.py
homeassistant/components/insteon_local.py
homeassistant/components/*/insteon_local.py
@ -106,6 +109,9 @@ omit =
homeassistant/components/ios.py
homeassistant/components/*/ios.py
homeassistant/components/iota.py
homeassistant/components/*/iota.py
homeassistant/components/isy994.py
homeassistant/components/*/isy994.py
@ -145,6 +151,9 @@ omit =
homeassistant/components/modbus.py
homeassistant/components/*/modbus.py
homeassistant/components/mychevy.py
homeassistant/components/*/mychevy.py
homeassistant/components/mysensors.py
homeassistant/components/*/mysensors.py
@ -241,6 +250,9 @@ omit =
homeassistant/components/volvooncall.py
homeassistant/components/*/volvooncall.py
homeassistant/components/waterfurnace.py
homeassistant/components/*/waterfurnace.py
homeassistant/components/*/webostv.py
homeassistant/components/wemo.py
@ -304,6 +316,7 @@ omit =
homeassistant/components/camera/ring.py
homeassistant/components/camera/rpi_camera.py
homeassistant/components/camera/synology.py
homeassistant/components/camera/xeoma.py
homeassistant/components/camera/yi.py
homeassistant/components/climate/econet.py
homeassistant/components/climate/ephember.py
@ -318,6 +331,7 @@ omit =
homeassistant/components/climate/radiotherm.py
homeassistant/components/climate/sensibo.py
homeassistant/components/climate/touchline.py
homeassistant/components/climate/venstar.py
homeassistant/components/cover/garadget.py
homeassistant/components/cover/homematic.py
homeassistant/components/cover/knx.py
@ -327,7 +341,6 @@ omit =
homeassistant/components/cover/scsgate.py
homeassistant/components/device_tracker/actiontec.py
homeassistant/components/device_tracker/aruba.py
homeassistant/components/device_tracker/asuswrt.py
homeassistant/components/device_tracker/automatic.py
homeassistant/components/device_tracker/bbox.py
homeassistant/components/device_tracker/bluetooth_le_tracker.py
@ -504,6 +517,7 @@ omit =
homeassistant/components/sensor/bitcoin.py
homeassistant/components/sensor/blockchain.py
homeassistant/components/sensor/bme280.py
homeassistant/components/sensor/bme680.py
homeassistant/components/sensor/bom.py
homeassistant/components/sensor/broadlink.py
homeassistant/components/sensor/buienradar.py
@ -606,6 +620,7 @@ omit =
homeassistant/components/sensor/sytadin.py
homeassistant/components/sensor/tank_utility.py
homeassistant/components/sensor/ted5000.py
homeassistant/components/sensor/teksavvy.py
homeassistant/components/sensor/temper.py
homeassistant/components/sensor/tibber.py
homeassistant/components/sensor/time_date.py

13
.github/move.yml vendored Normal file
View File

@ -0,0 +1,13 @@
# Configuration for move-issues - https://github.com/dessant/move-issues
# Delete the command comment. Ignored when the comment also contains other content
deleteCommand: true
# Close the source issue after moving
closeSourceIssue: true
# Lock the source issue after moving
lockSourceIssue: false
# Set custom aliases for targets
# aliases:
# r: repo
# or: owner/repo

View File

@ -32,12 +32,15 @@ homeassistant/components/zone.py @home-assistant/core
# To monitor non-pypi additions
requirements_all.txt @andrey-git
# HomeAssistant developer Teams
Dockerfile @home-assistant/docker
virtualization/Docker/* @home-assistant/docker
homeassistant/components/zwave/* @home-assistant/z-wave
homeassistant/components/*/zwave.py @home-assistant/z-wave
homeassistant/components/hassio.py @home-assistant/hassio
# Indiviudal components
homeassistant/components/alarm_control_panel/egardia.py @jeroenterheerdt
homeassistant/components/camera/yi.py @bachya

View File

@ -1,9 +1,8 @@
"""
ADS Component.
Support for Automation Device Specification (ADS).
For more details about this component, please refer to the documentation.
https://home-assistant.io/components/ads/
"""
import threading
import struct
@ -29,7 +28,6 @@ ADSTYPE_BOOL = 'bool'
DOMAIN = 'ads'
# config variable names
CONF_ADS_VAR = 'adsvar'
CONF_ADS_VAR_BRIGHTNESS = 'adsvar_brightness'
CONF_ADS_TYPE = 'adstype'
@ -47,10 +45,10 @@ CONFIG_SCHEMA = vol.Schema({
}, extra=vol.ALLOW_EXTRA)
SCHEMA_SERVICE_WRITE_DATA_BY_NAME = vol.Schema({
vol.Required(CONF_ADS_TYPE):
vol.In([ADSTYPE_INT, ADSTYPE_UINT, ADSTYPE_BYTE]),
vol.Required(CONF_ADS_VALUE): cv.match_all,
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
})
@ -59,15 +57,12 @@ def setup(hass, config):
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,
@ -81,16 +76,13 @@ def setup(hass, config):
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
)
"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)
@ -107,43 +99,41 @@ def setup(hass, config):
hass.services.register(
DOMAIN, SERVICE_WRITE_DATA_BY_NAME, handle_write_data_by_name,
schema=SCHEMA_SERVICE_WRITE_DATA_BY_NAME
)
schema=SCHEMA_SERVICE_WRITE_DATA_BY_NAME)
return True
# tuple to hold data needed for notification
# Tuple to hold data needed for notification
NotificationItem = namedtuple(
'NotificationItem', 'hnotify huser name plc_datatype callback'
)
class AdsHub:
"""Representation of a PyADS connection."""
class AdsHub(object):
"""Representation of an ADS connection."""
def __init__(self, ads_client):
"""Initialize the ADS Hub."""
"""Initialize the ADS hub."""
self._client = ads_client
self._client.open()
# all ADS devices are registered here
# 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')
_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
)
"Deleting device notification %d, %d",
notification_item.hnotify, notification_item.huser)
self._client.close()
def register_device(self, device):
@ -167,33 +157,30 @@ class AdsHub:
with self._lock:
hnotify, huser = self._client.add_device_notification(
name, attr, self._device_notification_callback
)
name, attr, self._device_notification_callback)
hnotify = int(hnotify)
_LOGGER.debug(
'Added Device Notification %d for variable %s', hnotify, name
)
"Added device notification %d for variable %s", hnotify, name)
self._notification_items[hnotify] = NotificationItem(
hnotify, huser, name, plc_datatype, callback
)
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)
_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)
_LOGGER.debug("Unknown device notification handle: %d", hnotify)
return
# parse data to desired datatype
# 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:
@ -204,7 +191,6 @@ class AdsHub:
value = struct.unpack('<H', bytearray(data)[:2])[0]
else:
value = bytearray(data)
_LOGGER.warning('No callback available for this datatype.')
_LOGGER.warning("No callback available for this datatype")
# execute callback
notification_item.callback(notification_item.name, value)

View File

@ -6,12 +6,12 @@ https://home-assistant.io/components/alarm_control_panel.abode/
"""
import logging
from homeassistant.components.abode import (
AbodeDevice, DOMAIN as ABODE_DOMAIN, CONF_ATTRIBUTION)
from homeassistant.components.alarm_control_panel import (AlarmControlPanel)
from homeassistant.const import (ATTR_ATTRIBUTION, STATE_ALARM_ARMED_AWAY,
STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED)
from homeassistant.components.abode import CONF_ATTRIBUTION, AbodeDevice
from homeassistant.components.abode import DOMAIN as ABODE_DOMAIN
from homeassistant.components.alarm_control_panel import AlarmControlPanel
from homeassistant.const import (
ATTR_ATTRIBUTION, STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME,
STATE_ALARM_DISARMED)
DEPENDENCIES = ['abode']
@ -21,7 +21,7 @@ ICON = 'mdi:security'
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up a sensor for an Abode device."""
"""Set up an alarm control panel for an Abode device."""
data = hass.data[ABODE_DOMAIN]
alarm_devices = [AbodeAlarm(data, data.abode.get_alarm(), data.name)]
@ -41,7 +41,7 @@ class AbodeAlarm(AbodeDevice, AlarmControlPanel):
@property
def icon(self):
"""Return icon."""
"""Return the icon."""
return ICON
@property
@ -81,5 +81,5 @@ class AbodeAlarm(AbodeDevice, AlarmControlPanel):
ATTR_ATTRIBUTION: CONF_ATTRIBUTION,
'device_id': self._device.device_id,
'battery_backup': self._device.battery,
'cellular_backup': self._device.is_cellular
'cellular_backup': self._device.is_cellular,
}

View File

@ -10,12 +10,11 @@ import logging
import voluptuous as vol
import homeassistant.components.alarm_control_panel as alarm
import homeassistant.helpers.config_validation as cv
from homeassistant.components.alarmdecoder import (
DATA_AD, SIGNAL_PANEL_MESSAGE)
from homeassistant.components.alarmdecoder import DATA_AD, SIGNAL_PANEL_MESSAGE
from homeassistant.const import (
ATTR_CODE, STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME,
STATE_ALARM_DISARMED, STATE_ALARM_TRIGGERED)
import homeassistant.helpers.config_validation as cv
_LOGGER = logging.getLogger(__name__)
@ -67,6 +66,7 @@ class AlarmDecoderAlarmPanel(alarm.AlarmControlPanel):
SIGNAL_PANEL_MESSAGE, self._message_callback)
def _message_callback(self, message):
"""Handle received messages."""
if message.alarm_sounding or message.fire_alarm:
self._state = STATE_ALARM_TRIGGERED
elif message.armed_away:
@ -120,7 +120,7 @@ class AlarmDecoderAlarmPanel(alarm.AlarmControlPanel):
'entry_delay_off': self._entry_delay_off,
'programming_mode': self._programming_mode,
'ready': self._ready,
'zone_bypassed': self._zone_bypassed
'zone_bypassed': self._zone_bypassed,
}
def alarm_disarm(self, code=None):

View File

@ -4,17 +4,18 @@ Interfaces with Alarm.com alarm control panels.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/alarm_control_panel.alarmdotcom/
"""
import logging
import asyncio
import logging
import voluptuous as vol
import homeassistant.components.alarm_control_panel as alarm
from homeassistant.components.alarm_control_panel import PLATFORM_SCHEMA
from homeassistant.const import (
CONF_PASSWORD, CONF_USERNAME, STATE_ALARM_ARMED_AWAY,
STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED, STATE_UNKNOWN, CONF_CODE,
CONF_NAME)
import homeassistant.helpers.config_validation as cv
CONF_CODE, CONF_NAME, CONF_PASSWORD, CONF_USERNAME, STATE_ALARM_ARMED_AWAY,
STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED, STATE_UNKNOWN)
from homeassistant.helpers.aiohttp_client import async_get_clientsession
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['pyalarmdotcom==0.3.0']
@ -44,7 +45,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
class AlarmDotCom(alarm.AlarmControlPanel):
"""Represent an Alarm.com status."""
"""Representation of an Alarm.com status."""
def __init__(self, hass, name, code, username, password):
"""Initialize the Alarm.com status."""
@ -57,10 +58,8 @@ class AlarmDotCom(alarm.AlarmControlPanel):
self._password = password
self._websession = async_get_clientsession(self._hass)
self._state = STATE_UNKNOWN
self._alarm = Alarmdotcom(username,
password,
self._websession,
hass.loop)
self._alarm = Alarmdotcom(
username, password, self._websession, hass.loop)
@asyncio.coroutine
def async_login(self):
@ -80,7 +79,7 @@ class AlarmDotCom(alarm.AlarmControlPanel):
@property
def code_format(self):
"""One or more characters if code is defined."""
"""Return one or more characters if code is defined."""
return None if self._code is None else '.+'
@property
@ -116,5 +115,5 @@ class AlarmDotCom(alarm.AlarmControlPanel):
"""Validate given code."""
check = self._code is None or code == self._code
if not check:
_LOGGER.warning('Wrong code entered.')
_LOGGER.warning("Wrong code entered")
return check

View File

@ -29,9 +29,9 @@ DEFAULT_PORT = 5007
SCAN_INTERVAL = timedelta(seconds=1)
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_HOST, default=DEFAULT_HOST): cv.string,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
})
@ -47,7 +47,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
add_devices([Concord232Alarm(hass, url, name)])
except requests.exceptions.ConnectionError as ex:
_LOGGER.error("Unable to connect to Concord232: %s", str(ex))
return False
return
class Concord232Alarm(alarm.AlarmControlPanel):
@ -107,7 +107,7 @@ class Concord232Alarm(alarm.AlarmControlPanel):
newstate = STATE_ALARM_ARMED_AWAY
if not newstate == self._state:
_LOGGER.info("State Change from %s to %s", self._state, newstate)
_LOGGER.info("State change from %s to %s", self._state, newstate)
self._state = newstate
return self._state

View File

@ -10,13 +10,13 @@ import requests
import voluptuous as vol
import homeassistant.components.alarm_control_panel as alarm
import homeassistant.exceptions as exc
import homeassistant.helpers.config_validation as cv
from homeassistant.components.alarm_control_panel import PLATFORM_SCHEMA
from homeassistant.const import (
CONF_PORT, CONF_HOST, CONF_PASSWORD, CONF_USERNAME, STATE_UNKNOWN,
CONF_NAME, STATE_ALARM_DISARMED, STATE_ALARM_ARMED_HOME,
STATE_ALARM_ARMED_AWAY, STATE_ALARM_TRIGGERED, EVENT_HOMEASSISTANT_STOP)
CONF_HOST, CONF_NAME, CONF_PASSWORD, CONF_PORT, CONF_USERNAME,
EVENT_HOMEASSISTANT_STOP, STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME,
STATE_ALARM_DISARMED, STATE_ALARM_TRIGGERED, STATE_UNKNOWN)
import homeassistant.exceptions as exc
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['pythonegardia==1.0.26']
@ -35,6 +35,7 @@ DEFAULT_REPORT_SERVER_PORT = 52010
DEFAULT_VERSION = 'GATE-01'
DOMAIN = 'egardia'
D_EGARDIASRV = 'egardiaserver'
NOTIFICATION_ID = 'egardia_notification'
NOTIFICATION_TITLE = 'Egardia'
@ -97,8 +98,8 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
server = egardiaserver.EgardiaServer('', rs_port)
bound = server.bind()
if not bound:
raise IOError("Binding error occurred while " +
"starting EgardiaServer")
raise IOError(
"Binding error occurred while starting EgardiaServer")
hass.data[D_EGARDIASRV] = server
server.start()
except IOError:
@ -106,22 +107,19 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
hass.data[D_EGARDIASRV].register_callback(eg_dev.handle_status_event)
def handle_stop_event(event):
"""Callback function for HA stop event."""
"""Call function for Home Assistant stop event."""
hass.data[D_EGARDIASRV].stop()
# listen to home assistant stop event
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, handle_stop_event)
# add egardia alarm device
add_devices([eg_dev], True)
class EgardiaAlarm(alarm.AlarmControlPanel):
"""Representation of a Egardia alarm."""
def __init__(self, name, egardiasystem,
rs_enabled=False, rs_codes=None):
"""Initialize object."""
def __init__(self, name, egardiasystem, rs_enabled=False, rs_codes=None):
"""Initialize the Egardia alarm."""
self._name = name
self._egardiasystem = egardiasystem
self._status = None
@ -149,7 +147,7 @@ class EgardiaAlarm(alarm.AlarmControlPanel):
return False
def handle_status_event(self, event):
"""Handle egardia_system_status_event."""
"""Handle the Egardia system status event."""
statuscode = event.get('status')
if statuscode is not None:
status = self.lookupstatusfromcode(statuscode)

View File

@ -8,12 +8,12 @@ import logging
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
import homeassistant.components.alarm_control_panel as alarm
from homeassistant.components.alarm_control_panel import PLATFORM_SCHEMA
from homeassistant.const import (
CONF_PASSWORD, CONF_USERNAME, CONF_HOST, STATE_ALARM_ARMED_AWAY,
STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED, CONF_NAME)
CONF_HOST, CONF_NAME, CONF_PASSWORD, CONF_USERNAME, STATE_ALARM_ARMED_AWAY,
STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED)
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['pyialarm==0.2']
@ -33,9 +33,9 @@ def no_application_protocol(value):
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_HOST): vol.All(cv.string, no_application_protocol),
vol.Required(CONF_PASSWORD): cv.string,
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_HOST): vol.All(cv.string, no_application_protocol),
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
})
@ -53,7 +53,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
class IAlarmPanel(alarm.AlarmControlPanel):
"""Represent an iAlarm status."""
"""Representation of an iAlarm status."""
def __init__(self, name, username, password, url):
"""Initialize the iAlarm status."""

View File

@ -11,15 +11,17 @@ import logging
import voluptuous as vol
import homeassistant.components.alarm_control_panel as alarm
import homeassistant.util.dt as dt_util
from homeassistant.const import (
STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT,
STATE_ALARM_ARMED_CUSTOM_BYPASS, STATE_ALARM_DISARMED, STATE_ALARM_PENDING,
STATE_ALARM_TRIGGERED, CONF_PLATFORM, CONF_NAME, CONF_CODE,
CONF_DELAY_TIME, CONF_PENDING_TIME, CONF_TRIGGER_TIME,
CONF_DISARM_AFTER_TRIGGER)
CONF_CODE, CONF_DELAY_TIME, CONF_DISARM_AFTER_TRIGGER, CONF_NAME,
CONF_PENDING_TIME, CONF_PLATFORM, CONF_TRIGGER_TIME,
STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_CUSTOM_BYPASS,
STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT, STATE_ALARM_DISARMED,
STATE_ALARM_PENDING, STATE_ALARM_TRIGGERED)
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.event import track_point_in_time
import homeassistant.util.dt as dt_util
_LOGGER = logging.getLogger(__name__)
CONF_CODE_TEMPLATE = 'code_template'
@ -44,6 +46,7 @@ ATTR_POST_PENDING_STATE = 'post_pending_state'
def _state_validator(config):
"""Validate the state."""
config = copy.deepcopy(config)
for state in SUPPORTED_PRETRIGGER_STATES:
if CONF_DELAY_TIME not in config[state]:
@ -58,6 +61,7 @@ def _state_validator(config):
def _state_schema(state):
"""Validate the state."""
schema = {}
if state in SUPPORTED_PRETRIGGER_STATES:
schema[vol.Optional(CONF_DELAY_TIME)] = vol.All(
@ -97,8 +101,6 @@ PLATFORM_SCHEMA = vol.Schema(vol.All({
_state_schema(STATE_ALARM_TRIGGERED),
}, _state_validator))
_LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the manual alarm platform."""
@ -151,7 +153,7 @@ class ManualAlarm(alarm.AlarmControlPanel):
@property
def should_poll(self):
"""Return the plling state."""
"""Return the polling state."""
return False
@property
@ -182,23 +184,26 @@ class ManualAlarm(alarm.AlarmControlPanel):
@property
def _active_state(self):
"""Get the current state."""
if self.state == STATE_ALARM_PENDING:
return self._previous_state
else:
return self._state
def _pending_time(self, state):
"""Get the pending time."""
pending_time = self._pending_time_by_state[state]
if state == STATE_ALARM_TRIGGERED:
pending_time += self._delay_time_by_state[self._previous_state]
return pending_time
def _within_pending_time(self, state):
"""Get if the action is in the pending time window."""
return self._state_ts + self._pending_time(state) > dt_util.utcnow()
@property
def code_format(self):
"""One or more characters."""
"""Return one or more characters."""
return None if self._code is None else '.+'
def alarm_disarm(self, code=None):
@ -250,6 +255,7 @@ class ManualAlarm(alarm.AlarmControlPanel):
self._update_state(STATE_ALARM_TRIGGERED)
def _update_state(self, state):
"""Update the state."""
if self._state == state:
return

View File

@ -26,6 +26,8 @@ from homeassistant.core import callback
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.event import track_point_in_time
_LOGGER = logging.getLogger(__name__)
CONF_CODE_TEMPLATE = 'code_template'
CONF_PAYLOAD_DISARM = 'payload_disarm'
@ -58,6 +60,7 @@ ATTR_POST_PENDING_STATE = 'post_pending_state'
def _state_validator(config):
"""Validate the state."""
config = copy.deepcopy(config)
for state in SUPPORTED_PRETRIGGER_STATES:
if CONF_DELAY_TIME not in config[state]:
@ -72,6 +75,7 @@ def _state_validator(config):
def _state_schema(state):
"""Validate the state."""
schema = {}
if state in SUPPORTED_PRETRIGGER_STATES:
schema[vol.Optional(CONF_DELAY_TIME)] = vol.All(
@ -117,8 +121,6 @@ PLATFORM_SCHEMA = vol.Schema(vol.All(mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend({
vol.Optional(CONF_PAYLOAD_DISARM, default=DEFAULT_DISARM): cv.string,
}), _state_validator))
_LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the manual MQTT alarm platform."""
@ -150,11 +152,10 @@ class ManualMQTTAlarm(alarm.AlarmControlPanel):
A trigger_time of zero disables the alarm_trigger service.
"""
def __init__(self, hass, name, code, code_template,
disarm_after_trigger,
state_topic, command_topic, qos,
payload_disarm, payload_arm_home, payload_arm_away,
payload_arm_night, config):
def __init__(self, hass, name, code, code_template, disarm_after_trigger,
state_topic, command_topic, qos, payload_disarm,
payload_arm_home, payload_arm_away, payload_arm_night,
config):
"""Init the manual MQTT alarm panel."""
self._state = STATE_ALARM_DISARMED
self._hass = hass
@ -219,23 +220,26 @@ class ManualMQTTAlarm(alarm.AlarmControlPanel):
@property
def _active_state(self):
"""Get the current state."""
if self.state == STATE_ALARM_PENDING:
return self._previous_state
else:
return self._state
def _pending_time(self, state):
"""Get the pending time."""
pending_time = self._pending_time_by_state[state]
if state == STATE_ALARM_TRIGGERED:
pending_time += self._delay_time_by_state[self._previous_state]
return pending_time
def _within_pending_time(self, state):
"""Get if the action is in the pending time window."""
return self._state_ts + self._pending_time(state) > dt_util.utcnow()
@property
def code_format(self):
"""One or more characters."""
"""Return one or more characters."""
return None if self._code is None else '.+'
def alarm_disarm(self, code=None):
@ -280,6 +284,7 @@ class ManualMQTTAlarm(alarm.AlarmControlPanel):
self._update_state(STATE_ALARM_TRIGGERED)
def _update_state(self, state):
"""Update the state."""
if self._state == state:
return
@ -329,7 +334,7 @@ class ManualMQTTAlarm(alarm.AlarmControlPanel):
return state_attr
def async_added_to_hass(self):
"""Subscribe mqtt events.
"""Subscribe to MQTT events.
This method must be run in the event loop and returns a coroutine.
"""
@ -358,5 +363,5 @@ class ManualMQTTAlarm(alarm.AlarmControlPanel):
@asyncio.coroutine
def _async_state_changed_listener(self, entity_id, old_state, new_state):
"""Publish state change to MQTT."""
mqtt.async_publish(self.hass, self._state_topic, new_state.state,
self._qos, True)
mqtt.async_publish(
self.hass, self._state_topic, new_state.state, self._qos, True)

View File

@ -42,7 +42,7 @@ PLATFORM_SCHEMA = mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend({
vol.Optional(CONF_PAYLOAD_ARM_AWAY, default=DEFAULT_ARM_AWAY): cv.string,
vol.Optional(CONF_PAYLOAD_ARM_HOME, default=DEFAULT_ARM_HOME): cv.string,
vol.Optional(CONF_PAYLOAD_DISARM, default=DEFAULT_DISARM): cv.string,
})
}).extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema)
@asyncio.coroutine

View File

@ -12,8 +12,8 @@ import voluptuous as vol
import homeassistant.components.alarm_control_panel as alarm
from homeassistant.components.alarm_control_panel import PLATFORM_SCHEMA
from homeassistant.const import (
STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED,
STATE_UNKNOWN, CONF_NAME, CONF_HOST, CONF_PORT)
CONF_HOST, CONF_NAME, CONF_PORT, STATE_ALARM_ARMED_AWAY,
STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED, STATE_UNKNOWN)
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['pynx584==0.4']
@ -25,14 +25,14 @@ DEFAULT_NAME = 'NX584'
DEFAULT_PORT = 5007
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_HOST, default=DEFAULT_HOST): cv.string,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
})
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the nx584 platform."""
"""Set up the NX584 platform."""
name = config.get(CONF_NAME)
host = config.get(CONF_HOST)
port = config.get(CONF_PORT)
@ -88,7 +88,7 @@ class NX584Alarm(alarm.AlarmControlPanel):
self._state = STATE_UNKNOWN
zones = []
except IndexError:
_LOGGER.error("nx584 reports no partitions")
_LOGGER.error("NX584 reports no partitions")
self._state = STATE_UNKNOWN
zones = []

View File

@ -8,9 +8,8 @@ import asyncio
import logging
import homeassistant.components.alarm_control_panel as alarm
from homeassistant.components.satel_integra import (CONF_ARM_HOME_MODE,
DATA_SATEL,
SIGNAL_PANEL_MESSAGE)
from homeassistant.components.satel_integra import (
CONF_ARM_HOME_MODE, DATA_SATEL, SIGNAL_PANEL_MESSAGE)
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
@ -21,12 +20,12 @@ DEPENDENCIES = ['satel_integra']
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Set up for AlarmDecoder alarm panels."""
"""Set up for Satel Integra alarm panels."""
if not discovery_info:
return
device = SatelIntegraAlarmPanel("Alarm Panel",
discovery_info.get(CONF_ARM_HOME_MODE))
device = SatelIntegraAlarmPanel(
"Alarm Panel", discovery_info.get(CONF_ARM_HOME_MODE))
async_add_devices([device])
@ -47,7 +46,7 @@ class SatelIntegraAlarmPanel(alarm.AlarmControlPanel):
@callback
def _message_callback(self, message):
"""Handle received messages."""
if message != self._state:
self._state = message
self.async_schedule_update_ha_state()
@ -90,5 +89,5 @@ class SatelIntegraAlarmPanel(alarm.AlarmControlPanel):
def async_alarm_arm_home(self, code=None):
"""Send arm home command."""
if code:
yield from self.hass.data[DATA_SATEL].arm(code,
self._arm_home_mode)
yield from self.hass.data[DATA_SATEL].arm(
code, self._arm_home_mode)

View File

@ -11,9 +11,9 @@ import voluptuous as vol
import homeassistant.components.alarm_control_panel as alarm
from homeassistant.components.alarm_control_panel import PLATFORM_SCHEMA
from homeassistant.const import (
CONF_PASSWORD, CONF_USERNAME, STATE_UNKNOWN, CONF_CODE, CONF_NAME,
STATE_ALARM_DISARMED, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_AWAY,
EVENT_HOMEASSISTANT_STOP)
CONF_CODE, CONF_NAME, CONF_PASSWORD, CONF_USERNAME,
EVENT_HOMEASSISTANT_STOP, STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME,
STATE_ALARM_DISARMED, STATE_UNKNOWN)
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['simplisafe-python==1.0.5']
@ -22,6 +22,7 @@ _LOGGER = logging.getLogger(__name__)
DEFAULT_NAME = 'SimpliSafe'
DOMAIN = 'simplisafe'
NOTIFICATION_ID = 'simplisafe_notification'
NOTIFICATION_TITLE = 'SimpliSafe Setup'
@ -65,7 +66,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
class SimpliSafeAlarm(alarm.AlarmControlPanel):
"""Representation a SimpliSafe alarm."""
"""Representation of a SimpliSafe alarm."""
def __init__(self, simplisafe, name, code):
"""Initialize the SimpliSafe alarm."""
@ -82,7 +83,7 @@ class SimpliSafeAlarm(alarm.AlarmControlPanel):
@property
def code_format(self):
"""One or more characters if code is defined."""
"""Return one or more characters if code is defined."""
return None if self._code is None else '.+'
@property
@ -103,12 +104,12 @@ class SimpliSafeAlarm(alarm.AlarmControlPanel):
def device_state_attributes(self):
"""Return the state attributes."""
return {
'temperature': self.simplisafe.temperature(),
'alarm': self.simplisafe.alarm(),
'co': self.simplisafe.carbon_monoxide(),
'fire': self.simplisafe.fire(),
'alarm': self.simplisafe.alarm(),
'flood': self.simplisafe.flood(),
'last_event': self.simplisafe.last_event(),
'flood': self.simplisafe.flood()
'temperature': self.simplisafe.temperature(),
}
def update(self):

View File

@ -9,26 +9,27 @@ import logging
import homeassistant.components.alarm_control_panel as alarm
from homeassistant.components.spc import (
SpcWebGateway, ATTR_DISCOVER_AREAS, DATA_API, DATA_REGISTRY)
ATTR_DISCOVER_AREAS, DATA_API, DATA_REGISTRY, SpcWebGateway)
from homeassistant.const import (
STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED,
STATE_UNKNOWN)
_LOGGER = logging.getLogger(__name__)
SPC_AREA_MODE_TO_STATE = {'0': STATE_ALARM_DISARMED,
'1': STATE_ALARM_ARMED_HOME,
'3': STATE_ALARM_ARMED_AWAY}
SPC_AREA_MODE_TO_STATE = {
'0': STATE_ALARM_DISARMED,
'1': STATE_ALARM_ARMED_HOME,
'3': STATE_ALARM_ARMED_AWAY,
}
def _get_alarm_state(spc_mode):
"""Get the alarm state."""
return SPC_AREA_MODE_TO_STATE.get(spc_mode, STATE_UNKNOWN)
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices,
discovery_info=None):
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Set up the SPC alarm control panel platform."""
if (discovery_info is None or
discovery_info[ATTR_DISCOVER_AREAS] is None):
@ -42,7 +43,7 @@ def async_setup_platform(hass, config, async_add_devices,
class SpcAlarm(alarm.AlarmControlPanel):
"""Represents the SPC alarm panel."""
"""Representation of the SPC alarm panel."""
def __init__(self, api, area):
"""Initialize the SPC alarm panel."""
@ -57,7 +58,7 @@ class SpcAlarm(alarm.AlarmControlPanel):
@asyncio.coroutine
def async_added_to_hass(self):
"""Calbback for init handlers."""
"""Call for adding new entities."""
self.hass.data[DATA_REGISTRY].register_alarm_device(
self._area_id, self)

View File

@ -8,8 +8,8 @@ import logging
from time import sleep
import homeassistant.components.alarm_control_panel as alarm
from homeassistant.components.verisure import CONF_ALARM, CONF_CODE_DIGITS
from homeassistant.components.verisure import HUB as hub
from homeassistant.components.verisure import (CONF_ALARM, CONF_CODE_DIGITS)
from homeassistant.const import (
STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED,
STATE_UNKNOWN)
@ -43,7 +43,7 @@ class VerisureAlarm(alarm.AlarmControlPanel):
"""Representation of a Verisure alarm status."""
def __init__(self):
"""Initalize the Verisure alarm panel."""
"""Initialize the Verisure alarm panel."""
self._state = STATE_UNKNOWN
self._digits = hub.config.get(CONF_CODE_DIGITS)
self._changed_by = None

View File

@ -8,11 +8,10 @@ import asyncio
import logging
import homeassistant.components.alarm_control_panel as alarm
from homeassistant.const import (STATE_UNKNOWN,
STATE_ALARM_DISARMED,
STATE_ALARM_ARMED_HOME,
STATE_ALARM_ARMED_AWAY)
from homeassistant.components.wink import WinkDevice, DOMAIN
from homeassistant.components.wink import DOMAIN, WinkDevice
from homeassistant.const import (
STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED,
STATE_UNKNOWN)
_LOGGER = logging.getLogger(__name__)
@ -41,7 +40,7 @@ class WinkCameraDevice(WinkDevice, alarm.AlarmControlPanel):
@asyncio.coroutine
def async_added_to_hass(self):
"""Callback when entity is added to hass."""
"""Call when entity is added to hass."""
self.hass.data[DOMAIN]['entities']['alarm_control_panel'].append(self)
@property

View File

@ -10,18 +10,33 @@ import logging
import voluptuous as vol
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers import entityfilter
from . import flash_briefings, intent, smart_home
from .const import (
DOMAIN, CONF_UID, CONF_TITLE, CONF_AUDIO, CONF_TEXT, CONF_DISPLAY_URL)
from . import flash_briefings, intent
CONF_AUDIO, CONF_DISPLAY_URL, CONF_TEXT, CONF_TITLE, CONF_UID, DOMAIN,
CONF_FILTER, CONF_ENTITY_CONFIG)
_LOGGER = logging.getLogger(__name__)
CONF_FLASH_BRIEFINGS = 'flash_briefings'
CONF_SMART_HOME = 'smart_home'
DEPENDENCIES = ['http']
CONF_FLASH_BRIEFINGS = 'flash_briefings'
ALEXA_ENTITY_SCHEMA = vol.Schema({
vol.Optional(smart_home.CONF_DESCRIPTION): cv.string,
vol.Optional(smart_home.CONF_DISPLAY_CATEGORIES): cv.string,
vol.Optional(smart_home.CONF_NAME): cv.string,
})
SMART_HOME_SCHEMA = vol.Schema({
vol.Optional(
CONF_FILTER,
default=lambda: entityfilter.generate_filter([], [], [], [])
): entityfilter.FILTER_SCHEMA,
vol.Optional(CONF_ENTITY_CONFIG): {cv.entity_id: ALEXA_ENTITY_SCHEMA}
})
CONFIG_SCHEMA = vol.Schema({
DOMAIN: {
@ -33,7 +48,10 @@ CONFIG_SCHEMA = vol.Schema({
vol.Required(CONF_TEXT, default=""): cv.template,
vol.Optional(CONF_DISPLAY_URL): cv.template,
}]),
}
},
# vol.Optional here would mean we couldn't distinguish between an empty
# smart_home: and none at all.
CONF_SMART_HOME: vol.Any(SMART_HOME_SCHEMA, None),
}
}, extra=vol.ALLOW_EXTRA)
@ -49,4 +67,12 @@ def async_setup(hass, config):
if flash_briefings_config:
flash_briefings.async_setup(hass, flash_briefings_config)
try:
smart_home_config = config[CONF_SMART_HOME]
except KeyError:
pass
else:
smart_home_config = smart_home_config or SMART_HOME_SCHEMA({})
smart_home.async_setup(hass, smart_home_config)
return True

View File

@ -8,6 +8,9 @@ CONF_AUDIO = 'audio'
CONF_TEXT = 'text'
CONF_DISPLAY_URL = 'display_url'
CONF_FILTER = 'filter'
CONF_ENTITY_CONFIG = 'entity_config'
ATTR_UID = 'uid'
ATTR_UPDATE_DATE = 'updateDate'
ATTR_TITLE_TEXT = 'titleText'

View File

@ -5,19 +5,18 @@ For more details about this component, please refer to the documentation at
https://home-assistant.io/components/alexa/
"""
import copy
import logging
from datetime import datetime
import logging
import uuid
from homeassistant.components import http
from homeassistant.core import callback
from homeassistant.helpers import template
from homeassistant.components import http
from .const import (
CONF_UID, CONF_TITLE, CONF_AUDIO, CONF_TEXT, CONF_DISPLAY_URL, ATTR_UID,
ATTR_UPDATE_DATE, ATTR_TITLE_TEXT, ATTR_STREAM_URL, ATTR_MAIN_TEXT,
ATTR_REDIRECTION_URL, DATE_FORMAT)
ATTR_MAIN_TEXT, ATTR_REDIRECTION_URL, ATTR_STREAM_URL, ATTR_TITLE_TEXT,
ATTR_UID, ATTR_UPDATE_DATE, CONF_AUDIO, CONF_DISPLAY_URL, CONF_TEXT,
CONF_TITLE, CONF_UID, DATE_FORMAT)
_LOGGER = logging.getLogger(__name__)
@ -46,11 +45,11 @@ class AlexaFlashBriefingView(http.HomeAssistantView):
@callback
def get(self, request, briefing_id):
"""Handle Alexa Flash Briefing request."""
_LOGGER.debug('Received Alexa flash briefing request for: %s',
_LOGGER.debug("Received Alexa flash briefing request for: %s",
briefing_id)
if self.flash_briefings.get(briefing_id) is None:
err = 'No configured Alexa flash briefing was found for: %s'
err = "No configured Alexa flash briefing was found for: %s"
_LOGGER.error(err, briefing_id)
return b'', 404

View File

@ -3,30 +3,31 @@ Support for Alexa skill service end point.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/alexa/
"""
import asyncio
import enum
import logging
from homeassistant.exceptions import HomeAssistantError
from homeassistant.core import callback
from homeassistant.helpers import intent
from homeassistant.components import http
from homeassistant.core import callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import intent
from homeassistant.util.decorator import Registry
from .const import DOMAIN, SYN_RESOLUTION_MATCH
INTENTS_API_ENDPOINT = '/api/alexa'
HANDLERS = Registry()
_LOGGER = logging.getLogger(__name__)
HANDLERS = Registry()
INTENTS_API_ENDPOINT = '/api/alexa'
class SpeechType(enum.Enum):
"""The Alexa speech types."""
plaintext = "PlainText"
ssml = "SSML"
plaintext = 'PlainText'
ssml = 'SSML'
SPEECH_MAPPINGS = {
@ -38,8 +39,8 @@ SPEECH_MAPPINGS = {
class CardType(enum.Enum):
"""The Alexa card types."""
simple = "Simple"
link_account = "LinkAccount"
simple = 'Simple'
link_account = 'LinkAccount'
@callback
@ -64,7 +65,7 @@ class AlexaIntentsView(http.HomeAssistantView):
hass = request.app['hass']
message = yield from request.json()
_LOGGER.debug('Received Alexa request: %s', message)
_LOGGER.debug("Received Alexa request: %s", message)
try:
response = yield from async_handle_message(hass, message)
@ -81,7 +82,7 @@ class AlexaIntentsView(http.HomeAssistantView):
"This intent is not yet configured within Home Assistant."))
except intent.InvalidSlotInfo as err:
_LOGGER.error('Received invalid slot data from Alexa: %s', err)
_LOGGER.error("Received invalid slot data from Alexa: %s", err)
return self.json(intent_error_response(
hass, message,
"Invalid slot information received for this intent."))
@ -109,6 +110,7 @@ def async_handle_message(hass, message):
- intent.UnknownIntent
- intent.InvalidSlotInfo
- intent.IntentError
"""
req = message.get('request')
req_type = req['type']
@ -138,6 +140,7 @@ def async_handle_intent(hass, message):
- intent.UnknownIntent
- intent.InvalidSlotInfo
- intent.IntentError
"""
req = message.get('request')
alexa_intent_info = req.get('intent')

View File

@ -2,75 +2,374 @@
import asyncio
import logging
import math
from datetime import datetime
from uuid import uuid4
from homeassistant.components import (
alert, automation, cover, fan, group, input_boolean, light, lock,
media_player, scene, script, switch, http, sensor)
import homeassistant.core as ha
import homeassistant.util.color as color_util
from homeassistant.util.decorator import Registry
from homeassistant.const import (
ATTR_ENTITY_ID, ATTR_SUPPORTED_FEATURES, SERVICE_LOCK,
ATTR_ENTITY_ID, ATTR_SUPPORTED_FEATURES, CONF_NAME, SERVICE_LOCK,
SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PAUSE, SERVICE_MEDIA_PLAY,
SERVICE_MEDIA_PREVIOUS_TRACK, SERVICE_MEDIA_STOP,
SERVICE_SET_COVER_POSITION, SERVICE_TURN_OFF, SERVICE_TURN_ON,
SERVICE_UNLOCK, SERVICE_VOLUME_SET)
from homeassistant.components import (
alert, automation, cover, fan, group, input_boolean, light, lock,
media_player, scene, script, switch)
import homeassistant.util.color as color_util
from homeassistant.util.decorator import Registry
SERVICE_UNLOCK, SERVICE_VOLUME_SET, TEMP_FAHRENHEIT, TEMP_CELSIUS,
CONF_UNIT_OF_MEASUREMENT)
from .const import CONF_FILTER, CONF_ENTITY_CONFIG
HANDLERS = Registry()
_LOGGER = logging.getLogger(__name__)
API_DIRECTIVE = 'directive'
API_ENDPOINT = 'endpoint'
API_EVENT = 'event'
API_CONTEXT = 'context'
API_HEADER = 'header'
API_PAYLOAD = 'payload'
API_TEMP_UNITS = {
TEMP_FAHRENHEIT: 'FAHRENHEIT',
TEMP_CELSIUS: 'CELSIUS',
}
SMART_HOME_HTTP_ENDPOINT = '/api/alexa/smart_home'
CONF_DESCRIPTION = 'description'
CONF_DISPLAY_CATEGORIES = 'display_categories'
CONF_NAME = 'name'
HANDLERS = Registry()
MAPPING_COMPONENT = {
alert.DOMAIN: ['OTHER', ('Alexa.PowerController',), None],
automation.DOMAIN: ['OTHER', ('Alexa.PowerController',), None],
cover.DOMAIN: [
'DOOR', ('Alexa.PowerController',), {
cover.SUPPORT_SET_POSITION: 'Alexa.PercentageController',
}
],
fan.DOMAIN: [
'OTHER', ('Alexa.PowerController',), {
fan.SUPPORT_SET_SPEED: 'Alexa.PercentageController',
}
],
group.DOMAIN: ['OTHER', ('Alexa.PowerController',), None],
input_boolean.DOMAIN: ['OTHER', ('Alexa.PowerController',), None],
light.DOMAIN: [
'LIGHT', ('Alexa.PowerController',), {
light.SUPPORT_BRIGHTNESS: 'Alexa.BrightnessController',
light.SUPPORT_RGB_COLOR: 'Alexa.ColorController',
light.SUPPORT_XY_COLOR: 'Alexa.ColorController',
light.SUPPORT_COLOR_TEMP: 'Alexa.ColorTemperatureController',
}
],
lock.DOMAIN: ['SMARTLOCK', ('Alexa.LockController',), None],
media_player.DOMAIN: [
'TV', ('Alexa.PowerController',), {
media_player.SUPPORT_VOLUME_SET: 'Alexa.Speaker',
media_player.SUPPORT_PLAY: 'Alexa.PlaybackController',
media_player.SUPPORT_PAUSE: 'Alexa.PlaybackController',
media_player.SUPPORT_STOP: 'Alexa.PlaybackController',
media_player.SUPPORT_NEXT_TRACK: 'Alexa.PlaybackController',
media_player.SUPPORT_PREVIOUS_TRACK: 'Alexa.PlaybackController',
}
],
scene.DOMAIN: ['ACTIVITY_TRIGGER', ('Alexa.SceneController',), None],
script.DOMAIN: ['OTHER', ('Alexa.PowerController',), None],
switch.DOMAIN: ['SWITCH', ('Alexa.PowerController',), None],
class _DisplayCategory(object):
"""Possible display categories for Discovery response.
https://developer.amazon.com/docs/device-apis/alexa-discovery.html#display-categories
"""
# Describes a combination of devices set to a specific state, when the
# state change must occur in a specific order. For example, a "watch
# Neflix" scene might require the: 1. TV to be powered on & 2. Input set to
# HDMI1. Applies to Scenes
ACTIVITY_TRIGGER = "ACTIVITY_TRIGGER"
# Indicates media devices with video or photo capabilities.
CAMERA = "CAMERA"
# Indicates a door.
DOOR = "DOOR"
# Indicates light sources or fixtures.
LIGHT = "LIGHT"
# An endpoint that cannot be described in on of the other categories.
OTHER = "OTHER"
# Describes a combination of devices set to a specific state, when the
# order of the state change is not important. For example a bedtime scene
# might include turning off lights and lowering the thermostat, but the
# order is unimportant. Applies to Scenes
SCENE_TRIGGER = "SCENE_TRIGGER"
# Indicates an endpoint that locks.
SMARTLOCK = "SMARTLOCK"
# Indicates modules that are plugged into an existing electrical outlet.
# Can control a variety of devices.
SMARTPLUG = "SMARTPLUG"
# Indicates the endpoint is a speaker or speaker system.
SPEAKER = "SPEAKER"
# Indicates in-wall switches wired to the electrical system. Can control a
# variety of devices.
SWITCH = "SWITCH"
# Indicates endpoints that report the temperature only.
TEMPERATURE_SENSOR = "TEMPERATURE_SENSOR"
# Indicates endpoints that control temperature, stand-alone air
# conditioners, or heaters with direct temperature control.
THERMOSTAT = "THERMOSTAT"
# Indicates the endpoint is a television.
# pylint: disable=invalid-name
TV = "TV"
def _capability(interface,
version=3,
supports_deactivation=None,
retrievable=None,
properties_supported=None,
cap_type='AlexaInterface'):
"""Return a Smart Home API capability object.
https://developer.amazon.com/docs/device-apis/alexa-discovery.html#capability-object
There are some additional fields allowed but not implemented here since
we've no use case for them yet:
- proactively_reported
`supports_deactivation` applies only to scenes.
"""
result = {
'type': cap_type,
'interface': interface,
'version': version,
}
if supports_deactivation is not None:
result['supportsDeactivation'] = supports_deactivation
if retrievable is not None:
result['retrievable'] = retrievable
if properties_supported is not None:
result['properties'] = {'supported': properties_supported}
return result
class _EntityCapabilities(object):
def __init__(self, config, entity):
self.config = config
self.entity = entity
def display_categories(self):
"""Return a list of display categories."""
entity_conf = self.config.entity_config.get(self.entity.entity_id, {})
if CONF_DISPLAY_CATEGORIES in entity_conf:
return [entity_conf[CONF_DISPLAY_CATEGORIES]]
return self.default_display_categories()
def default_display_categories(self):
"""Return a list of default display categories.
This can be overridden by the user in the Home Assistant configuration.
See also _DisplayCategory.
"""
raise NotImplementedError
def capabilities(self):
"""Return a list of supported capabilities.
If the returned list is empty, the entity will not be discovered.
You might find _capability() useful.
"""
raise NotImplementedError
class _GenericCapabilities(_EntityCapabilities):
"""A generic, on/off device.
The choice of last resort.
"""
def default_display_categories(self):
return [_DisplayCategory.OTHER]
def capabilities(self):
return [_capability('Alexa.PowerController')]
class _SwitchCapabilities(_EntityCapabilities):
def default_display_categories(self):
return [_DisplayCategory.SWITCH]
def capabilities(self):
return [_capability('Alexa.PowerController')]
class _CoverCapabilities(_EntityCapabilities):
def default_display_categories(self):
return [_DisplayCategory.DOOR]
def capabilities(self):
capabilities = [_capability('Alexa.PowerController')]
supported = self.entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
if supported & cover.SUPPORT_SET_POSITION:
capabilities.append(_capability('Alexa.PercentageController'))
return capabilities
class _LightCapabilities(_EntityCapabilities):
def default_display_categories(self):
return [_DisplayCategory.LIGHT]
def capabilities(self):
capabilities = [_capability('Alexa.PowerController')]
supported = self.entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
if supported & light.SUPPORT_BRIGHTNESS:
capabilities.append(_capability('Alexa.BrightnessController'))
if supported & light.SUPPORT_RGB_COLOR:
capabilities.append(_capability('Alexa.ColorController'))
if supported & light.SUPPORT_XY_COLOR:
capabilities.append(_capability('Alexa.ColorController'))
if supported & light.SUPPORT_COLOR_TEMP:
capabilities.append(
_capability('Alexa.ColorTemperatureController'))
return capabilities
class _FanCapabilities(_EntityCapabilities):
def default_display_categories(self):
return [_DisplayCategory.OTHER]
def capabilities(self):
capabilities = [_capability('Alexa.PowerController')]
supported = self.entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
if supported & fan.SUPPORT_SET_SPEED:
capabilities.append(_capability('Alexa.PercentageController'))
return capabilities
class _LockCapabilities(_EntityCapabilities):
def default_display_categories(self):
return [_DisplayCategory.SMARTLOCK]
def capabilities(self):
return [_capability('Alexa.LockController')]
class _MediaPlayerCapabilities(_EntityCapabilities):
def default_display_categories(self):
return [_DisplayCategory.TV]
def capabilities(self):
capabilities = [_capability('Alexa.PowerController')]
supported = self.entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
if supported & media_player.SUPPORT_VOLUME_SET:
capabilities.append(_capability('Alexa.Speaker'))
playback_features = (media_player.SUPPORT_PLAY |
media_player.SUPPORT_PAUSE |
media_player.SUPPORT_STOP |
media_player.SUPPORT_NEXT_TRACK |
media_player.SUPPORT_PREVIOUS_TRACK)
if supported & playback_features:
capabilities.append(_capability('Alexa.PlaybackController'))
return capabilities
class _SceneCapabilities(_EntityCapabilities):
def default_display_categories(self):
return [_DisplayCategory.SCENE_TRIGGER]
def capabilities(self):
return [_capability('Alexa.SceneController')]
class _ScriptCapabilities(_EntityCapabilities):
def default_display_categories(self):
return [_DisplayCategory.ACTIVITY_TRIGGER]
def capabilities(self):
can_cancel = bool(self.entity.attributes.get('can_cancel'))
return [_capability('Alexa.SceneController',
supports_deactivation=can_cancel)]
class _GroupCapabilities(_EntityCapabilities):
def default_display_categories(self):
return [_DisplayCategory.SCENE_TRIGGER]
def capabilities(self):
return [_capability('Alexa.SceneController',
supports_deactivation=True)]
class _SensorCapabilities(_EntityCapabilities):
def default_display_categories(self):
# although there are other kinds of sensors, all but temperature
# sensors are currently ignored.
return [_DisplayCategory.TEMPERATURE_SENSOR]
def capabilities(self):
capabilities = []
attrs = self.entity.attributes
if attrs.get(CONF_UNIT_OF_MEASUREMENT) in (
TEMP_FAHRENHEIT,
TEMP_CELSIUS,
):
capabilities.append(_capability(
'Alexa.TemperatureSensor',
retrievable=True,
properties_supported=[{'name': 'temperature'}]))
return capabilities
class _UnknownEntityDomainError(Exception):
pass
def _capabilities_for_entity(config, entity):
"""Return an _EntityCapabilities appropriate for given entity.
raises _UnknownEntityDomainError if the given domain is unsupported.
"""
if entity.domain not in _CAPABILITIES_FOR_DOMAIN:
raise _UnknownEntityDomainError()
return _CAPABILITIES_FOR_DOMAIN[entity.domain](config, entity)
_CAPABILITIES_FOR_DOMAIN = {
alert.DOMAIN: _GenericCapabilities,
automation.DOMAIN: _GenericCapabilities,
cover.DOMAIN: _CoverCapabilities,
fan.DOMAIN: _FanCapabilities,
group.DOMAIN: _GroupCapabilities,
input_boolean.DOMAIN: _GenericCapabilities,
light.DOMAIN: _LightCapabilities,
lock.DOMAIN: _LockCapabilities,
media_player.DOMAIN: _MediaPlayerCapabilities,
scene.DOMAIN: _SceneCapabilities,
script.DOMAIN: _ScriptCapabilities,
switch.DOMAIN: _SwitchCapabilities,
sensor.DOMAIN: _SensorCapabilities,
}
class _Cause(object):
"""Possible causes for property changes.
https://developer.amazon.com/docs/smarthome/state-reporting-for-a-smart-home-skill.html#cause-object
"""
# Indicates that the event was caused by a customer interaction with an
# application. For example, a customer switches on a light, or locks a door
# using the Alexa app or an app provided by a device vendor.
APP_INTERACTION = 'APP_INTERACTION'
# Indicates that the event was caused by a physical interaction with an
# endpoint. For example manually switching on a light or manually locking a
# door lock
PHYSICAL_INTERACTION = 'PHYSICAL_INTERACTION'
# Indicates that the event was caused by the periodic poll of an appliance,
# which found a change in value. For example, you might poll a temperature
# sensor every hour, and send the updated temperature to Alexa.
PERIODIC_POLL = 'PERIODIC_POLL'
# Indicates that the event was caused by the application of a device rule.
# For example, a customer configures a rule to switch on a light if a
# motion sensor detects motion. In this case, Alexa receives an event from
# the motion sensor, and another event from the light to indicate that its
# state change was caused by the rule.
RULE_TRIGGER = 'RULE_TRIGGER'
# Indicates that the event was caused by a voice interaction with Alexa.
# For example a user speaking to their Echo device.
VOICE_INTERACTION = 'VOICE_INTERACTION'
class Config:
"""Hold the configuration for Alexa."""
@ -80,6 +379,52 @@ class Config:
self.entity_config = entity_config or {}
@ha.callback
def async_setup(hass, config):
"""Activate Smart Home functionality of Alexa component.
This is optional, triggered by having a `smart_home:` sub-section in the
alexa configuration.
Even if that's disabled, the functionality in this module may still be used
by the cloud component which will call async_handle_message directly.
"""
smart_home_config = Config(
should_expose=config[CONF_FILTER],
entity_config=config.get(CONF_ENTITY_CONFIG),
)
hass.http.register_view(SmartHomeView(smart_home_config))
class SmartHomeView(http.HomeAssistantView):
"""Expose Smart Home v3 payload interface via HTTP POST."""
url = SMART_HOME_HTTP_ENDPOINT
name = 'api:alexa:smart_home'
def __init__(self, smart_home_config):
"""Initialize."""
self.smart_home_config = smart_home_config
@asyncio.coroutine
def post(self, request):
"""Handle Alexa Smart Home requests.
The Smart Home API requires the endpoint to be implemented in AWS
Lambda, which will need to forward the requests to here and pass back
the response.
"""
hass = request.app['hass']
message = yield from request.json()
_LOGGER.debug("Received Alexa Smart Home request: %s", message)
response = yield from async_handle_message(
hass, self.smart_home_config, message)
_LOGGER.debug("Sending Alexa Smart Home response: %s", response)
return b'' if response is None else self.json(response)
@asyncio.coroutine
def async_handle_message(hass, config, message):
"""Handle incoming API messages."""
@ -100,7 +445,11 @@ def async_handle_message(hass, config, message):
return (yield from funct_ref(hass, config, message))
def api_message(request, name='Response', namespace='Alexa', payload=None):
def api_message(request,
name='Response',
namespace='Alexa',
payload=None,
context=None):
"""Create a API formatted response message.
Async friendly.
@ -128,6 +477,9 @@ def api_message(request, name='Response', namespace='Alexa', payload=None):
if API_ENDPOINT in request:
response[API_EVENT][API_ENDPOINT] = request[API_ENDPOINT].copy()
if context is not None:
response[API_CONTEXT] = context
return response
@ -159,9 +511,9 @@ def async_api_discovery(hass, config, request):
entity.entity_id)
continue
class_data = MAPPING_COMPONENT.get(entity.domain)
if not class_data:
try:
entity_capabilities = _capabilities_for_entity(config, entity)
except _UnknownEntityDomainError:
continue
entity_conf = config.entity_config.get(entity.entity_id, {})
@ -174,40 +526,21 @@ def async_api_discovery(hass, config, request):
scene_fmt = '{} (Scene connected via Home Assistant)'
description = scene_fmt.format(description)
display_categories = entity_conf.get(CONF_DISPLAY_CATEGORIES,
class_data[0])
endpoint = {
'displayCategories': [display_categories],
'displayCategories': entity_capabilities.display_categories(),
'additionalApplianceDetails': {},
'endpointId': entity.entity_id.replace('.', '#'),
'friendlyName': friendly_name,
'description': description,
'manufacturerName': 'Home Assistant',
}
actions = set()
# static actions
if class_data[1]:
actions |= set(class_data[1])
# dynamic actions
if class_data[2]:
supported = entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
for feature, action_name in class_data[2].items():
if feature & supported > 0:
actions.add(action_name)
# Write action into capabilities
capabilities = []
for action in actions:
capabilities.append({
'type': 'AlexaInterface',
'interface': action,
'version': 3,
})
endpoint['capabilities'] = capabilities
alexa_capabilities = entity_capabilities.capabilities()
if not alexa_capabilities:
_LOGGER.debug("Not exposing %s because it has no capabilities",
entity.entity_id)
continue
endpoint['capabilities'] = alexa_capabilities
discovery_endpoints.append(endpoint)
return api_message(
@ -216,7 +549,7 @@ def async_api_discovery(hass, config, request):
def extract_entity(funct):
"""Decorator for extract entity object from request."""
"""Decorate for extract entity object from request."""
@asyncio.coroutine
def async_api_entity_wrapper(hass, config, request):
"""Process a turn on request."""
@ -240,8 +573,6 @@ def extract_entity(funct):
def async_api_turn_on(hass, config, request, entity):
"""Process a turn on request."""
domain = entity.domain
if entity.domain == group.DOMAIN:
domain = ha.DOMAIN
service = SERVICE_TURN_ON
if entity.domain == cover.DOMAIN:
@ -379,7 +710,7 @@ def async_api_decrease_color_temp(hass, config, request, entity):
@extract_entity
@asyncio.coroutine
def async_api_increase_color_temp(hass, config, request, entity):
"""Process a increase color temperature request."""
"""Process an increase color temperature request."""
current = int(entity.attributes.get(light.ATTR_COLOR_TEMP))
min_mireds = int(entity.attributes.get(light.ATTR_MIN_MIREDS))
@ -396,12 +727,54 @@ def async_api_increase_color_temp(hass, config, request, entity):
@extract_entity
@asyncio.coroutine
def async_api_activate(hass, config, request, entity):
"""Process a activate request."""
yield from hass.services.async_call(entity.domain, SERVICE_TURN_ON, {
"""Process an activate request."""
if entity.domain == group.DOMAIN:
domain = ha.DOMAIN
else:
domain = entity.domain
yield from hass.services.async_call(domain, SERVICE_TURN_ON, {
ATTR_ENTITY_ID: entity.entity_id
}, blocking=False)
return api_message(request)
payload = {
'cause': {'type': _Cause.VOICE_INTERACTION},
'timestamp': '%sZ' % (datetime.utcnow().isoformat(),)
}
return api_message(
request,
name='ActivationStarted',
namespace='Alexa.SceneController',
payload=payload,
)
@HANDLERS.register(('Alexa.SceneController', 'Deactivate'))
@extract_entity
@asyncio.coroutine
def async_api_deactivate(hass, config, request, entity):
"""Process a deactivate request."""
if entity.domain == group.DOMAIN:
domain = ha.DOMAIN
else:
domain = entity.domain
yield from hass.services.async_call(domain, SERVICE_TURN_OFF, {
ATTR_ENTITY_ID: entity.entity_id
}, blocking=False)
payload = {
'cause': {'type': _Cause.VOICE_INTERACTION},
'timestamp': '%sZ' % (datetime.utcnow().isoformat(),)
}
return api_message(
request,
name='DeactivationStarted',
namespace='Alexa.SceneController',
payload=payload,
)
@HANDLERS.register(('Alexa.PercentageController', 'SetPercentage'))
@ -653,3 +1026,25 @@ def async_api_previous(hass, config, request, entity):
data, blocking=False)
return api_message(request)
@HANDLERS.register(('Alexa', 'ReportState'))
@extract_entity
@asyncio.coroutine
def async_api_reportstate(hass, config, request, entity):
"""Process a ReportState request."""
unit = entity.attributes[CONF_UNIT_OF_MEASUREMENT]
temp_property = {
'namespace': 'Alexa.TemperatureSensor',
'name': 'temperature',
'value': {
'value': float(entity.state),
'scale': API_TEMP_UNITS[unit],
},
}
return api_message(
request,
name='StateReport',
context={'properties': [temp_property]}
)

View File

@ -7,13 +7,14 @@ https://home-assistant.io/components/apple_tv/
import asyncio
import logging
from typing import Sequence, TypeVar, Union
import voluptuous as vol
from typing import Union, TypeVar, Sequence
from homeassistant.const import (CONF_HOST, CONF_NAME, ATTR_ENTITY_ID)
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers import discovery
from homeassistant.components.discovery import SERVICE_APPLE_TV
from homeassistant.const import ATTR_ENTITY_ID, CONF_HOST, CONF_NAME
from homeassistant.helpers import discovery
from homeassistant.helpers.aiohttp_client import async_get_clientsession
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['pyatv==0.3.9']
@ -59,9 +60,9 @@ CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.All(ensure_list, [vol.Schema({
vol.Required(CONF_HOST): cv.string,
vol.Required(CONF_LOGIN_ID): cv.string,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_CREDENTIALS, default=None): cv.string,
vol.Optional(CONF_START_OFF, default=False): cv.boolean
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_START_OFF, default=False): cv.boolean,
})])
}, extra=vol.ALLOW_EXTRA)
@ -140,7 +141,7 @@ def async_setup(hass, config):
@asyncio.coroutine
def async_service_handler(service):
"""Handler for service calls."""
"""Handle service calls."""
entity_ids = service.data.get(ATTR_ENTITY_ID)
if service.service == SERVICE_SCAN:
@ -167,7 +168,7 @@ def async_setup(hass, config):
@asyncio.coroutine
def atv_discovered(service, info):
"""Setup an Apple TV that was auto discovered."""
"""Set up an Apple TV that was auto discovered."""
yield from _setup_atv(hass, {
CONF_NAME: info['name'],
CONF_HOST: info['host'],
@ -194,7 +195,7 @@ def async_setup(hass, config):
@asyncio.coroutine
def _setup_atv(hass, atv_config):
"""Setup an Apple TV."""
"""Set up an Apple TV."""
import pyatv
name = atv_config.get(CONF_NAME)
host = atv_config.get(CONF_HOST)
@ -245,7 +246,7 @@ class AppleTVPowerManager:
@property
def turned_on(self):
"""If device is on or off."""
"""Return true if device is on or off."""
return self._is_on
def set_power_on(self, value):

View File

@ -1,34 +1,34 @@
"""Support for Asterisk Voicemail interface."""
"""
Support for Asterisk Voicemail interface.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/asterisk_mbox/
"""
import logging
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers import discovery
from homeassistant.const import (CONF_HOST,
CONF_PORT, CONF_PASSWORD)
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import (async_dispatcher_connect,
async_dispatcher_send)
from homeassistant.helpers import discovery
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.dispatcher import (
async_dispatcher_connect, async_dispatcher_send)
REQUIREMENTS = ['asterisk_mbox==0.4.0']
SIGNAL_MESSAGE_UPDATE = 'asterisk_mbox.message_updated'
SIGNAL_MESSAGE_REQUEST = 'asterisk_mbox.message_request'
_LOGGER = logging.getLogger(__name__)
DOMAIN = 'asterisk_mbox'
_LOGGER = logging.getLogger(__name__)
SIGNAL_MESSAGE_REQUEST = 'asterisk_mbox.message_request'
SIGNAL_MESSAGE_UPDATE = 'asterisk_mbox.message_updated'
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
vol.Required(CONF_HOST): cv.string,
vol.Required(CONF_PORT): int,
vol.Required(CONF_PASSWORD): cv.string,
vol.Required(CONF_PORT): int,
}),
}, extra=vol.ALLOW_EXTRA)
@ -43,7 +43,7 @@ def setup(hass, config):
hass.data[DOMAIN] = AsteriskData(hass, host, port, password)
discovery.load_platform(hass, "mailbox", DOMAIN, {}, config)
discovery.load_platform(hass, 'mailbox', DOMAIN, {}, config)
return True
@ -68,15 +68,14 @@ class AsteriskData(object):
from asterisk_mbox.commands import CMD_MESSAGE_LIST
if command == CMD_MESSAGE_LIST:
_LOGGER.info("AsteriskVM sent updated message list")
self.messages = sorted(msg,
key=lambda item: item['info']['origtime'],
reverse=True)
async_dispatcher_send(self.hass, SIGNAL_MESSAGE_UPDATE,
self.messages)
_LOGGER.debug("AsteriskVM sent updated message list")
self.messages = sorted(
msg, key=lambda item: item['info']['origtime'], reverse=True)
async_dispatcher_send(
self.hass, SIGNAL_MESSAGE_UPDATE, self.messages)
@callback
def _request_messages(self):
"""Handle changes to the mailbox."""
_LOGGER.info("Requesting message list")
_LOGGER.debug("Requesting message list")
self.client.messages()

View File

@ -338,10 +338,9 @@ class AutomationEntity(ToggleEntity):
yield from self.async_update_ha_state()
@asyncio.coroutine
def async_remove(self):
"""Remove automation from HASS."""
def async_will_remove_from_hass(self):
"""Remove listeners when removing automation from HASS."""
yield from self.async_turn_off()
yield from super().async_remove()
@asyncio.coroutine
def async_enable(self):

View File

@ -4,24 +4,21 @@ Support for Axis devices.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/axis/
"""
import logging
import voluptuous as vol
from homeassistant.components.discovery import SERVICE_AXIS
from homeassistant.const import (ATTR_LOCATION, ATTR_TRIPPED,
CONF_EVENT, CONF_HOST, CONF_INCLUDE,
CONF_NAME, CONF_PASSWORD, CONF_PORT,
CONF_TRIGGER_TIME, CONF_USERNAME,
EVENT_HOMEASSISTANT_STOP)
from homeassistant.const import (
ATTR_LOCATION, ATTR_TRIPPED, CONF_EVENT, CONF_HOST, CONF_INCLUDE,
CONF_NAME, CONF_PASSWORD, CONF_PORT, CONF_TRIGGER_TIME, CONF_USERNAME,
EVENT_HOMEASSISTANT_STOP)
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers import discovery
from homeassistant.helpers.dispatcher import dispatcher_send
from homeassistant.helpers.entity import Entity
from homeassistant.util.json import load_json, save_json
REQUIREMENTS = ['axis==14']
_LOGGER = logging.getLogger(__name__)
@ -81,10 +78,10 @@ def request_configuration(hass, config, name, host, serialnumber):
configurator = hass.components.configurator
def configuration_callback(callback_data):
"""Called when config is submitted."""
"""Call when configuration is submitted."""
if CONF_INCLUDE not in callback_data:
configurator.notify_errors(request_id,
"Functionality mandatory.")
configurator.notify_errors(
request_id, "Functionality mandatory.")
return False
callback_data[CONF_INCLUDE] = callback_data[CONF_INCLUDE].split()
@ -96,18 +93,20 @@ def request_configuration(hass, config, name, host, serialnumber):
try:
device_config = DEVICE_SCHEMA(callback_data)
except vol.Invalid:
configurator.notify_errors(request_id,
"Bad input, please check spelling.")
configurator.notify_errors(
request_id, "Bad input, please check spelling.")
return False
if setup_device(hass, config, device_config):
del device_config['events']
del device_config['signal']
config_file = load_json(hass.config.path(CONFIG_FILE))
config_file[serialnumber] = dict(device_config)
save_json(hass.config.path(CONFIG_FILE), config_file)
configurator.request_done(request_id)
else:
configurator.notify_errors(request_id,
"Failed to register, please try again.")
configurator.notify_errors(
request_id, "Failed to register, please try again.")
return False
title = '{} ({})'.format(name, host)
@ -145,7 +144,7 @@ def request_configuration(hass, config, name, host, serialnumber):
def setup(hass, config):
"""Common setup for Axis devices."""
"""Set up for Axis devices."""
def _shutdown(call): # pylint: disable=unused-argument
"""Stop the event stream on shutdown."""
for serialnumber, device in AXIS_DEVICES.items():
@ -155,7 +154,7 @@ def setup(hass, config):
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, _shutdown)
def axis_device_discovered(service, discovery_info):
"""Called when axis devices has been found."""
"""Call when axis devices has been found."""
host = discovery_info[CONF_HOST]
name = discovery_info['hostname']
serialnumber = discovery_info['properties']['macaddress']
@ -171,8 +170,8 @@ def setup(hass, config):
_LOGGER.error("Bad data from %s. %s", CONFIG_FILE, err)
return False
if not setup_device(hass, config, device_config):
_LOGGER.error("Couldn\'t set up %s",
device_config[CONF_NAME])
_LOGGER.error(
"Couldn't set up %s", device_config[CONF_NAME])
else:
# New device, create configuration request for UI
request_configuration(hass, config, name, host, serialnumber)
@ -191,7 +190,7 @@ def setup(hass, config):
if CONF_NAME not in device_config:
device_config[CONF_NAME] = device
if not setup_device(hass, config, device_config):
_LOGGER.error("Couldn\'t set up %s", device_config[CONF_NAME])
_LOGGER.error("Couldn't set up %s", device_config[CONF_NAME])
def vapix_service(call):
"""Service to send a message."""
@ -203,23 +202,21 @@ def setup(hass, config):
call.data[SERVICE_PARAM])
hass.bus.fire(SERVICE_VAPIX_CALL_RESPONSE, response)
return True
_LOGGER.info("Couldn\'t find device %s", call.data[CONF_NAME])
_LOGGER.info("Couldn't find device %s", call.data[CONF_NAME])
return False
# Register service with Home Assistant.
hass.services.register(DOMAIN,
SERVICE_VAPIX_CALL,
vapix_service,
schema=SERVICE_SCHEMA)
hass.services.register(
DOMAIN, SERVICE_VAPIX_CALL, vapix_service, schema=SERVICE_SCHEMA)
return True
def setup_device(hass, config, device_config):
"""Set up device."""
"""Set up an Axis device."""
from axis import AxisDevice
def signal_callback(action, event):
"""Callback to configure events when initialized on event stream."""
"""Call to configure events when initialized on event stream."""
if action == 'add':
event_config = {
CONF_EVENT: event,
@ -228,11 +225,8 @@ def setup_device(hass, config, device_config):
CONF_TRIGGER_TIME: device_config[CONF_TRIGGER_TIME]
}
component = event.event_platform
discovery.load_platform(hass,
component,
DOMAIN,
event_config,
config)
discovery.load_platform(
hass, component, DOMAIN, event_config, config)
event_types = list(filter(lambda x: x in device_config[CONF_INCLUDE],
EVENT_TYPES))
@ -243,7 +237,7 @@ def setup_device(hass, config, device_config):
if device.serial_number is None:
# If there is no serial number a connection could not be made
_LOGGER.error("Couldn\'t connect to %s", device_config[CONF_HOST])
_LOGGER.error("Couldn't connect to %s", device_config[CONF_HOST])
return False
for component in device_config[CONF_INCLUDE]:
@ -255,11 +249,8 @@ def setup_device(hass, config, device_config):
CONF_USERNAME: device_config[CONF_USERNAME],
CONF_PASSWORD: device_config[CONF_PASSWORD]
}
discovery.load_platform(hass,
component,
DOMAIN,
camera_config,
config)
discovery.load_platform(
hass, component, DOMAIN, camera_config, config)
AXIS_DEVICES[device.serial_number] = device
if event_types:
@ -273,9 +264,9 @@ class AxisDeviceEvent(Entity):
def __init__(self, event_config):
"""Initialize the event."""
self.axis_event = event_config[CONF_EVENT]
self._name = '{}_{}_{}'.format(event_config[CONF_NAME],
self.axis_event.event_type,
self.axis_event.id)
self._name = '{}_{}_{}'.format(
event_config[CONF_NAME], self.axis_event.event_type,
self.axis_event.id)
self.location = event_config[ATTR_LOCATION]
self.axis_event.callback = self._update_callback
@ -296,7 +287,7 @@ class AxisDeviceEvent(Entity):
@property
def should_poll(self):
"""No polling needed."""
"""Return the polling state. No polling needed."""
return False
@property

View File

@ -3,23 +3,22 @@ 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
import voluptuous as vol
from homeassistant.components.ads import CONF_ADS_VAR, DATA_ADS
from homeassistant.components.binary_sensor import (
DEVICE_CLASSES_SCHEMA, PLATFORM_SCHEMA, BinarySensorDevice)
from homeassistant.const import CONF_DEVICE_CLASS, CONF_NAME
import homeassistant.helpers.config_validation as cv
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['ads']
DEFAULT_NAME = 'ADS binary sensor'
DEPENDENCIES = ['ads']
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_ADS_VAR): cv.string,
@ -44,7 +43,7 @@ class AdsBinarySensor(BinarySensorDevice):
"""Representation of ADS binary sensors."""
def __init__(self, ads_hub, name, ads_var, device_class):
"""Initialize AdsBinarySensor entity."""
"""Initialize ADS binary sensor."""
self._name = name
self._state = False
self._device_class = device_class or 'moving'
@ -56,15 +55,13 @@ class AdsBinarySensor(BinarySensorDevice):
"""Register device notification."""
def update(name, value):
"""Handle device notifications."""
_LOGGER.debug('Variable %s changed its value to %d',
name, value)
_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
)
self.ads_var, self._ads_hub.PLCTYPE_BOOL, update)
@property
def name(self):

View File

@ -4,13 +4,12 @@ Support for Axis binary sensors.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.axis/
"""
import logging
from datetime import timedelta
import logging
from homeassistant.components.binary_sensor import (BinarySensorDevice)
from homeassistant.components.axis import (AxisDeviceEvent)
from homeassistant.const import (CONF_TRIGGER_TIME)
from homeassistant.components.axis import AxisDeviceEvent
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.const import CONF_TRIGGER_TIME
from homeassistant.helpers.event import track_point_in_utc_time
from homeassistant.util.dt import utcnow
@ -20,7 +19,7 @@ _LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup Axis device event."""
"""Set up the Axis binary devices."""
add_devices([AxisBinarySensor(hass, discovery_info)], True)
@ -28,7 +27,7 @@ class AxisBinarySensor(AxisDeviceEvent, BinarySensorDevice):
"""Representation of a binary Axis event."""
def __init__(self, hass, event_config):
"""Initialize the binary sensor."""
"""Initialize the Axis binary sensor."""
self.hass = hass
self._state = False
self._delay = event_config[CONF_TRIGGER_TIME]
@ -56,7 +55,7 @@ class AxisBinarySensor(AxisDeviceEvent, BinarySensorDevice):
# Set timer to wait until updating the state
def _delay_update(now):
"""Timer callback for sensor update."""
_LOGGER.debug("%s Called delayed (%s sec) update.",
_LOGGER.debug("%s called delayed (%s sec) update",
self._name, self._delay)
self.schedule_update_ha_state()
self._timer = None

View File

@ -53,7 +53,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
sensors = []
try:
_LOGGER.debug("Initializing Client")
_LOGGER.debug("Initializing client")
client = concord232_client.Client('http://{}:{}'.format(host, port))
client.zones = client.list_zones()
client.last_zone_update = datetime.datetime.now()

View File

@ -4,7 +4,6 @@ Support for deCONZ binary sensor.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.deconz/
"""
import asyncio
from homeassistant.components.binary_sensor import BinarySensorDevice
@ -17,7 +16,7 @@ DEPENDENCIES = ['deconz']
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Setup binary sensor for deCONZ component."""
"""Set up the deCONZ binary sensor."""
if discovery_info is None:
return
@ -25,8 +24,9 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
sensors = hass.data[DECONZ_DATA].sensors
entities = []
for sensor in sensors.values():
if sensor.type in DECONZ_BINARY_SENSOR:
for key in sorted(sensors.keys(), key=int):
sensor = sensors[key]
if sensor and sensor.type in DECONZ_BINARY_SENSOR:
entities.append(DeconzBinarySensor(sensor))
async_add_devices(entities, True)
@ -35,7 +35,7 @@ class DeconzBinarySensor(BinarySensorDevice):
"""Representation of a binary sensor."""
def __init__(self, sensor):
"""Setup sensor and add update callback to get data from websocket."""
"""Set up sensor and add update callback to get data from websocket."""
self._sensor = sensor
@asyncio.coroutine
@ -67,7 +67,7 @@ class DeconzBinarySensor(BinarySensorDevice):
@property
def device_class(self):
"""Class of the sensor."""
"""Return the class of the sensor."""
return self._sensor.sensor_class
@property

View File

@ -238,6 +238,5 @@ class FlicButton(BinarySensorDevice):
import pyflic
if connection_status == pyflic.ConnectionStatus.Disconnected:
_LOGGER.info("Button (%s) disconnected. Reason: %s",
self.address, disconnect_reason)
self.remove()
_LOGGER.warning("Button (%s) disconnected. Reason: %s",
self.address, disconnect_reason)

View File

@ -1,63 +1,63 @@
"""
Support for the Hive devices.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.hive/
"""
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.hive import DATA_HIVE
DEPENDENCIES = ['hive']
DEVICETYPE_DEVICE_CLASS = {'motionsensor': 'motion',
'contactsensor': 'opening'}
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up Hive sensor devices."""
if discovery_info is None:
return
session = hass.data.get(DATA_HIVE)
add_devices([HiveBinarySensorEntity(session, discovery_info)])
class HiveBinarySensorEntity(BinarySensorDevice):
"""Representation of a Hive binary sensor."""
def __init__(self, hivesession, hivedevice):
"""Initialize the hive sensor."""
self.node_id = hivedevice["Hive_NodeID"]
self.node_name = hivedevice["Hive_NodeName"]
self.device_type = hivedevice["HA_DeviceType"]
self.node_device_type = hivedevice["Hive_DeviceType"]
self.session = hivesession
self.data_updatesource = '{}.{}'.format(self.device_type,
self.node_id)
self.session.entities.append(self)
def handle_update(self, updatesource):
"""Handle the new update request."""
if '{}.{}'.format(self.device_type, self.node_id) not in updatesource:
self.schedule_update_ha_state()
@property
def device_class(self):
"""Return the class of this sensor."""
return DEVICETYPE_DEVICE_CLASS.get(self.node_device_type)
@property
def name(self):
"""Return the name of the binary sensor."""
return self.node_name
@property
def is_on(self):
"""Return true if the binary sensor is on."""
return self.session.sensor.get_state(self.node_id,
self.node_device_type)
def update(self):
"""Update all Node data frome Hive."""
self.session.core.update_data(self.node_id)
"""
Support for the Hive devices.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.hive/
"""
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.hive import DATA_HIVE
DEPENDENCIES = ['hive']
DEVICETYPE_DEVICE_CLASS = {'motionsensor': 'motion',
'contactsensor': 'opening'}
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up Hive sensor devices."""
if discovery_info is None:
return
session = hass.data.get(DATA_HIVE)
add_devices([HiveBinarySensorEntity(session, discovery_info)])
class HiveBinarySensorEntity(BinarySensorDevice):
"""Representation of a Hive binary sensor."""
def __init__(self, hivesession, hivedevice):
"""Initialize the hive sensor."""
self.node_id = hivedevice["Hive_NodeID"]
self.node_name = hivedevice["Hive_NodeName"]
self.device_type = hivedevice["HA_DeviceType"]
self.node_device_type = hivedevice["Hive_DeviceType"]
self.session = hivesession
self.data_updatesource = '{}.{}'.format(self.device_type,
self.node_id)
self.session.entities.append(self)
def handle_update(self, updatesource):
"""Handle the new update request."""
if '{}.{}'.format(self.device_type, self.node_id) not in updatesource:
self.schedule_update_ha_state()
@property
def device_class(self):
"""Return the class of this sensor."""
return DEVICETYPE_DEVICE_CLASS.get(self.node_device_type)
@property
def name(self):
"""Return the name of the binary sensor."""
return self.node_name
@property
def is_on(self):
"""Return true if the binary sensor is on."""
return self.session.sensor.get_state(self.node_id,
self.node_device_type)
def update(self):
"""Update all Node data frome Hive."""
self.session.core.update_data(self.node_id)

View File

@ -0,0 +1,95 @@
"""IHC binary sensor platform.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.ihc/
"""
from xml.etree.ElementTree import Element
import voluptuous as vol
from homeassistant.components.binary_sensor import (
BinarySensorDevice, PLATFORM_SCHEMA, DEVICE_CLASSES_SCHEMA)
from homeassistant.components.ihc import (
validate_name, IHC_DATA, IHC_CONTROLLER, IHC_INFO)
from homeassistant.components.ihc.const import CONF_INVERTING
from homeassistant.components.ihc.ihcdevice import IHCDevice
from homeassistant.const import (
CONF_NAME, CONF_TYPE, CONF_ID, CONF_BINARY_SENSORS)
import homeassistant.helpers.config_validation as cv
DEPENDENCIES = ['ihc']
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_BINARY_SENSORS, default=[]):
vol.All(cv.ensure_list, [
vol.All({
vol.Required(CONF_ID): cv.positive_int,
vol.Optional(CONF_NAME): cv.string,
vol.Optional(CONF_TYPE, default=None): DEVICE_CLASSES_SCHEMA,
vol.Optional(CONF_INVERTING, default=False): cv.boolean,
}, validate_name)
])
})
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the IHC binary sensor platform."""
ihc_controller = hass.data[IHC_DATA][IHC_CONTROLLER]
info = hass.data[IHC_DATA][IHC_INFO]
devices = []
if discovery_info:
for name, device in discovery_info.items():
ihc_id = device['ihc_id']
product_cfg = device['product_cfg']
product = device['product']
sensor = IHCBinarySensor(ihc_controller, name, ihc_id, info,
product_cfg[CONF_TYPE],
product_cfg[CONF_INVERTING],
product)
devices.append(sensor)
else:
binary_sensors = config[CONF_BINARY_SENSORS]
for sensor_cfg in binary_sensors:
ihc_id = sensor_cfg[CONF_ID]
name = sensor_cfg[CONF_NAME]
sensor_type = sensor_cfg[CONF_TYPE]
inverting = sensor_cfg[CONF_INVERTING]
sensor = IHCBinarySensor(ihc_controller, name, ihc_id, info,
sensor_type, inverting)
devices.append(sensor)
add_devices(devices)
class IHCBinarySensor(IHCDevice, BinarySensorDevice):
"""IHC Binary Sensor.
The associated IHC resource can be any in or output from a IHC product
or function block, but it must be a boolean ON/OFF resources.
"""
def __init__(self, ihc_controller, name, ihc_id: int, info: bool,
sensor_type: str, inverting: bool, product: Element=None):
"""Initialize the IHC binary sensor."""
super().__init__(ihc_controller, name, ihc_id, info, product)
self._state = None
self._sensor_type = sensor_type
self.inverting = inverting
@property
def device_class(self):
"""Return the class of this sensor."""
return self._sensor_type
@property
def is_on(self):
"""Return true if the binary sensor is on/open."""
return self._state
def on_ihc_change(self, ihc_id, value):
"""IHC resource has changed."""
if self.inverting:
self._state = not value
else:
self._state = value
self.schedule_update_ha_state()

View File

@ -5,12 +5,13 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.knx/
"""
import asyncio
import voluptuous as vol
from homeassistant.components.knx import DATA_KNX, ATTR_DISCOVER_DEVICES, \
KNXAutomation
from homeassistant.components.binary_sensor import PLATFORM_SCHEMA, \
BinarySensorDevice
from homeassistant.components.binary_sensor import (
PLATFORM_SCHEMA, BinarySensorDevice)
from homeassistant.components.knx import (
ATTR_DISCOVER_DEVICES, DATA_KNX, KNXAutomation)
from homeassistant.const import CONF_NAME
from homeassistant.core import callback
import homeassistant.helpers.config_validation as cv
@ -53,20 +54,16 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices,
discovery_info=None):
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Set up binary sensor(s) for KNX platform."""
if DATA_KNX not in hass.data \
or not hass.data[DATA_KNX].initialized:
return False
if DATA_KNX not in hass.data or not hass.data[DATA_KNX].initialized:
return
if discovery_info is not None:
async_add_devices_discovery(hass, discovery_info, async_add_devices)
else:
async_add_devices_config(hass, config, async_add_devices)
return True
@callback
def async_add_devices_discovery(hass, discovery_info, async_add_devices):
@ -80,7 +77,7 @@ def async_add_devices_discovery(hass, discovery_info, async_add_devices):
@callback
def async_add_devices_config(hass, config, async_add_devices):
"""Set up binary senor for KNX platform configured within plattform."""
"""Set up binary senor for KNX platform configured within platform."""
name = config.get(CONF_NAME)
import xknx
binary_sensor = xknx.devices.BinarySensor(
@ -108,7 +105,7 @@ class KNXBinarySensor(BinarySensorDevice):
"""Representation of a KNX binary sensor."""
def __init__(self, hass, device):
"""Initialization of KNXBinarySensor."""
"""Initialize of KNX binary sensor."""
self.device = device
self.hass = hass
self.async_register_callbacks()
@ -119,7 +116,7 @@ class KNXBinarySensor(BinarySensorDevice):
"""Register callbacks to update hass after device was changed."""
@asyncio.coroutine
def after_update_callback(device):
"""Callback after device was updated."""
"""Call after device was updated."""
# pylint: disable=unused-argument
yield from self.async_update_ha_state()
self.device.register_device_updated_cb(after_update_callback)

View File

@ -36,7 +36,7 @@ class MaxCubeShutter(BinarySensorDevice):
def __init__(self, hass, name, rf_address):
"""Initialize MAX! Cube BinarySensorDevice."""
self._name = name
self._sensor_type = 'opening'
self._sensor_type = 'window'
self._rf_address = rf_address
self._cubehandle = hass.data[MAXCUBE_HANDLE]
self._state = STATE_UNKNOWN

View File

@ -0,0 +1,85 @@
"""Support for MyChevy sensors.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.mychevy/
"""
import asyncio
import logging
from homeassistant.components.mychevy import (
EVBinarySensorConfig, DOMAIN as MYCHEVY_DOMAIN, UPDATE_TOPIC
)
from homeassistant.components.binary_sensor import (
ENTITY_ID_FORMAT, BinarySensorDevice)
from homeassistant.core import callback
from homeassistant.util import slugify
_LOGGER = logging.getLogger(__name__)
SENSORS = [
EVBinarySensorConfig("Plugged In", "plugged_in", "plug")
]
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Set up the MyChevy sensors."""
if discovery_info is None:
return
sensors = []
hub = hass.data[MYCHEVY_DOMAIN]
for sconfig in SENSORS:
sensors.append(EVBinarySensor(hub, sconfig))
async_add_devices(sensors)
class EVBinarySensor(BinarySensorDevice):
"""Base EVSensor class.
The only real difference between sensors is which units and what
attribute from the car object they are returning. All logic can be
built with just setting subclass attributes.
"""
def __init__(self, connection, config):
"""Initialize sensor with car connection."""
self._conn = connection
self._name = config.name
self._attr = config.attr
self._type = config.device_class
self._is_on = None
self.entity_id = ENTITY_ID_FORMAT.format(
'{}_{}'.format(MYCHEVY_DOMAIN, slugify(self._name)))
@property
def name(self):
"""Return the name."""
return self._name
@property
def is_on(self):
"""Return if on."""
return self._is_on
@asyncio.coroutine
def async_added_to_hass(self):
"""Register callbacks."""
self.hass.helpers.dispatcher.async_dispatcher_connect(
UPDATE_TOPIC, self.async_update_callback)
@callback
def async_update_callback(self):
"""Update state."""
if self._conn.car is not None:
self._is_on = getattr(self._conn.car, self._attr, None)
self.async_schedule_update_ha_state()
@property
def should_poll(self):
"""Return the polling state."""
return False

View File

@ -5,21 +5,20 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.mysensors/
"""
from homeassistant.components import mysensors
from homeassistant.components.binary_sensor import (DEVICE_CLASSES, DOMAIN,
BinarySensorDevice)
from homeassistant.components.binary_sensor import (
DEVICE_CLASSES, DOMAIN, BinarySensorDevice)
from homeassistant.const import STATE_ON
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the mysensors platform for binary sensors."""
"""Set up the MySensors platform for binary sensors."""
mysensors.setup_mysensors_platform(
hass, DOMAIN, discovery_info, MySensorsBinarySensor,
add_devices=add_devices)
class MySensorsBinarySensor(
mysensors.MySensorsEntity, BinarySensorDevice):
"""Represent the value of a MySensors Binary Sensor child node."""
class MySensorsBinarySensor(mysensors.MySensorsEntity, BinarySensorDevice):
"""Representation of a MySensors Binary Sensor child node."""
@property
def is_on(self):

View File

@ -7,7 +7,7 @@ https://home-assistant.io/components/binary_sensor.mystrom/
import asyncio
import logging
from homeassistant.components.binary_sensor import (BinarySensorDevice, DOMAIN)
from homeassistant.components.binary_sensor import DOMAIN, BinarySensorDevice
from homeassistant.components.http import HomeAssistantView
from homeassistant.const import HTTP_UNPROCESSABLE_ENTITY
@ -37,7 +37,7 @@ class MyStromView(HomeAssistantView):
@asyncio.coroutine
def get(self, request):
"""The GET request received from a myStrom button."""
"""Handle the GET request received from a myStrom button."""
res = yield from self._handle(request.app['hass'], request.query)
return res

View File

@ -5,18 +5,17 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.raspihats/
"""
import logging
import voluptuous as vol
from homeassistant.const import (
CONF_NAME, CONF_DEVICE_CLASS, DEVICE_DEFAULT_NAME
)
import homeassistant.helpers.config_validation as cv
from homeassistant.components.binary_sensor import (
PLATFORM_SCHEMA, BinarySensorDevice
)
PLATFORM_SCHEMA, BinarySensorDevice)
from homeassistant.components.raspihats import (
CONF_I2C_HATS, CONF_BOARD, CONF_ADDRESS, CONF_CHANNELS, CONF_INDEX,
CONF_INVERT_LOGIC, I2C_HAT_NAMES, I2C_HATS_MANAGER, I2CHatsException
)
CONF_ADDRESS, CONF_BOARD, CONF_CHANNELS, CONF_I2C_HATS, CONF_INDEX,
CONF_INVERT_LOGIC, I2C_HAT_NAMES, I2C_HATS_MANAGER, I2CHatsException)
from homeassistant.const import (
CONF_DEVICE_CLASS, CONF_NAME, DEVICE_DEFAULT_NAME)
import homeassistant.helpers.config_validation as cv
_LOGGER = logging.getLogger(__name__)
@ -45,7 +44,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the raspihats binary_sensor devices."""
"""Set up the raspihats binary_sensor devices."""
I2CHatBinarySensor.I2C_HATS_MANAGER = hass.data[I2C_HATS_MANAGER]
binary_sensors = []
i2c_hat_configs = config.get(CONF_I2C_HATS)
@ -65,39 +64,32 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
)
)
except I2CHatsException as ex:
_LOGGER.error(
"Failed to register " + board + "I2CHat@" + hex(address) + " "
+ str(ex)
)
_LOGGER.error("Failed to register %s I2CHat@%s %s",
board, hex(address), str(ex))
add_devices(binary_sensors)
class I2CHatBinarySensor(BinarySensorDevice):
"""Represents a binary sensor that uses a I2C-HAT digital input."""
"""Representation of a binary sensor that uses a I2C-HAT digital input."""
I2C_HATS_MANAGER = None
def __init__(self, address, channel, name, invert_logic, device_class):
"""Initialize sensor."""
"""Initialize the raspihats sensor."""
self._address = address
self._channel = channel
self._name = name or DEVICE_DEFAULT_NAME
self._invert_logic = invert_logic
self._device_class = device_class
self._state = self.I2C_HATS_MANAGER.read_di(
self._address,
self._channel
)
self._address, self._channel)
def online_callback():
"""Callback fired when board is online."""
"""Call fired when board is online."""
self.schedule_update_ha_state()
self.I2C_HATS_MANAGER.register_online_callback(
self._address,
self._channel,
online_callback
)
self._address, self._channel, online_callback)
def edge_callback(state):
"""Read digital input state."""
@ -105,10 +97,7 @@ class I2CHatBinarySensor(BinarySensorDevice):
self.schedule_update_ha_state()
self.I2C_HATS_MANAGER.register_di_callback(
self._address,
self._channel,
edge_callback
)
self._address, self._channel, edge_callback)
@property
def device_class(self):
@ -122,7 +111,7 @@ class I2CHatBinarySensor(BinarySensorDevice):
@property
def should_poll(self):
"""Polling not needed for this sensor."""
"""No polling needed for this sensor."""
return False
@property

View File

@ -1,38 +1,36 @@
"""
Support for RFXtrx binary sensors.
Lighting4 devices (sensors based on PT2262 encoder) are supported and
tested. Other types may need some work.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.rfxtrx/
"""
import logging
import voluptuous as vol
from homeassistant.const import (
CONF_DEVICE_CLASS, CONF_COMMAND_ON, CONF_COMMAND_OFF, CONF_NAME)
from homeassistant.components import rfxtrx
from homeassistant.helpers import event as evt
from homeassistant.helpers import config_validation as cv
from homeassistant.components.binary_sensor import (
BinarySensorDevice, PLATFORM_SCHEMA)
PLATFORM_SCHEMA, DEVICE_CLASSES_SCHEMA, BinarySensorDevice)
from homeassistant.components.rfxtrx import (
ATTR_NAME, CONF_AUTOMATIC_ADD, CONF_FIRE_EVENT,
CONF_OFF_DELAY, CONF_DATA_BITS, CONF_DEVICES)
from homeassistant.util import slugify
ATTR_NAME, CONF_AUTOMATIC_ADD, CONF_DATA_BITS, CONF_DEVICES,
CONF_FIRE_EVENT, CONF_OFF_DELAY)
from homeassistant.const import (
CONF_COMMAND_OFF, CONF_COMMAND_ON, CONF_DEVICE_CLASS, CONF_NAME)
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers import event as evt
from homeassistant.util import dt as dt_util
DEPENDENCIES = ["rfxtrx"]
from homeassistant.util import slugify
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['rfxtrx']
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_DEVICES, default={}): {
cv.string: vol.Schema({
vol.Optional(CONF_NAME): cv.string,
vol.Optional(CONF_DEVICE_CLASS): cv.string,
vol.Optional(CONF_NAME, default=None): cv.string,
vol.Optional(CONF_DEVICE_CLASS, default=None):
DEVICE_CLASSES_SCHEMA,
vol.Optional(CONF_FIRE_EVENT, default=False): cv.boolean,
vol.Optional(CONF_OFF_DELAY, default=None):
vol.Any(cv.time_period, cv.positive_timedelta),
@ -45,8 +43,8 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
}, extra=vol.ALLOW_EXTRA)
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
"""Setup the Binary Sensor platform to rfxtrx."""
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the Binary Sensor platform to RFXtrx."""
import RFXtrx as rfxtrxmod
sensors = []
@ -58,29 +56,26 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
continue
if entity[CONF_DATA_BITS] is not None:
_LOGGER.debug("Masked device id: %s",
rfxtrx.get_pt2262_deviceid(device_id,
entity[CONF_DATA_BITS]))
_LOGGER.debug(
"Masked device id: %s", rfxtrx.get_pt2262_deviceid(
device_id, entity[CONF_DATA_BITS]))
_LOGGER.debug("Add %s rfxtrx.binary_sensor (class %s)",
entity[ATTR_NAME], entity[CONF_DEVICE_CLASS])
device = RfxtrxBinarySensor(event, entity[ATTR_NAME],
entity[CONF_DEVICE_CLASS],
entity[CONF_FIRE_EVENT],
entity[CONF_OFF_DELAY],
entity[CONF_DATA_BITS],
entity[CONF_COMMAND_ON],
entity[CONF_COMMAND_OFF])
device = RfxtrxBinarySensor(
event, entity[ATTR_NAME], entity[CONF_DEVICE_CLASS],
entity[CONF_FIRE_EVENT], entity[CONF_OFF_DELAY],
entity[CONF_DATA_BITS], entity[CONF_COMMAND_ON],
entity[CONF_COMMAND_OFF])
device.hass = hass
sensors.append(device)
rfxtrx.RFX_DEVICES[device_id] = device
add_devices_callback(sensors)
add_devices(sensors)
# pylint: disable=too-many-branches
def binary_sensor_update(event):
"""Callback for control updates from the RFXtrx gateway."""
"""Call for control updates from the RFXtrx gateway."""
if not isinstance(event, rfxtrxmod.ControlEvent):
return
@ -100,29 +95,26 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
poss_dev = rfxtrx.find_possible_pt2262_device(device_id)
if poss_dev is not None:
poss_id = slugify(poss_dev.event.device.id_string.lower())
_LOGGER.debug("Found possible matching deviceid %s.",
poss_id)
_LOGGER.debug(
"Found possible matching device ID: %s", poss_id)
pkt_id = "".join("{0:02x}".format(x) for x in event.data)
sensor = RfxtrxBinarySensor(event, pkt_id)
sensor.hass = hass
rfxtrx.RFX_DEVICES[device_id] = sensor
add_devices_callback([sensor])
_LOGGER.info("Added binary sensor %s "
"(Device_id: %s Class: %s Sub: %s)",
pkt_id,
slugify(event.device.id_string.lower()),
event.device.__class__.__name__,
event.device.subtype)
add_devices([sensor])
_LOGGER.info(
"Added binary sensor %s (Device ID: %s Class: %s Sub: %s)",
pkt_id, slugify(event.device.id_string.lower()),
event.device.__class__.__name__, event.device.subtype)
elif not isinstance(sensor, RfxtrxBinarySensor):
return
else:
_LOGGER.debug("Binary sensor update "
"(Device_id: %s Class: %s Sub: %s)",
slugify(event.device.id_string.lower()),
event.device.__class__.__name__,
event.device.subtype)
_LOGGER.debug(
"Binary sensor update (Device ID: %s Class: %s Sub: %s)",
slugify(event.device.id_string.lower()),
event.device.__class__.__name__, event.device.subtype)
if sensor.is_lighting4:
if sensor.data_bits is not None:
@ -142,22 +134,20 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
sensor.update_state(False)
sensor.delay_listener = evt.track_point_in_time(
hass, off_delay_listener, dt_util.utcnow() + sensor.off_delay
)
hass, off_delay_listener, dt_util.utcnow() + sensor.off_delay)
# Subscribe to main rfxtrx events
# Subscribe to main RFXtrx events
if binary_sensor_update not in rfxtrx.RECEIVED_EVT_SUBSCRIBERS:
rfxtrx.RECEIVED_EVT_SUBSCRIBERS.append(binary_sensor_update)
# pylint: disable=too-many-instance-attributes,too-many-arguments
class RfxtrxBinarySensor(BinarySensorDevice):
"""An Rfxtrx binary sensor."""
"""A representation of a RFXtrx binary sensor."""
def __init__(self, event, name, device_class=None,
should_fire=False, off_delay=None, data_bits=None,
cmd_on=None, cmd_off=None):
"""Initialize the sensor."""
"""Initialize the RFXtrx sensor."""
self.event = event
self._name = name
self._should_fire_event = should_fire
@ -172,8 +162,7 @@ class RfxtrxBinarySensor(BinarySensorDevice):
if data_bits is not None:
self._masked_id = rfxtrx.get_pt2262_deviceid(
event.device.id_string.lower(),
data_bits)
event.device.id_string.lower(), data_bits)
else:
self._masked_id = None

View File

@ -8,18 +8,17 @@ import logging
import voluptuous as vol
import homeassistant.components.rpi_pfio as rpi_pfio
from homeassistant.components.binary_sensor import (
BinarySensorDevice, PLATFORM_SCHEMA)
from homeassistant.const import DEVICE_DEFAULT_NAME
PLATFORM_SCHEMA, BinarySensorDevice)
import homeassistant.components.rpi_pfio as rpi_pfio
from homeassistant.const import CONF_NAME, DEVICE_DEFAULT_NAME
import homeassistant.helpers.config_validation as cv
_LOGGER = logging.getLogger(__name__)
ATTR_NAME = 'name'
ATTR_INVERT_LOGIC = 'invert_logic'
ATTR_SETTLE_TIME = 'settle_time'
CONF_INVERT_LOGIC = 'invert_logic'
CONF_PORTS = 'ports'
CONF_SETTLE_TIME = 'settle_time'
DEFAULT_INVERT_LOGIC = False
DEFAULT_SETTLE_TIME = 20
@ -27,27 +26,27 @@ DEFAULT_SETTLE_TIME = 20
DEPENDENCIES = ['rpi_pfio']
PORT_SCHEMA = vol.Schema({
vol.Optional(ATTR_NAME, default=None): cv.string,
vol.Optional(ATTR_SETTLE_TIME, default=DEFAULT_SETTLE_TIME):
vol.Optional(CONF_NAME, default=None): cv.string,
vol.Optional(CONF_SETTLE_TIME, default=DEFAULT_SETTLE_TIME):
cv.positive_int,
vol.Optional(ATTR_INVERT_LOGIC, default=DEFAULT_INVERT_LOGIC): cv.boolean
vol.Optional(CONF_INVERT_LOGIC, default=DEFAULT_INVERT_LOGIC): cv.boolean,
})
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_PORTS, default={}): vol.Schema({
cv.positive_int: PORT_SCHEMA
cv.positive_int: PORT_SCHEMA,
})
})
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the PiFace Digital Input devices."""
"""Set up the PiFace Digital Input devices."""
binary_sensors = []
ports = config.get('ports')
ports = config.get(CONF_PORTS)
for port, port_entity in ports.items():
name = port_entity[ATTR_NAME]
settle_time = port_entity[ATTR_SETTLE_TIME] / 1000
invert_logic = port_entity[ATTR_INVERT_LOGIC]
name = port_entity[CONF_NAME]
settle_time = port_entity[CONF_SETTLE_TIME] / 1000
invert_logic = port_entity[CONF_INVERT_LOGIC]
binary_sensors.append(RPiPFIOBinarySensor(
hass, port, name, settle_time, invert_logic))

View File

@ -4,46 +4,49 @@ Support for Vanderbilt (formerly Siemens) SPC alarm systems.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.spc/
"""
import logging
import asyncio
import logging
from homeassistant.components.spc import (
ATTR_DISCOVER_DEVICES, DATA_REGISTRY)
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.const import (STATE_UNAVAILABLE, STATE_ON, STATE_OFF)
from homeassistant.components.spc import ATTR_DISCOVER_DEVICES, DATA_REGISTRY
from homeassistant.const import STATE_OFF, STATE_ON, STATE_UNAVAILABLE
_LOGGER = logging.getLogger(__name__)
SPC_TYPE_TO_DEVICE_CLASS = {'0': 'motion',
'1': 'opening',
'3': 'smoke'}
SPC_TYPE_TO_DEVICE_CLASS = {
'0': 'motion',
'1': 'opening',
'3': 'smoke',
}
SPC_INPUT_TO_SENSOR_STATE = {'0': STATE_OFF,
'1': STATE_ON}
SPC_INPUT_TO_SENSOR_STATE = {
'0': STATE_OFF,
'1': STATE_ON,
}
def _get_device_class(spc_type):
"""Get the device class."""
return SPC_TYPE_TO_DEVICE_CLASS.get(spc_type, None)
def _get_sensor_state(spc_input):
"""Get the sensor state."""
return SPC_INPUT_TO_SENSOR_STATE.get(spc_input, STATE_UNAVAILABLE)
def _create_sensor(hass, zone):
return SpcBinarySensor(zone_id=zone['id'],
name=zone['zone_name'],
state=_get_sensor_state(zone['input']),
device_class=_get_device_class(zone['type']),
spc_registry=hass.data[DATA_REGISTRY])
"""Create a SPC sensor."""
return SpcBinarySensor(
zone_id=zone['id'], name=zone['zone_name'],
state=_get_sensor_state(zone['input']),
device_class=_get_device_class(zone['type']),
spc_registry=hass.data[DATA_REGISTRY])
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices,
discovery_info=None):
"""Initialize the platform."""
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Set up the SPC binary sensor."""
if (discovery_info is None or
discovery_info[ATTR_DISCOVER_DEVICES] is None):
return
@ -55,7 +58,7 @@ def async_setup_platform(hass, config, async_add_devices,
class SpcBinarySensor(BinarySensorDevice):
"""Represents a sensor based on an SPC zone."""
"""Representation of a sensor based on a SPC zone."""
def __init__(self, zone_id, name, state, device_class, spc_registry):
"""Initialize the sensor device."""
@ -74,7 +77,7 @@ class SpcBinarySensor(BinarySensorDevice):
@property
def name(self):
"""The name of the device."""
"""Return the name of the device."""
return self._name
@property
@ -85,7 +88,7 @@ class SpcBinarySensor(BinarySensorDevice):
@property
def hidden(self) -> bool:
"""Whether the device is hidden by default."""
# these type of sensors are probably mainly used for automations
# These type of sensors are probably mainly used for automations
return True
@property
@ -95,5 +98,5 @@ class SpcBinarySensor(BinarySensorDevice):
@property
def device_class(self):
"""The device class."""
"""Return the device class."""
return self._device_class

View File

@ -4,15 +4,15 @@ Support for Taps Affs.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.tapsaff/
"""
import logging
from datetime import timedelta
import logging
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.components.binary_sensor import (
BinarySensorDevice, PLATFORM_SCHEMA)
from homeassistant.const import (CONF_NAME)
PLATFORM_SCHEMA, BinarySensorDevice)
from homeassistant.const import CONF_NAME
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['tapsaff==0.1.3']
@ -67,7 +67,7 @@ class TapsAffData(object):
"""Class for handling the data retrieval for pins."""
def __init__(self, location):
"""Initialize the sensor."""
"""Initialize the data object."""
from tapsaff import TapsAff
self._is_taps_aff = None

View File

@ -28,7 +28,7 @@ class TeslaBinarySensor(TeslaDevice, BinarySensorDevice):
"""Implement an Tesla binary sensor for parking and charger."""
def __init__(self, tesla_device, controller, sensor_type):
"""Initialisation of binary sensor."""
"""Initialise of a Tesla binary sensor."""
super().__init__(tesla_device, controller)
self._state = False
self.entity_id = ENTITY_ID_FORMAT.format(self.tesla_id)

View File

@ -6,15 +6,15 @@ https://home-assistant.io/components/binary_sensor.verisure/
"""
import logging
from homeassistant.components.verisure import HUB as hub
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.verisure import CONF_DOOR_WINDOW
from homeassistant.components.verisure import HUB as hub
_LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup Verisure binary sensors."""
"""Set up the Verisure binary sensors."""
sensors = []
hub.update_overview()
@ -27,10 +27,10 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
class VerisureDoorWindowSensor(BinarySensorDevice):
"""Verisure door window sensor."""
"""Representation of a Verisure door window sensor."""
def __init__(self, device_label):
"""Initialize the modbus coil sensor."""
"""Initialize the Verisure door window sensor."""
self._device_label = device_label
@property

View File

@ -31,7 +31,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the Vultr subscription (server) sensor."""
"""Set up the Vultr subscription (server) binary sensor."""
vultr = hass.data[DATA_VULTR]
subscription = config.get(CONF_SUBSCRIPTION)
@ -39,7 +39,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
if subscription not in vultr.data:
_LOGGER.error("Subscription %s not found", subscription)
return False
return
add_devices([VultrBinarySensor(vultr, subscription, name)], True)
@ -48,7 +48,7 @@ class VultrBinarySensor(BinarySensorDevice):
"""Representation of a Vultr subscription sensor."""
def __init__(self, vultr, subscription, name):
"""Initialize a new Vultr sensor."""
"""Initialize a new Vultr binary sensor."""
self._vultr = vultr
self._name = name

View File

@ -8,7 +8,7 @@ import asyncio
import logging
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.wink import WinkDevice, DOMAIN
from homeassistant.components.wink import DOMAIN, WinkDevice
_LOGGER = logging.getLogger(__name__)
@ -16,18 +16,18 @@ DEPENDENCIES = ['wink']
# These are the available sensors mapped to binary_sensor class
SENSOR_TYPES = {
'opened': 'opening',
'brightness': 'light',
'vibration': 'vibration',
'loudness': 'sound',
'noise': 'sound',
'capturing_audio': 'sound',
'liquid_detected': 'moisture',
'motion': 'motion',
'presence': 'occupancy',
'capturing_video': None,
'co_detected': 'gas',
'liquid_detected': 'moisture',
'loudness': 'sound',
'motion': 'motion',
'noise': 'sound',
'opened': 'opening',
'presence': 'occupancy',
'smoke_detected': 'smoke',
'capturing_video': None
'vibration': 'vibration',
}
@ -103,7 +103,7 @@ class WinkBinarySensorDevice(WinkDevice, BinarySensorDevice):
@asyncio.coroutine
def async_added_to_hass(self):
"""Callback when entity is added to hass."""
"""Call when entity is added to hass."""
self.hass.data[DOMAIN]['entities']['binary_sensor'].append(self)
@property
@ -118,7 +118,7 @@ class WinkBinarySensorDevice(WinkDevice, BinarySensorDevice):
@property
def device_state_attributes(self):
"""Return the state attributes."""
"""Return the device state attributes."""
return super().device_state_attributes
@ -127,7 +127,7 @@ class WinkSmokeDetector(WinkBinarySensorDevice):
@property
def device_state_attributes(self):
"""Return the state attributes."""
"""Return the device state attributes."""
_attributes = super().device_state_attributes
_attributes['test_activated'] = self.wink.test_activated()
return _attributes
@ -138,11 +138,18 @@ class WinkHub(WinkBinarySensorDevice):
@property
def device_state_attributes(self):
"""Return the state attributes."""
"""Return the device state attributes."""
_attributes = super().device_state_attributes
_attributes['update_needed'] = self.wink.update_needed()
_attributes['firmware_version'] = self.wink.firmware_version()
_attributes['pairing_mode'] = self.wink.pairing_mode()
_kidde_code = self.wink.kidde_radio_code()
if _kidde_code is not None:
# The service call to set the Kidde code
# takes a string of 1s and 0s so it makes
# sense to display it to the user that way
_formatted_kidde_code = "{:b}".format(_kidde_code).zfill(8)
_attributes['kidde_radio_code'] = _formatted_kidde_code
return _attributes
@ -170,7 +177,7 @@ class WinkButton(WinkBinarySensorDevice):
@property
def device_state_attributes(self):
"""Return the state attributes."""
"""Return the device state attributes."""
_attributes = super().device_state_attributes
_attributes['pressed'] = self.wink.pressed()
_attributes['long_pressed'] = self.wink.long_pressed()

View File

@ -17,17 +17,21 @@ import homeassistant.helpers.config_validation as cv
_LOGGER = logging.getLogger(__name__)
REQUIREMENTS = ['holidays==0.8.1']
REQUIREMENTS = ['holidays==0.9.3']
# List of all countries currently supported by holidays
# There seems to be no way to get the list out at runtime
ALL_COUNTRIES = ['Australia', 'AU', 'Austria', 'AT', 'Canada', 'CA',
'Colombia', 'CO', 'Czech', 'CZ', 'Denmark', 'DK', 'England',
'EuropeanCentralBank', 'ECB', 'TAR', 'Germany', 'DE',
'Ireland', 'Isle of Man', 'Mexico', 'MX', 'Netherlands', 'NL',
'NewZealand', 'NZ', 'Northern Ireland', 'Norway', 'NO',
'Portugal', 'PT', 'PortugalExt', 'PTE', 'Scotland', 'Spain',
'ES', 'UnitedKingdom', 'UK', 'UnitedStates', 'US', 'Wales']
ALL_COUNTRIES = ['Australia', 'AU', 'Austria', 'AT', 'Belgium', 'BE', 'Canada',
'CA', 'Colombia', 'CO', 'Czech', 'CZ', 'Denmark', 'DK',
'England', 'EuropeanCentralBank', 'ECB', 'TAR', 'Finland',
'FI', 'France', 'FRA', 'Germany', 'DE', 'Ireland',
'Isle of Man', 'Italy', 'IT', 'Japan', 'JP', 'Mexico', 'MX',
'Netherlands', 'NL', 'NewZealand', 'NZ', 'Northern Ireland',
'Norway', 'NO', 'Polish', 'PL', 'Portugal', 'PT',
'PortugalExt', 'PTE', 'Scotland', 'Slovenia', 'SI',
'Slovakia', 'SK', 'South Africa', 'ZA', 'Spain', 'ES',
'Sweden', 'SE', 'UnitedKingdom', 'UK', 'UnitedStates', 'US',
'Wales']
CONF_COUNTRY = 'country'
CONF_PROVINCE = 'province'
CONF_WORKDAYS = 'workdays'

View File

@ -101,7 +101,7 @@ class XiaomiNatgasSensor(XiaomiBinarySensor):
attrs.update(super().device_state_attributes)
return attrs
def parse_data(self, data):
def parse_data(self, data, raw_data):
"""Parse data sent by gateway."""
if DENSITY in data:
self._density = int(data.get(DENSITY))
@ -139,8 +139,16 @@ class XiaomiMotionSensor(XiaomiBinarySensor):
attrs.update(super().device_state_attributes)
return attrs
def parse_data(self, data):
def parse_data(self, data, raw_data):
"""Parse data sent by gateway."""
if raw_data['cmd'] == 'heartbeat':
_LOGGER.debug(
'Skipping heartbeat of the motion sensor. '
'It can introduce an incorrect state because of a firmware '
'bug (https://github.com/home-assistant/home-assistant/pull/'
'11631#issuecomment-357507744).')
return
self._should_poll = False
if NO_MOTION in data: # handle push from the hub
self._no_motion_since = data[NO_MOTION]
@ -186,7 +194,7 @@ class XiaomiDoorSensor(XiaomiBinarySensor):
attrs.update(super().device_state_attributes)
return attrs
def parse_data(self, data):
def parse_data(self, data, raw_data):
"""Parse data sent by gateway."""
self._should_poll = False
if NO_CLOSE in data: # handle push from the hub
@ -219,7 +227,7 @@ class XiaomiWaterLeakSensor(XiaomiBinarySensor):
XiaomiBinarySensor.__init__(self, device, 'Water Leak Sensor',
xiaomi_hub, 'status', 'moisture')
def parse_data(self, data):
def parse_data(self, data, raw_data):
"""Parse data sent by gateway."""
self._should_poll = False
@ -256,7 +264,7 @@ class XiaomiSmokeSensor(XiaomiBinarySensor):
attrs.update(super().device_state_attributes)
return attrs
def parse_data(self, data):
def parse_data(self, data, raw_data):
"""Parse data sent by gateway."""
if DENSITY in data:
self._density = int(data.get(DENSITY))
@ -293,7 +301,7 @@ class XiaomiButton(XiaomiBinarySensor):
attrs.update(super().device_state_attributes)
return attrs
def parse_data(self, data):
def parse_data(self, data, raw_data):
"""Parse data sent by gateway."""
value = data.get(self._data_key)
if value is None:
@ -343,7 +351,7 @@ class XiaomiCube(XiaomiBinarySensor):
attrs.update(super().device_state_attributes)
return attrs
def parse_data(self, data):
def parse_data(self, data, raw_data):
"""Parse data sent by gateway."""
if 'status' in data:
self._hass.bus.fire('cube_action', {

View File

@ -4,18 +4,18 @@ Support for WebDav Calendar.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/calendar.caldav/
"""
from datetime import datetime, timedelta
import logging
import re
from datetime import datetime, timedelta
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.components.calendar import (
CalendarEventDevice, PLATFORM_SCHEMA)
PLATFORM_SCHEMA, CalendarEventDevice)
from homeassistant.const import (
CONF_NAME, CONF_PASSWORD, CONF_URL, CONF_USERNAME)
from homeassistant.util import dt, Throttle
import homeassistant.helpers.config_validation as cv
from homeassistant.util import Throttle, dt
REQUIREMENTS = ['caldav==0.5.0']
@ -39,9 +39,9 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_CUSTOM_CALENDARS, default=[]):
vol.All(cv.ensure_list, vol.Schema([
vol.Schema({
vol.Required(CONF_NAME): cv.string,
vol.Required(CONF_CALENDAR): cv.string,
vol.Required(CONF_SEARCH): cv.string
vol.Required(CONF_NAME): cv.string,
vol.Required(CONF_SEARCH): cv.string,
})
]))
})
@ -53,12 +53,12 @@ def setup_platform(hass, config, add_devices, disc_info=None):
"""Set up the WebDav Calendar platform."""
import caldav
client = caldav.DAVClient(config.get(CONF_URL),
None,
config.get(CONF_USERNAME),
config.get(CONF_PASSWORD))
url = config.get(CONF_URL)
username = config.get(CONF_USERNAME)
password = config.get(CONF_PASSWORD)
client = caldav.DAVClient(url, None, username, password)
# Retrieve all the remote calendars
calendars = client.principal().calendars()
calendar_devices = []
@ -70,8 +70,7 @@ def setup_platform(hass, config, add_devices, disc_info=None):
_LOGGER.debug("Ignoring calendar '%s'", calendar.name)
continue
# Create additional calendars based on custom filtering
# rules
# Create additional calendars based on custom filtering rules
for cust_calendar in config.get(CONF_CUSTOM_CALENDARS):
# Check that the base calendar matches
if cust_calendar.get(CONF_CALENDAR) != calendar.name:
@ -85,12 +84,9 @@ def setup_platform(hass, config, add_devices, disc_info=None):
}
calendar_devices.append(
WebDavCalendarEventDevice(hass,
device_data,
calendar,
True,
cust_calendar.get(CONF_SEARCH))
)
WebDavCalendarEventDevice(
hass, device_data, calendar, True,
cust_calendar.get(CONF_SEARCH)))
# Create a default calendar if there was no custom one
if not config.get(CONF_CUSTOM_CALENDARS):
@ -102,18 +98,13 @@ def setup_platform(hass, config, add_devices, disc_info=None):
WebDavCalendarEventDevice(hass, device_data, calendar)
)
# Finally add all the calendars we've created
add_devices(calendar_devices)
class WebDavCalendarEventDevice(CalendarEventDevice):
"""A device for getting the next Task from a WebDav Calendar."""
def __init__(self,
hass,
device_data,
calendar,
all_day=False,
def __init__(self, hass, device_data, calendar, all_day=False,
search=None):
"""Create the WebDav Calendar Event Device."""
self.data = WebDavCalendarData(calendar, all_day, search)
@ -167,9 +158,7 @@ class WebDavCalendarData(object):
if vevent is None:
_LOGGER.debug(
"No matching event found in the %d results for %s",
len(results),
self.calendar.name,
)
len(results), self.calendar.name)
self.event = None
return True

View File

@ -4,29 +4,28 @@ Support for Todoist task management (https://todoist.com).
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/calendar.todoist/
"""
from datetime import datetime
from datetime import timedelta
from datetime import datetime, timedelta
import logging
import voluptuous as vol
from homeassistant.components.calendar import (
CalendarEventDevice, DOMAIN, PLATFORM_SCHEMA)
from homeassistant.components.google import (
CONF_DEVICE_ID)
from homeassistant.const import (
CONF_ID, CONF_NAME, CONF_TOKEN)
DOMAIN, PLATFORM_SCHEMA, CalendarEventDevice)
from homeassistant.components.google import CONF_DEVICE_ID
from homeassistant.const import CONF_ID, CONF_NAME, CONF_TOKEN
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.template import DATE_STR_FORMAT
from homeassistant.util import dt
from homeassistant.util import Throttle
from homeassistant.util import Throttle, dt
REQUIREMENTS = ['todoist-python==7.0.17']
_LOGGER = logging.getLogger(__name__)
CONF_EXTRA_PROJECTS = 'custom_projects'
CONF_PROJECT_DUE_DATE = 'due_date_days'
CONF_PROJECT_LABEL_WHITELIST = 'labels'
CONF_PROJECT_WHITELIST = 'include_projects'
# Calendar Platform: Does this calendar event last all day?
ALL_DAY = 'all_day'
# Attribute: All tasks in this project
@ -78,20 +77,15 @@ SUMMARY = 'summary'
TASKS = 'items'
SERVICE_NEW_TASK = 'todoist_new_task'
NEW_TASK_SERVICE_SCHEMA = vol.Schema({
vol.Required(CONTENT): cv.string,
vol.Optional(PROJECT_NAME, default='inbox'): vol.All(cv.string, vol.Lower),
vol.Optional(LABELS): cv.ensure_list_csv,
vol.Optional(PRIORITY): vol.All(vol.Coerce(int),
vol.Range(min=1, max=4)),
vol.Optional(DUE_DATE): cv.string
vol.Optional(PRIORITY): vol.All(vol.Coerce(int), vol.Range(min=1, max=4)),
vol.Optional(DUE_DATE): cv.string,
})
CONF_EXTRA_PROJECTS = 'custom_projects'
CONF_PROJECT_DUE_DATE = 'due_date_days'
CONF_PROJECT_WHITELIST = 'include_projects'
CONF_PROJECT_LABEL_WHITELIST = 'labels'
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_TOKEN): cv.string,
vol.Optional(CONF_EXTRA_PROJECTS, default=[]):
@ -111,8 +105,7 @@ MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=15)
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Todoist platform."""
# Check token:
"""Set up the Todoist platform."""
token = config.get(CONF_TOKEN)
# Look up IDs based on (lowercase) names.
@ -176,7 +169,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
add_devices(project_devices)
def handle_new_task(call):
"""Called when a user creates a new Todoist Task from HASS."""
"""Call when a user creates a new Todoist Task from HASS."""
project_name = call.data[PROJECT_NAME]
project_id = project_id_lookup[project_name]
@ -528,8 +521,7 @@ class TodoistProjectData(object):
# Let's set our "due date" to tomorrow
self.event[END] = {
DATETIME: (
datetime.utcnow() +
timedelta(days=1)
datetime.utcnow() + timedelta(days=1)
).strftime(DATE_STR_FORMAT)
}
_LOGGER.debug("Updated %s", self._name)

View File

@ -124,15 +124,15 @@ def async_setup(hass, config):
"""Set up the camera component."""
component = EntityComponent(_LOGGER, DOMAIN, hass, SCAN_INTERVAL)
hass.http.register_view(CameraImageView(component.entities))
hass.http.register_view(CameraMjpegStream(component.entities))
hass.http.register_view(CameraImageView(component))
hass.http.register_view(CameraMjpegStream(component))
yield from component.async_setup(config)
@callback
def update_tokens(time):
"""Update tokens of the entities."""
for entity in component.entities.values():
for entity in component.entities:
entity.async_update_token()
hass.async_add_job(entity.async_update_ha_state())
@ -358,14 +358,14 @@ class CameraView(HomeAssistantView):
requires_auth = False
def __init__(self, entities):
def __init__(self, component):
"""Initialize a basic camera view."""
self.entities = entities
self.component = component
@asyncio.coroutine
def get(self, request, entity_id):
"""Start a GET request."""
camera = self.entities.get(entity_id)
camera = self.component.get_entity(entity_id)
if camera is None:
status = 404 if request[KEY_AUTHENTICATED] else 401

View File

@ -75,7 +75,9 @@ class ArloCam(Camera):
self._ffmpeg = hass.data[DATA_FFMPEG]
self._ffmpeg_arguments = device_info.get(CONF_FFMPEG_ARGUMENTS)
self._last_refresh = None
self._camera.base_station.refresh_rate = SCAN_INTERVAL.total_seconds()
if self._camera.base_station:
self._camera.base_station.refresh_rate = \
SCAN_INTERVAL.total_seconds()
self.attrs = {}
def camera_image(self):

View File

@ -6,11 +6,11 @@ https://home-assistant.io/components/camera.axis/
"""
import logging
from homeassistant.const import (
CONF_HOST, CONF_NAME, CONF_USERNAME, CONF_PASSWORD, CONF_PORT,
CONF_AUTHENTICATION, HTTP_DIGEST_AUTHENTICATION)
from homeassistant.components.camera.mjpeg import (
CONF_MJPEG_URL, CONF_STILL_IMAGE_URL, MjpegCamera)
from homeassistant.const import (
CONF_AUTHENTICATION, CONF_HOST, CONF_NAME, CONF_PASSWORD, CONF_PORT,
CONF_USERNAME, HTTP_DIGEST_AUTHENTICATION)
from homeassistant.helpers.dispatcher import dispatcher_connect
_LOGGER = logging.getLogger(__name__)
@ -20,6 +20,7 @@ DEPENDENCIES = [DOMAIN]
def _get_image_url(host, port, mode):
"""Set the URL to get the image."""
if mode == 'mjpeg':
return 'http://{}:{}/axis-cgi/mjpg/video.cgi'.format(host, port)
elif mode == 'single':
@ -27,34 +28,32 @@ def _get_image_url(host, port, mode):
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup Axis camera."""
"""Set up the Axis camera."""
camera_config = {
CONF_NAME: discovery_info[CONF_NAME],
CONF_USERNAME: discovery_info[CONF_USERNAME],
CONF_PASSWORD: discovery_info[CONF_PASSWORD],
CONF_MJPEG_URL: _get_image_url(discovery_info[CONF_HOST],
str(discovery_info[CONF_PORT]),
'mjpeg'),
CONF_STILL_IMAGE_URL: _get_image_url(discovery_info[CONF_HOST],
str(discovery_info[CONF_PORT]),
'single'),
CONF_MJPEG_URL: _get_image_url(
discovery_info[CONF_HOST], str(discovery_info[CONF_PORT]),
'mjpeg'),
CONF_STILL_IMAGE_URL: _get_image_url(
discovery_info[CONF_HOST], str(discovery_info[CONF_PORT]),
'single'),
CONF_AUTHENTICATION: HTTP_DIGEST_AUTHENTICATION,
}
add_devices([AxisCamera(hass,
camera_config,
str(discovery_info[CONF_PORT]))])
add_devices([AxisCamera(
hass, camera_config, str(discovery_info[CONF_PORT]))])
class AxisCamera(MjpegCamera):
"""AxisCamera class."""
"""Representation of a Axis camera."""
def __init__(self, hass, config, port):
"""Initialize Axis Communications camera component."""
super().__init__(hass, config)
self.port = port
dispatcher_connect(hass,
DOMAIN + '_' + config[CONF_NAME] + '_new_ip',
self._new_ip)
dispatcher_connect(
hass, DOMAIN + '_' + config[CONF_NAME] + '_new_ip', self._new_ip)
def _new_ip(self, host):
"""Set new IP for video stream."""

View File

@ -4,21 +4,21 @@ Support for Blink system camera.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/camera.blink/
"""
from datetime import timedelta
import logging
from datetime import timedelta
import requests
from homeassistant.components.blink import DOMAIN
from homeassistant.components.camera import Camera
from homeassistant.util import Throttle
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['blink']
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=90)
_LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up a Blink Camera."""
@ -45,7 +45,7 @@ class BlinkCamera(Camera):
self.notifications = self.data.cameras[self._name].notifications
self.response = None
_LOGGER.info("Initialized blink camera %s", self._name)
_LOGGER.debug("Initialized blink camera %s", self._name)
@property
def name(self):
@ -55,7 +55,7 @@ class BlinkCamera(Camera):
@Throttle(MIN_TIME_BETWEEN_UPDATES)
def request_image(self):
"""Request a new image from Blink servers."""
_LOGGER.info("Requesting new image from blink servers")
_LOGGER.debug("Requesting new image from blink servers")
image_url = self.check_for_motion()
header = self.data.cameras[self._name].header
self.response = requests.get(image_url, headers=header, stream=True)
@ -68,7 +68,7 @@ class BlinkCamera(Camera):
# We detected motion at some point
self.data.last_motion()
self.notifications = notifs
# returning motion image currently not working
# Returning motion image currently not working
# return self.data.cameras[self._name].motion['image']
elif notifs < self.notifications:
self.notifications = notifs

View File

@ -44,6 +44,8 @@ class FoscamCam(Camera):
def __init__(self, device_info):
"""Initialize a Foscam camera."""
from libpyfoscam import FoscamCamera
super(FoscamCam, self).__init__()
ip_address = device_info.get(CONF_IP)
@ -53,10 +55,8 @@ class FoscamCam(Camera):
self._name = device_info.get(CONF_NAME)
self._motion_status = False
from libpyfoscam import FoscamCamera
self._foscam_session = FoscamCamera(ip_address, port, self._username,
self._password, verbose=False)
self._foscam_session = FoscamCamera(
ip_address, port, self._username, self._password, verbose=False)
def camera_image(self):
"""Return a still image response from the camera."""
@ -75,20 +75,20 @@ class FoscamCam(Camera):
def enable_motion_detection(self):
"""Enable motion detection in camera."""
ret, err = self._foscam_session.enable_motion_detection()
if ret == FOSCAM_COMM_ERROR:
_LOGGER.debug("Unable to communicate with Foscam Camera: %s", err)
self._motion_status = True
else:
try:
ret = self._foscam_session.enable_motion_detection()
self._motion_status = ret == FOSCAM_COMM_ERROR
except TypeError:
_LOGGER.debug("Communication problem")
self._motion_status = False
def disable_motion_detection(self):
"""Disable motion detection."""
ret, err = self._foscam_session.disable_motion_detection()
if ret == FOSCAM_COMM_ERROR:
_LOGGER.debug("Unable to communicate with Foscam Camera: %s", err)
self._motion_status = True
else:
try:
ret = self._foscam_session.disable_motion_detection()
self._motion_status = ret == FOSCAM_COMM_ERROR
except TypeError:
_LOGGER.debug("Communication problem")
self._motion_status = False
@property

View File

@ -14,7 +14,7 @@ from homeassistant.const import (
CONF_NAME, CONF_HOST, CONF_USERNAME, CONF_PASSWORD, CONF_PORT)
from homeassistant.components.camera import Camera, PLATFORM_SCHEMA
from homeassistant.components.ffmpeg import (
DATA_FFMPEG)
DATA_FFMPEG, CONF_EXTRA_ARGUMENTS)
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.aiohttp_client import (
async_aiohttp_proxy_stream)
@ -31,6 +31,7 @@ DEFAULT_NAME = 'ONVIF Camera'
DEFAULT_PORT = 5000
DEFAULT_USERNAME = 'admin'
DEFAULT_PASSWORD = '888888'
DEFAULT_ARGUMENTS = '-q:v 2'
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_HOST): cv.string,
@ -38,6 +39,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_PASSWORD, default=DEFAULT_PASSWORD): cv.string,
vol.Optional(CONF_USERNAME, default=DEFAULT_USERNAME): cv.string,
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
vol.Optional(CONF_EXTRA_ARGUMENTS, default=DEFAULT_ARGUMENTS): cv.string,
})
@ -59,7 +61,7 @@ class ONVIFCamera(Camera):
super().__init__()
self._name = config.get(CONF_NAME)
self._ffmpeg_arguments = '-q:v 2'
self._ffmpeg_arguments = config.get(CONF_EXTRA_ARGUMENTS)
media = ONVIFService(
'http://{}:{}/onvif/device_service'.format(
config.get(CONF_HOST), config.get(CONF_PORT)),

View File

@ -0,0 +1,113 @@
"""
Support for Xeoma Cameras.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/camera.xeoma/
"""
import asyncio
import logging
import voluptuous as vol
from homeassistant.components.camera import PLATFORM_SCHEMA, Camera
from homeassistant.const import (
CONF_HOST, CONF_NAME, CONF_PASSWORD, CONF_USERNAME)
from homeassistant.helpers import config_validation as cv
REQUIREMENTS = ['pyxeoma==1.2']
_LOGGER = logging.getLogger(__name__)
CONF_CAMERAS = 'cameras'
CONF_HIDE = 'hide'
CONF_IMAGE_NAME = 'image_name'
CONF_NEW_VERSION = 'new_version'
CAMERAS_SCHEMA = vol.Schema({
vol.Required(CONF_IMAGE_NAME): cv.string,
vol.Optional(CONF_HIDE, default=False): cv.boolean,
vol.Optional(CONF_NAME): cv.string,
}, required=False)
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_HOST): cv.string,
vol.Optional(CONF_CAMERAS, default={}):
vol.Schema(vol.All(cv.ensure_list, [CAMERAS_SCHEMA])),
vol.Optional(CONF_NEW_VERSION, default=True): cv.boolean,
vol.Optional(CONF_PASSWORD): cv.string,
vol.Optional(CONF_USERNAME): cv.string,
})
@asyncio.coroutine
# pylint: disable=unused-argument
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Discover and setup Xeoma Cameras."""
from pyxeoma.xeoma import Xeoma, XeomaError
host = config[CONF_HOST]
login = config.get(CONF_USERNAME)
password = config.get(CONF_PASSWORD)
new_version = config[CONF_NEW_VERSION]
xeoma = Xeoma(host, new_version, login, password)
try:
yield from xeoma.async_test_connection()
discovered_image_names = yield from xeoma.async_get_image_names()
discovered_cameras = [
{
CONF_IMAGE_NAME: image_name,
CONF_HIDE: False,
CONF_NAME: image_name
}
for image_name in discovered_image_names
]
for cam in config[CONF_CAMERAS]:
camera = next(
(dc for dc in discovered_cameras
if dc[CONF_IMAGE_NAME] == cam[CONF_IMAGE_NAME]), None)
if camera is not None:
if CONF_NAME in cam:
camera[CONF_NAME] = cam[CONF_NAME]
if CONF_HIDE in cam:
camera[CONF_HIDE] = cam[CONF_HIDE]
cameras = list(filter(lambda c: not c[CONF_HIDE], discovered_cameras))
async_add_devices(
[XeomaCamera(xeoma, camera[CONF_IMAGE_NAME], camera[CONF_NAME])
for camera in cameras])
except XeomaError as err:
_LOGGER.error("Error: %s", err.message)
return
class XeomaCamera(Camera):
"""Implementation of a Xeoma camera."""
def __init__(self, xeoma, image, name):
"""Initialize a Xeoma camera."""
super().__init__()
self._xeoma = xeoma
self._name = name
self._image = image
self._last_image = None
@asyncio.coroutine
def async_camera_image(self):
"""Return a still image response from the camera."""
from pyxeoma.xeoma import XeomaError
try:
image = yield from self._xeoma.async_get_camera_image(self._image)
self._last_image = image
except XeomaError as err:
_LOGGER.error("Error fetching image: %s", err.message)
return self._last_image
@property
def name(self):
"""Return the name of this device."""
return self._name

View File

@ -499,53 +499,54 @@ class ClimateDevice(Entity):
self.precision),
}
supported_features = self.supported_features
if self.target_temperature_step is not None:
data[ATTR_TARGET_TEMP_STEP] = self.target_temperature_step
target_temp_high = self.target_temperature_high
if target_temp_high is not None:
if supported_features & SUPPORT_TARGET_TEMPERATURE_HIGH:
data[ATTR_TARGET_TEMP_HIGH] = show_temp(
self.hass, self.target_temperature_high, self.temperature_unit,
self.precision)
if supported_features & SUPPORT_TARGET_TEMPERATURE_LOW:
data[ATTR_TARGET_TEMP_LOW] = show_temp(
self.hass, self.target_temperature_low, self.temperature_unit,
self.precision)
humidity = self.target_humidity
if humidity is not None:
data[ATTR_HUMIDITY] = humidity
if supported_features & SUPPORT_TARGET_HUMIDITY:
data[ATTR_HUMIDITY] = self.target_humidity
data[ATTR_CURRENT_HUMIDITY] = self.current_humidity
data[ATTR_MIN_HUMIDITY] = self.min_humidity
data[ATTR_MAX_HUMIDITY] = self.max_humidity
fan_mode = self.current_fan_mode
if fan_mode is not None:
data[ATTR_FAN_MODE] = fan_mode
if supported_features & SUPPORT_TARGET_HUMIDITY_LOW:
data[ATTR_MIN_HUMIDITY] = self.min_humidity
if supported_features & SUPPORT_TARGET_HUMIDITY_HIGH:
data[ATTR_MAX_HUMIDITY] = self.max_humidity
if supported_features & SUPPORT_FAN_MODE:
data[ATTR_FAN_MODE] = self.current_fan_mode
if self.fan_list:
data[ATTR_FAN_LIST] = self.fan_list
operation_mode = self.current_operation
if operation_mode is not None:
data[ATTR_OPERATION_MODE] = operation_mode
if supported_features & SUPPORT_OPERATION_MODE:
data[ATTR_OPERATION_MODE] = self.current_operation
if self.operation_list:
data[ATTR_OPERATION_LIST] = self.operation_list
is_hold = self.current_hold_mode
if is_hold is not None:
data[ATTR_HOLD_MODE] = is_hold
if supported_features & SUPPORT_HOLD_MODE:
data[ATTR_HOLD_MODE] = self.current_hold_mode
swing_mode = self.current_swing_mode
if swing_mode is not None:
data[ATTR_SWING_MODE] = swing_mode
if supported_features & SUPPORT_SWING_MODE:
data[ATTR_SWING_MODE] = self.current_swing_mode
if self.swing_list:
data[ATTR_SWING_LIST] = self.swing_list
is_away = self.is_away_mode_on
if is_away is not None:
if supported_features & SUPPORT_AWAY_MODE:
is_away = self.is_away_mode_on
data[ATTR_AWAY_MODE] = STATE_ON if is_away else STATE_OFF
is_aux_heat = self.is_aux_heat_on
if is_aux_heat is not None:
if supported_features & SUPPORT_AUX_HEAT:
is_aux_heat = self.is_aux_heat_on
data[ATTR_AUX_HEAT] = STATE_ON if is_aux_heat else STATE_OFF
return data

View File

@ -9,35 +9,23 @@ import re
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.components.climate import (
ATTR_OPERATION_MODE, ATTR_FAN_MODE, ATTR_SWING_MODE,
ATTR_CURRENT_TEMPERATURE, ClimateDevice, PLATFORM_SCHEMA,
SUPPORT_TARGET_TEMPERATURE, SUPPORT_FAN_MODE, SUPPORT_OPERATION_MODE,
SUPPORT_SWING_MODE, STATE_OFF, STATE_AUTO, STATE_HEAT, STATE_COOL,
STATE_DRY, STATE_FAN_ONLY
)
ATTR_CURRENT_TEMPERATURE, ATTR_FAN_MODE, ATTR_OPERATION_MODE,
ATTR_SWING_MODE, PLATFORM_SCHEMA, STATE_AUTO, STATE_COOL, STATE_DRY,
STATE_FAN_ONLY, STATE_HEAT, STATE_OFF, SUPPORT_FAN_MODE,
SUPPORT_OPERATION_MODE, SUPPORT_SWING_MODE, SUPPORT_TARGET_TEMPERATURE,
ClimateDevice)
from homeassistant.components.daikin import (
daikin_api_setup,
ATTR_TARGET_TEMPERATURE,
ATTR_INSIDE_TEMPERATURE,
ATTR_OUTSIDE_TEMPERATURE
)
ATTR_INSIDE_TEMPERATURE, ATTR_OUTSIDE_TEMPERATURE, ATTR_TARGET_TEMPERATURE,
daikin_api_setup)
from homeassistant.const import (
CONF_HOST, CONF_NAME,
TEMP_CELSIUS,
ATTR_TEMPERATURE
)
ATTR_TEMPERATURE, CONF_HOST, CONF_NAME, TEMP_CELSIUS)
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['pydaikin==0.4']
_LOGGER = logging.getLogger(__name__)
SUPPORT_FLAGS = (SUPPORT_TARGET_TEMPERATURE |
SUPPORT_FAN_MODE |
SUPPORT_OPERATION_MODE |
SUPPORT_SWING_MODE)
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_HOST): cv.string,
vol.Optional(CONF_NAME, default=None): cv.string,
@ -56,19 +44,22 @@ HA_ATTR_TO_DAIKIN = {
ATTR_OPERATION_MODE: 'mode',
ATTR_FAN_MODE: 'f_rate',
ATTR_SWING_MODE: 'f_dir',
ATTR_INSIDE_TEMPERATURE: 'htemp',
ATTR_OUTSIDE_TEMPERATURE: 'otemp',
ATTR_TARGET_TEMPERATURE: 'stemp'
}
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Daikin HVAC platform."""
"""Set up the Daikin HVAC platform."""
if discovery_info is not None:
host = discovery_info.get('ip')
name = None
_LOGGER.info("Discovered a Daikin AC on %s", host)
_LOGGER.debug("Discovered a Daikin AC on %s", host)
else:
host = config.get(CONF_HOST)
name = config.get(CONF_NAME)
_LOGGER.info("Added Daikin AC on %s", host)
_LOGGER.debug("Added Daikin AC on %s", host)
api = daikin_api_setup(hass, host, name)
add_devices([DaikinClimate(api)], True)
@ -101,6 +92,17 @@ class DaikinClimate(ClimateDevice):
),
}
self._supported_features = SUPPORT_TARGET_TEMPERATURE \
| SUPPORT_OPERATION_MODE
daikin_attr = HA_ATTR_TO_DAIKIN[ATTR_FAN_MODE]
if self._api.device.values.get(daikin_attr) is not None:
self._supported_features |= SUPPORT_FAN_MODE
daikin_attr = HA_ATTR_TO_DAIKIN[ATTR_SWING_MODE]
if self._api.device.values.get(daikin_attr) is not None:
self._supported_features |= SUPPORT_SWING_MODE
def get(self, key):
"""Retrieve device settings from API library cache."""
value = None
@ -108,29 +110,34 @@ class DaikinClimate(ClimateDevice):
if key in [ATTR_TEMPERATURE, ATTR_INSIDE_TEMPERATURE,
ATTR_CURRENT_TEMPERATURE]:
value = self._api.device.values.get('htemp')
key = ATTR_INSIDE_TEMPERATURE
daikin_attr = HA_ATTR_TO_DAIKIN.get(key)
if key == ATTR_INSIDE_TEMPERATURE:
value = self._api.device.values.get(daikin_attr)
cast_to_float = True
if key == ATTR_TARGET_TEMPERATURE:
value = self._api.device.values.get('stemp')
elif key == ATTR_TARGET_TEMPERATURE:
value = self._api.device.values.get(daikin_attr)
cast_to_float = True
elif key == ATTR_OUTSIDE_TEMPERATURE:
value = self._api.device.values.get('otemp')
value = self._api.device.values.get(daikin_attr)
cast_to_float = True
elif key == ATTR_FAN_MODE:
value = self._api.device.represent('f_rate')[1].title()
value = self._api.device.represent(daikin_attr)[1].title()
elif key == ATTR_SWING_MODE:
value = self._api.device.represent('f_dir')[1].title()
value = self._api.device.represent(daikin_attr)[1].title()
elif key == ATTR_OPERATION_MODE:
# Daikin can return also internal states auto-1 or auto-7
# and we need to translate them as AUTO
value = re.sub(
'[^a-z]',
'',
self._api.device.represent('mode')[1]
self._api.device.represent(daikin_attr)[1]
).title()
if value is None:
_LOGGER.warning("Invalid value requested for key %s", key)
_LOGGER.error("Invalid value requested for key %s", key)
else:
if value == "-" or value == "--":
value = None
@ -178,7 +185,7 @@ class DaikinClimate(ClimateDevice):
@property
def supported_features(self):
"""Return the list of supported features."""
return SUPPORT_FLAGS
return self._supported_features
@property
def name(self):

View File

@ -7,6 +7,7 @@ https://home-assistant.io/components/demo/
from homeassistant.components.climate import (
ClimateDevice, ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_LOW,
SUPPORT_TARGET_TEMPERATURE, SUPPORT_TARGET_HUMIDITY,
SUPPORT_TARGET_HUMIDITY_LOW, SUPPORT_TARGET_HUMIDITY_HIGH,
SUPPORT_AWAY_MODE, SUPPORT_HOLD_MODE, SUPPORT_FAN_MODE,
SUPPORT_OPERATION_MODE, SUPPORT_AUX_HEAT, SUPPORT_SWING_MODE,
SUPPORT_TARGET_TEMPERATURE_HIGH, SUPPORT_TARGET_TEMPERATURE_LOW,
@ -14,6 +15,7 @@ from homeassistant.components.climate import (
from homeassistant.const import TEMP_CELSIUS, TEMP_FAHRENHEIT, ATTR_TEMPERATURE
SUPPORT_FLAGS = (SUPPORT_TARGET_TEMPERATURE | SUPPORT_TARGET_HUMIDITY |
SUPPORT_TARGET_HUMIDITY_LOW | SUPPORT_TARGET_HUMIDITY_HIGH |
SUPPORT_AWAY_MODE | SUPPORT_HOLD_MODE | SUPPORT_FAN_MODE |
SUPPORT_OPERATION_MODE | SUPPORT_AUX_HEAT |
SUPPORT_SWING_MODE | SUPPORT_TARGET_TEMPERATURE_HIGH |

View File

@ -10,16 +10,12 @@ import logging
import voluptuous as vol
from homeassistant.components.climate import (
DOMAIN,
PLATFORM_SCHEMA,
STATE_ECO, STATE_GAS, STATE_ELECTRIC,
STATE_HEAT_PUMP, STATE_HIGH_DEMAND,
STATE_OFF, SUPPORT_TARGET_TEMPERATURE,
SUPPORT_OPERATION_MODE,
ClimateDevice)
from homeassistant.const import (ATTR_ENTITY_ID,
CONF_PASSWORD, CONF_USERNAME, TEMP_FAHRENHEIT,
ATTR_TEMPERATURE)
DOMAIN, PLATFORM_SCHEMA, STATE_ECO, STATE_ELECTRIC, STATE_GAS,
STATE_HEAT_PUMP, STATE_HIGH_DEMAND, STATE_OFF, STATE_PERFORMANCE,
SUPPORT_OPERATION_MODE, SUPPORT_TARGET_TEMPERATURE, ClimateDevice)
from homeassistant.const import (
ATTR_ENTITY_ID, ATTR_TEMPERATURE, CONF_PASSWORD, CONF_USERNAME,
TEMP_FAHRENHEIT)
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['pyeconet==0.0.4']
@ -59,6 +55,7 @@ HA_STATE_TO_ECONET = {
STATE_GAS: 'gas',
STATE_HIGH_DEMAND: 'High Demand',
STATE_OFF: 'Off',
STATE_PERFORMANCE: 'Performance'
}
ECONET_STATE_TO_HA = {value: key for key, value in HA_STATE_TO_ECONET.items()}
@ -87,7 +84,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
hass.data[ECONET_DATA]['water_heaters'].extend(hass_water_heaters)
def service_handle(service):
"""Handler for services."""
"""Handle the service calls."""
entity_ids = service.data.get('entity_id')
all_heaters = hass.data[ECONET_DATA]['water_heaters']
_heaters = [
@ -105,12 +102,10 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
_water_heater.schedule_update_ha_state(True)
hass.services.register(DOMAIN, SERVICE_ADD_VACATION,
service_handle,
hass.services.register(DOMAIN, SERVICE_ADD_VACATION, service_handle,
schema=ADD_VACATION_SCHEMA)
hass.services.register(DOMAIN, SERVICE_DELETE_VACATION,
service_handle,
hass.services.register(DOMAIN, SERVICE_DELETE_VACATION, service_handle,
schema=DELETE_VACATION_SCHEMA)
@ -138,7 +133,7 @@ class EcoNetWaterHeater(ClimateDevice):
@property
def device_state_attributes(self):
"""Return the optional state attributes."""
"""Return the optional device state attributes."""
data = {}
vacations = self.water_heater.get_vacations()
if vacations:
@ -157,8 +152,7 @@ class EcoNetWaterHeater(ClimateDevice):
"""
Return current operation as one of the following.
["eco", "heat_pump",
"high_demand", "electric_only"]
["eco", "heat_pump", "high_demand", "electric_only"]
"""
current_op = ECONET_STATE_TO_HA.get(self.water_heater.mode)
return current_op
@ -189,7 +183,7 @@ class EcoNetWaterHeater(ClimateDevice):
if target_temp is not None:
self.water_heater.set_target_set_point(target_temp)
else:
_LOGGER.error("A target temperature must be provided.")
_LOGGER.error("A target temperature must be provided")
def set_operation_mode(self, operation_mode):
"""Set operation mode."""
@ -197,7 +191,7 @@ class EcoNetWaterHeater(ClimateDevice):
if op_mode_to_set is not None:
self.water_heater.set_mode(op_mode_to_set)
else:
_LOGGER.error("An operation mode must be provided.")
_LOGGER.error("An operation mode must be provided")
def add_vacation(self, start, end):
"""Add a vacation to this water heater."""

View File

@ -9,9 +9,10 @@ from datetime import timedelta
import voluptuous as vol
from homeassistant.components.climate import (
ClimateDevice, PLATFORM_SCHEMA, STATE_HEAT, STATE_IDLE, SUPPORT_AUX_HEAT)
ClimateDevice, PLATFORM_SCHEMA, STATE_HEAT, STATE_IDLE, SUPPORT_AUX_HEAT,
SUPPORT_TARGET_TEMPERATURE)
from homeassistant.const import (
TEMP_CELSIUS, CONF_USERNAME, CONF_PASSWORD)
TEMP_CELSIUS, CONF_USERNAME, CONF_PASSWORD, ATTR_TEMPERATURE)
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['pyephember==0.1.1']
@ -59,7 +60,10 @@ class EphEmberThermostat(ClimateDevice):
@property
def supported_features(self):
"""Return the list of supported features."""
return SUPPORT_AUX_HEAT
if self._hot_water:
return SUPPORT_AUX_HEAT
return SUPPORT_TARGET_TEMPERATURE | SUPPORT_AUX_HEAT
@property
def name(self):
@ -81,6 +85,14 @@ class EphEmberThermostat(ClimateDevice):
"""Return the temperature we try to reach."""
return self._zone['targetTemperature']
@property
def target_temperature_step(self):
"""Return the supported step of target temperature."""
if self._hot_water:
return None
return 1
@property
def current_operation(self):
"""Return current operation ie. heat, cool, idle."""
@ -105,17 +117,38 @@ class EphEmberThermostat(ClimateDevice):
def set_temperature(self, **kwargs):
"""Set new target temperature."""
return
temperature = kwargs.get(ATTR_TEMPERATURE)
if temperature is None:
return
if self._hot_water:
return
if temperature == self.target_temperature:
return
if temperature > self.max_temp or temperature < self.min_temp:
return
self._ember.set_target_temperture_by_name(self._zone_name,
int(temperature))
@property
def min_temp(self):
"""Return the minimum temperature."""
return self._zone['targetTemperature']
# Hot water temp doesn't support being changed
if self._hot_water:
return self._zone['targetTemperature']
return 5
@property
def max_temp(self):
"""Return the maximum temperature."""
return self._zone['targetTemperature']
if self._hot_water:
return self._zone['targetTemperature']
return 35
def update(self):
"""Get the latest data."""

View File

@ -15,7 +15,7 @@ from homeassistant.const import (
CONF_MAC, CONF_DEVICES, TEMP_CELSIUS, ATTR_TEMPERATURE, PRECISION_HALVES)
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['python-eq3bt==0.1.6']
REQUIREMENTS = ['python-eq3bt==0.1.8']
_LOGGER = logging.getLogger(__name__)

View File

@ -17,7 +17,8 @@ from homeassistant.components.climate import (
SUPPORT_AWAY_MODE, SUPPORT_TARGET_TEMPERATURE, PLATFORM_SCHEMA)
from homeassistant.const import (
ATTR_UNIT_OF_MEASUREMENT, STATE_ON, STATE_OFF, ATTR_TEMPERATURE,
CONF_NAME, ATTR_ENTITY_ID, SERVICE_TURN_ON, SERVICE_TURN_OFF)
CONF_NAME, ATTR_ENTITY_ID, SERVICE_TURN_ON, SERVICE_TURN_OFF,
STATE_UNKNOWN)
from homeassistant.helpers import condition
from homeassistant.helpers.event import (
async_track_state_change, async_track_time_interval)
@ -30,7 +31,6 @@ DEPENDENCIES = ['switch', 'sensor']
DEFAULT_TOLERANCE = 0.3
DEFAULT_NAME = 'Generic Thermostat'
DEFAULT_AWAY_TEMP = 16
CONF_HEATER = 'heater'
CONF_SENSOR = 'target_sensor'
@ -44,7 +44,7 @@ CONF_HOT_TOLERANCE = 'hot_tolerance'
CONF_KEEP_ALIVE = 'keep_alive'
CONF_INITIAL_OPERATION_MODE = 'initial_operation_mode'
CONF_AWAY_TEMP = 'away_temp'
SUPPORT_FLAGS = (SUPPORT_TARGET_TEMPERATURE | SUPPORT_AWAY_MODE |
SUPPORT_FLAGS = (SUPPORT_TARGET_TEMPERATURE |
SUPPORT_OPERATION_MODE)
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
@ -64,8 +64,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
cv.time_period, cv.positive_timedelta),
vol.Optional(CONF_INITIAL_OPERATION_MODE):
vol.In([STATE_AUTO, STATE_OFF]),
vol.Optional(CONF_AWAY_TEMP,
default=DEFAULT_AWAY_TEMP): vol.Coerce(float)
vol.Optional(CONF_AWAY_TEMP): vol.Coerce(float)
})
@ -119,6 +118,7 @@ class GenericThermostat(ClimateDevice):
self._operation_list = [STATE_HEAT, STATE_OFF]
if initial_operation_mode == STATE_OFF:
self._enabled = False
self._current_operation = STATE_OFF
else:
self._enabled = True
self._active = False
@ -127,6 +127,9 @@ class GenericThermostat(ClimateDevice):
self._max_temp = max_temp
self._target_temp = target_temp
self._unit = hass.config.units.temperature_unit
self._support_flags = SUPPORT_FLAGS
if away_temp is not None:
self._support_flags = SUPPORT_FLAGS | SUPPORT_AWAY_MODE
self._away_temp = away_temp
self._is_away = False
@ -139,6 +142,10 @@ class GenericThermostat(ClimateDevice):
async_track_time_interval(
hass, self._async_keep_alive, self._keep_alive)
sensor_state = hass.states.get(sensor_entity_id)
if sensor_state and sensor_state.state != STATE_UNKNOWN:
self._async_update_temp(sensor_state)
@asyncio.coroutine
def async_added_to_hass(self):
"""Run when entity about to be added."""
@ -154,19 +161,29 @@ class GenericThermostat(ClimateDevice):
self._target_temp = self.max_temp
else:
self._target_temp = self.min_temp
_LOGGER.warning('Undefined target temperature, \
falling back to %s', self._target_temp)
_LOGGER.warning("Undefined target temperature,"
"falling back to %s", self._target_temp)
else:
self._target_temp = float(
old_state.attributes[ATTR_TEMPERATURE])
self._is_away = True if str(
old_state.attributes[ATTR_AWAY_MODE]) == STATE_ON else False
if old_state.attributes[ATTR_OPERATION_MODE] == STATE_OFF:
self._current_operation = STATE_OFF
self._enabled = False
if self._initial_operation_mode is None:
if old_state.attributes[ATTR_OPERATION_MODE] == STATE_OFF:
self._enabled = False
if old_state.attributes[ATTR_AWAY_MODE] is not None:
self._is_away = str(
old_state.attributes[ATTR_AWAY_MODE]) == STATE_ON
if (self._initial_operation_mode is None and
old_state.attributes[ATTR_OPERATION_MODE] is not None):
self._current_operation = \
old_state.attributes[ATTR_OPERATION_MODE]
if self._current_operation != STATE_OFF:
self._enabled = True
else:
# No previous state, try and restore defaults
if self._target_temp is None:
if self.ac_mode:
self._target_temp = self.max_temp
else:
self._target_temp = self.min_temp
_LOGGER.warning("No previously saved temperature, setting to %s",
self._target_temp)
@property
def state(self):
@ -230,7 +247,7 @@ class GenericThermostat(ClimateDevice):
if self._is_device_active:
self._heater_turn_off()
else:
_LOGGER.error('Unrecognized operation mode: %s', operation_mode)
_LOGGER.error("Unrecognized operation mode: %s", operation_mode)
return
# Ensure we updae the current operation after changing the mode
self.schedule_update_ha_state()
@ -299,7 +316,7 @@ class GenericThermostat(ClimateDevice):
self._cur_temp = self.hass.config.units.temperature(
float(state.state), unit)
except ValueError as ex:
_LOGGER.error('Unable to update from sensor: %s', ex)
_LOGGER.error("Unable to update from sensor: %s", ex)
@callback
def _async_control_heating(self):
@ -307,8 +324,9 @@ class GenericThermostat(ClimateDevice):
if not self._active and None not in (self._cur_temp,
self._target_temp):
self._active = True
_LOGGER.info('Obtained current and target temperature. '
'Generic thermostat active.')
_LOGGER.info("Obtained current and target temperature. "
"Generic thermostat active. %s, %s",
self._cur_temp, self._target_temp)
if not self._active:
return
@ -333,13 +351,13 @@ class GenericThermostat(ClimateDevice):
too_cold = self._target_temp - self._cur_temp >= \
self._cold_tolerance
if too_cold:
_LOGGER.info('Turning off AC %s', self.heater_entity_id)
_LOGGER.info("Turning off AC %s", self.heater_entity_id)
self._heater_turn_off()
else:
too_hot = self._cur_temp - self._target_temp >= \
self._hot_tolerance
if too_hot:
_LOGGER.info('Turning on AC %s', self.heater_entity_id)
_LOGGER.info("Turning on AC %s", self.heater_entity_id)
self._heater_turn_on()
else:
is_heating = self._is_device_active
@ -347,14 +365,14 @@ class GenericThermostat(ClimateDevice):
too_hot = self._cur_temp - self._target_temp >= \
self._hot_tolerance
if too_hot:
_LOGGER.info('Turning off heater %s',
_LOGGER.info("Turning off heater %s",
self.heater_entity_id)
self._heater_turn_off()
else:
too_cold = self._target_temp - self._cur_temp >= \
self._cold_tolerance
if too_cold:
_LOGGER.info('Turning on heater %s', self.heater_entity_id)
_LOGGER.info("Turning on heater %s", self.heater_entity_id)
self._heater_turn_on()
@property
@ -365,7 +383,7 @@ class GenericThermostat(ClimateDevice):
@property
def supported_features(self):
"""Return the list of supported features."""
return SUPPORT_FLAGS
return self._support_flags
@callback
def _heater_turn_on(self):

View File

@ -1,178 +1,178 @@
"""
Support for the Hive devices.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/climate.hive/
"""
from homeassistant.components.climate import (
ClimateDevice, STATE_AUTO, STATE_HEAT, STATE_OFF, STATE_ON,
SUPPORT_AUX_HEAT, SUPPORT_TARGET_TEMPERATURE, SUPPORT_OPERATION_MODE)
from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS
from homeassistant.components.hive import DATA_HIVE
DEPENDENCIES = ['hive']
HIVE_TO_HASS_STATE = {'SCHEDULE': STATE_AUTO, 'MANUAL': STATE_HEAT,
'ON': STATE_ON, 'OFF': STATE_OFF}
HASS_TO_HIVE_STATE = {STATE_AUTO: 'SCHEDULE', STATE_HEAT: 'MANUAL',
STATE_ON: 'ON', STATE_OFF: 'OFF'}
SUPPORT_FLAGS = (SUPPORT_TARGET_TEMPERATURE |
SUPPORT_OPERATION_MODE |
SUPPORT_AUX_HEAT)
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up Hive climate devices."""
if discovery_info is None:
return
session = hass.data.get(DATA_HIVE)
add_devices([HiveClimateEntity(session, discovery_info)])
class HiveClimateEntity(ClimateDevice):
"""Hive Climate Device."""
def __init__(self, hivesession, hivedevice):
"""Initialize the Climate device."""
self.node_id = hivedevice["Hive_NodeID"]
self.node_name = hivedevice["Hive_NodeName"]
self.device_type = hivedevice["HA_DeviceType"]
self.session = hivesession
self.data_updatesource = '{}.{}'.format(self.device_type,
self.node_id)
if self.device_type == "Heating":
self.modes = [STATE_AUTO, STATE_HEAT, STATE_OFF]
elif self.device_type == "HotWater":
self.modes = [STATE_AUTO, STATE_ON, STATE_OFF]
self.session.entities.append(self)
@property
def supported_features(self):
"""Return the list of supported features."""
return SUPPORT_FLAGS
def handle_update(self, updatesource):
"""Handle the new update request."""
if '{}.{}'.format(self.device_type, self.node_id) not in updatesource:
self.schedule_update_ha_state()
@property
def name(self):
"""Return the name of the Climate device."""
friendly_name = "Climate Device"
if self.device_type == "Heating":
friendly_name = "Heating"
if self.node_name is not None:
friendly_name = '{} {}'.format(self.node_name, friendly_name)
elif self.device_type == "HotWater":
friendly_name = "Hot Water"
return friendly_name
@property
def temperature_unit(self):
"""Return the unit of measurement."""
return TEMP_CELSIUS
@property
def current_temperature(self):
"""Return the current temperature."""
if self.device_type == "Heating":
return self.session.heating.current_temperature(self.node_id)
@property
def target_temperature(self):
"""Return the target temperature."""
if self.device_type == "Heating":
return self.session.heating.get_target_temperature(self.node_id)
@property
def min_temp(self):
"""Return minimum temperature."""
if self.device_type == "Heating":
return self.session.heating.min_temperature(self.node_id)
@property
def max_temp(self):
"""Return the maximum temperature."""
if self.device_type == "Heating":
return self.session.heating.max_temperature(self.node_id)
@property
def operation_list(self):
"""List of the operation modes."""
return self.modes
@property
def current_operation(self):
"""Return current mode."""
if self.device_type == "Heating":
currentmode = self.session.heating.get_mode(self.node_id)
elif self.device_type == "HotWater":
currentmode = self.session.hotwater.get_mode(self.node_id)
return HIVE_TO_HASS_STATE.get(currentmode)
def set_operation_mode(self, operation_mode):
"""Set new Heating mode."""
new_mode = HASS_TO_HIVE_STATE.get(operation_mode)
if self.device_type == "Heating":
self.session.heating.set_mode(self.node_id, new_mode)
elif self.device_type == "HotWater":
self.session.hotwater.set_mode(self.node_id, new_mode)
for entity in self.session.entities:
entity.handle_update(self.data_updatesource)
def set_temperature(self, **kwargs):
"""Set new target temperature."""
new_temperature = kwargs.get(ATTR_TEMPERATURE)
if new_temperature is not None:
if self.device_type == "Heating":
self.session.heating.set_target_temperature(self.node_id,
new_temperature)
for entity in self.session.entities:
entity.handle_update(self.data_updatesource)
@property
def is_aux_heat_on(self):
"""Return true if auxiliary heater is on."""
boost_status = None
if self.device_type == "Heating":
boost_status = self.session.heating.get_boost(self.node_id)
elif self.device_type == "HotWater":
boost_status = self.session.hotwater.get_boost(self.node_id)
return boost_status == "ON"
def turn_aux_heat_on(self):
"""Turn auxiliary heater on."""
target_boost_time = 30
if self.device_type == "Heating":
curtemp = self.session.heating.current_temperature(self.node_id)
curtemp = round(curtemp * 2) / 2
target_boost_temperature = curtemp + 0.5
self.session.heating.turn_boost_on(self.node_id,
target_boost_time,
target_boost_temperature)
elif self.device_type == "HotWater":
self.session.hotwater.turn_boost_on(self.node_id,
target_boost_time)
for entity in self.session.entities:
entity.handle_update(self.data_updatesource)
def turn_aux_heat_off(self):
"""Turn auxiliary heater off."""
if self.device_type == "Heating":
self.session.heating.turn_boost_off(self.node_id)
elif self.device_type == "HotWater":
self.session.hotwater.turn_boost_off(self.node_id)
for entity in self.session.entities:
entity.handle_update(self.data_updatesource)
def update(self):
"""Update all Node data frome Hive."""
self.session.core.update_data(self.node_id)
"""
Support for the Hive devices.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/climate.hive/
"""
from homeassistant.components.climate import (
ClimateDevice, STATE_AUTO, STATE_HEAT, STATE_OFF, STATE_ON,
SUPPORT_AUX_HEAT, SUPPORT_TARGET_TEMPERATURE, SUPPORT_OPERATION_MODE)
from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS
from homeassistant.components.hive import DATA_HIVE
DEPENDENCIES = ['hive']
HIVE_TO_HASS_STATE = {'SCHEDULE': STATE_AUTO, 'MANUAL': STATE_HEAT,
'ON': STATE_ON, 'OFF': STATE_OFF}
HASS_TO_HIVE_STATE = {STATE_AUTO: 'SCHEDULE', STATE_HEAT: 'MANUAL',
STATE_ON: 'ON', STATE_OFF: 'OFF'}
SUPPORT_FLAGS = (SUPPORT_TARGET_TEMPERATURE |
SUPPORT_OPERATION_MODE |
SUPPORT_AUX_HEAT)
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up Hive climate devices."""
if discovery_info is None:
return
session = hass.data.get(DATA_HIVE)
add_devices([HiveClimateEntity(session, discovery_info)])
class HiveClimateEntity(ClimateDevice):
"""Hive Climate Device."""
def __init__(self, hivesession, hivedevice):
"""Initialize the Climate device."""
self.node_id = hivedevice["Hive_NodeID"]
self.node_name = hivedevice["Hive_NodeName"]
self.device_type = hivedevice["HA_DeviceType"]
self.session = hivesession
self.data_updatesource = '{}.{}'.format(self.device_type,
self.node_id)
if self.device_type == "Heating":
self.modes = [STATE_AUTO, STATE_HEAT, STATE_OFF]
elif self.device_type == "HotWater":
self.modes = [STATE_AUTO, STATE_ON, STATE_OFF]
self.session.entities.append(self)
@property
def supported_features(self):
"""Return the list of supported features."""
return SUPPORT_FLAGS
def handle_update(self, updatesource):
"""Handle the new update request."""
if '{}.{}'.format(self.device_type, self.node_id) not in updatesource:
self.schedule_update_ha_state()
@property
def name(self):
"""Return the name of the Climate device."""
friendly_name = "Climate Device"
if self.device_type == "Heating":
friendly_name = "Heating"
if self.node_name is not None:
friendly_name = '{} {}'.format(self.node_name, friendly_name)
elif self.device_type == "HotWater":
friendly_name = "Hot Water"
return friendly_name
@property
def temperature_unit(self):
"""Return the unit of measurement."""
return TEMP_CELSIUS
@property
def current_temperature(self):
"""Return the current temperature."""
if self.device_type == "Heating":
return self.session.heating.current_temperature(self.node_id)
@property
def target_temperature(self):
"""Return the target temperature."""
if self.device_type == "Heating":
return self.session.heating.get_target_temperature(self.node_id)
@property
def min_temp(self):
"""Return minimum temperature."""
if self.device_type == "Heating":
return self.session.heating.min_temperature(self.node_id)
@property
def max_temp(self):
"""Return the maximum temperature."""
if self.device_type == "Heating":
return self.session.heating.max_temperature(self.node_id)
@property
def operation_list(self):
"""List of the operation modes."""
return self.modes
@property
def current_operation(self):
"""Return current mode."""
if self.device_type == "Heating":
currentmode = self.session.heating.get_mode(self.node_id)
elif self.device_type == "HotWater":
currentmode = self.session.hotwater.get_mode(self.node_id)
return HIVE_TO_HASS_STATE.get(currentmode)
def set_operation_mode(self, operation_mode):
"""Set new Heating mode."""
new_mode = HASS_TO_HIVE_STATE.get(operation_mode)
if self.device_type == "Heating":
self.session.heating.set_mode(self.node_id, new_mode)
elif self.device_type == "HotWater":
self.session.hotwater.set_mode(self.node_id, new_mode)
for entity in self.session.entities:
entity.handle_update(self.data_updatesource)
def set_temperature(self, **kwargs):
"""Set new target temperature."""
new_temperature = kwargs.get(ATTR_TEMPERATURE)
if new_temperature is not None:
if self.device_type == "Heating":
self.session.heating.set_target_temperature(self.node_id,
new_temperature)
for entity in self.session.entities:
entity.handle_update(self.data_updatesource)
@property
def is_aux_heat_on(self):
"""Return true if auxiliary heater is on."""
boost_status = None
if self.device_type == "Heating":
boost_status = self.session.heating.get_boost(self.node_id)
elif self.device_type == "HotWater":
boost_status = self.session.hotwater.get_boost(self.node_id)
return boost_status == "ON"
def turn_aux_heat_on(self):
"""Turn auxiliary heater on."""
target_boost_time = 30
if self.device_type == "Heating":
curtemp = self.session.heating.current_temperature(self.node_id)
curtemp = round(curtemp * 2) / 2
target_boost_temperature = curtemp + 0.5
self.session.heating.turn_boost_on(self.node_id,
target_boost_time,
target_boost_temperature)
elif self.device_type == "HotWater":
self.session.hotwater.turn_boost_on(self.node_id,
target_boost_time)
for entity in self.session.entities:
entity.handle_update(self.data_updatesource)
def turn_aux_heat_off(self):
"""Turn auxiliary heater off."""
if self.device_type == "Heating":
self.session.heating.turn_boost_off(self.node_id)
elif self.device_type == "HotWater":
self.session.hotwater.turn_boost_off(self.node_id)
for entity in self.session.entities:
entity.handle_update(self.data_updatesource)
def update(self):
"""Update all Node data frome Hive."""
self.session.core.update_data(self.node_id)

View File

@ -5,13 +5,14 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/climate.knx/
"""
import asyncio
import voluptuous as vol
from homeassistant.components.knx import DATA_KNX, ATTR_DISCOVER_DEVICES
from homeassistant.components.climate import (
PLATFORM_SCHEMA, ClimateDevice, SUPPORT_TARGET_TEMPERATURE,
SUPPORT_OPERATION_MODE)
from homeassistant.const import CONF_NAME, TEMP_CELSIUS, ATTR_TEMPERATURE
PLATFORM_SCHEMA, SUPPORT_OPERATION_MODE, SUPPORT_TARGET_TEMPERATURE,
ClimateDevice)
from homeassistant.components.knx import ATTR_DISCOVER_DEVICES, DATA_KNX
from homeassistant.const import ATTR_TEMPERATURE, CONF_NAME, TEMP_CELSIUS
from homeassistant.core import callback
import homeassistant.helpers.config_validation as cv
@ -61,24 +62,20 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices,
discovery_info=None):
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Set up climate(s) for KNX platform."""
if DATA_KNX not in hass.data \
or not hass.data[DATA_KNX].initialized:
return False
if DATA_KNX not in hass.data or not hass.data[DATA_KNX].initialized:
return
if discovery_info is not None:
async_add_devices_discovery(hass, discovery_info, async_add_devices)
else:
async_add_devices_config(hass, config, async_add_devices)
return True
@callback
def async_add_devices_discovery(hass, discovery_info, async_add_devices):
"""Set up climates for KNX platform configured within plattform."""
"""Set up climates for KNX platform configured within platform."""
entities = []
for device_name in discovery_info[ATTR_DISCOVER_DEVICES]:
device = hass.data[DATA_KNX].xknx.devices[device_name]
@ -88,28 +85,22 @@ def async_add_devices_discovery(hass, discovery_info, async_add_devices):
@callback
def async_add_devices_config(hass, config, async_add_devices):
"""Set up climate for KNX platform configured within plattform."""
"""Set up climate for KNX platform configured within platform."""
import xknx
climate = xknx.devices.Climate(
hass.data[DATA_KNX].xknx,
name=config.get(CONF_NAME),
group_address_temperature=config.get(
CONF_TEMPERATURE_ADDRESS),
group_address_temperature=config.get(CONF_TEMPERATURE_ADDRESS),
group_address_target_temperature=config.get(
CONF_TARGET_TEMPERATURE_ADDRESS),
group_address_setpoint_shift=config.get(
CONF_SETPOINT_SHIFT_ADDRESS),
group_address_setpoint_shift=config.get(CONF_SETPOINT_SHIFT_ADDRESS),
group_address_setpoint_shift_state=config.get(
CONF_SETPOINT_SHIFT_STATE_ADDRESS),
setpoint_shift_step=config.get(
CONF_SETPOINT_SHIFT_STEP),
setpoint_shift_max=config.get(
CONF_SETPOINT_SHIFT_MAX),
setpoint_shift_min=config.get(
CONF_SETPOINT_SHIFT_MIN),
group_address_operation_mode=config.get(
CONF_OPERATION_MODE_ADDRESS),
setpoint_shift_step=config.get(CONF_SETPOINT_SHIFT_STEP),
setpoint_shift_max=config.get(CONF_SETPOINT_SHIFT_MAX),
setpoint_shift_min=config.get(CONF_SETPOINT_SHIFT_MIN),
group_address_operation_mode=config.get(CONF_OPERATION_MODE_ADDRESS),
group_address_operation_mode_state=config.get(
CONF_OPERATION_MODE_STATE_ADDRESS),
group_address_controller_status=config.get(
@ -127,10 +118,10 @@ def async_add_devices_config(hass, config, async_add_devices):
class KNXClimate(ClimateDevice):
"""Representation of a KNX climate."""
"""Representation of a KNX climate device."""
def __init__(self, hass, device):
"""Initialization of KNXClimate."""
"""Initialize of a KNX climate device."""
self.device = device
self.hass = hass
self.async_register_callbacks()
@ -149,7 +140,7 @@ class KNXClimate(ClimateDevice):
"""Register callbacks to update hass after device was changed."""
@asyncio.coroutine
def after_update_callback(device):
"""Callback after device was updated."""
"""Call after device was updated."""
# pylint: disable=unused-argument
yield from self.async_update_ha_state()
self.device.register_device_updated_cb(after_update_callback)

View File

@ -19,7 +19,7 @@ from homeassistant.components.climate import (
SUPPORT_SWING_MODE, SUPPORT_FAN_MODE, SUPPORT_AWAY_MODE, SUPPORT_HOLD_MODE,
SUPPORT_AUX_HEAT)
from homeassistant.const import (
STATE_ON, STATE_OFF, ATTR_TEMPERATURE, CONF_NAME)
STATE_ON, STATE_OFF, ATTR_TEMPERATURE, CONF_NAME, CONF_VALUE_TEMPLATE)
from homeassistant.components.mqtt import (
CONF_AVAILABILITY_TOPIC, CONF_QOS, CONF_RETAIN, CONF_PAYLOAD_AVAILABLE,
CONF_PAYLOAD_NOT_AVAILABLE, MQTT_BASE_PLATFORM_SCHEMA, MqttAvailability)
@ -35,21 +35,30 @@ DEFAULT_NAME = 'MQTT HVAC'
CONF_POWER_COMMAND_TOPIC = 'power_command_topic'
CONF_POWER_STATE_TOPIC = 'power_state_topic'
CONF_POWER_STATE_TEMPLATE = 'power_state_template'
CONF_MODE_COMMAND_TOPIC = 'mode_command_topic'
CONF_MODE_STATE_TOPIC = 'mode_state_topic'
CONF_MODE_STATE_TEMPLATE = 'mode_state_template'
CONF_TEMPERATURE_COMMAND_TOPIC = 'temperature_command_topic'
CONF_TEMPERATURE_STATE_TOPIC = 'temperature_state_topic'
CONF_TEMPERATURE_STATE_TEMPLATE = 'temperature_state_template'
CONF_FAN_MODE_COMMAND_TOPIC = 'fan_mode_command_topic'
CONF_FAN_MODE_STATE_TOPIC = 'fan_mode_state_topic'
CONF_FAN_MODE_STATE_TEMPLATE = 'fan_mode_state_template'
CONF_SWING_MODE_COMMAND_TOPIC = 'swing_mode_command_topic'
CONF_SWING_MODE_STATE_TOPIC = 'swing_mode_state_topic'
CONF_SWING_MODE_STATE_TEMPLATE = 'swing_mode_state_template'
CONF_AWAY_MODE_COMMAND_TOPIC = 'away_mode_command_topic'
CONF_AWAY_MODE_STATE_TOPIC = 'away_mode_state_topic'
CONF_AWAY_MODE_STATE_TEMPLATE = 'away_mode_state_template'
CONF_HOLD_COMMAND_TOPIC = 'hold_command_topic'
CONF_HOLD_STATE_TOPIC = 'hold_state_topic'
CONF_HOLD_STATE_TEMPLATE = 'hold_state_template'
CONF_AUX_COMMAND_TOPIC = 'aux_command_topic'
CONF_AUX_STATE_TOPIC = 'aux_state_topic'
CONF_AUX_STATE_TEMPLATE = 'aux_state_template'
CONF_CURRENT_TEMPERATURE_TEMPLATE = 'current_temperature_template'
CONF_CURRENT_TEMPERATURE_TOPIC = 'current_temperature_topic'
CONF_PAYLOAD_ON = 'payload_on'
@ -71,6 +80,7 @@ PLATFORM_SCHEMA = SCHEMA_BASE.extend({
vol.Optional(CONF_AWAY_MODE_COMMAND_TOPIC): mqtt.valid_publish_topic,
vol.Optional(CONF_HOLD_COMMAND_TOPIC): mqtt.valid_publish_topic,
vol.Optional(CONF_AUX_COMMAND_TOPIC): mqtt.valid_publish_topic,
vol.Optional(CONF_POWER_STATE_TOPIC): mqtt.valid_subscribe_topic,
vol.Optional(CONF_MODE_STATE_TOPIC): mqtt.valid_subscribe_topic,
vol.Optional(CONF_TEMPERATURE_STATE_TOPIC): mqtt.valid_subscribe_topic,
@ -79,6 +89,18 @@ PLATFORM_SCHEMA = SCHEMA_BASE.extend({
vol.Optional(CONF_AWAY_MODE_STATE_TOPIC): mqtt.valid_subscribe_topic,
vol.Optional(CONF_HOLD_STATE_TOPIC): mqtt.valid_subscribe_topic,
vol.Optional(CONF_AUX_STATE_TOPIC): mqtt.valid_subscribe_topic,
vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
vol.Optional(CONF_POWER_STATE_TEMPLATE): cv.template,
vol.Optional(CONF_MODE_STATE_TEMPLATE): cv.template,
vol.Optional(CONF_TEMPERATURE_STATE_TEMPLATE): cv.template,
vol.Optional(CONF_FAN_MODE_STATE_TEMPLATE): cv.template,
vol.Optional(CONF_SWING_MODE_STATE_TEMPLATE): cv.template,
vol.Optional(CONF_AWAY_MODE_STATE_TEMPLATE): cv.template,
vol.Optional(CONF_HOLD_STATE_TEMPLATE): cv.template,
vol.Optional(CONF_AUX_STATE_TEMPLATE): cv.template,
vol.Optional(CONF_CURRENT_TEMPERATURE_TEMPLATE): cv.template,
vol.Optional(CONF_CURRENT_TEMPERATURE_TOPIC):
mqtt.valid_subscribe_topic,
vol.Optional(CONF_FAN_MODE_LIST,
@ -100,6 +122,26 @@ PLATFORM_SCHEMA = SCHEMA_BASE.extend({
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Set up the MQTT climate devices."""
template_keys = (
CONF_POWER_STATE_TEMPLATE,
CONF_MODE_STATE_TEMPLATE,
CONF_TEMPERATURE_STATE_TEMPLATE,
CONF_FAN_MODE_STATE_TEMPLATE,
CONF_SWING_MODE_STATE_TEMPLATE,
CONF_AWAY_MODE_STATE_TEMPLATE,
CONF_HOLD_STATE_TEMPLATE,
CONF_AUX_STATE_TEMPLATE,
CONF_CURRENT_TEMPERATURE_TEMPLATE
)
value_templates = {}
if CONF_VALUE_TEMPLATE in config:
value_template = config.get(CONF_VALUE_TEMPLATE)
value_template.hass = hass
value_templates = {key: value_template for key in template_keys}
for key in template_keys & config.keys():
value_templates[key] = config.get(key)
value_templates[key].hass = hass
async_add_devices([
MqttClimate(
hass,
@ -125,6 +167,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
CONF_CURRENT_TEMPERATURE_TOPIC
)
},
value_templates,
config.get(CONF_QOS),
config.get(CONF_RETAIN),
config.get(CONF_MODE_LIST),
@ -145,18 +188,19 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
class MqttClimate(MqttAvailability, ClimateDevice):
"""Representation of a demo climate device."""
def __init__(self, hass, name, topic, qos, retain, mode_list,
fan_mode_list, swing_mode_list, target_temperature, away,
hold, current_fan_mode, current_swing_mode,
current_operation, aux, send_if_off, payload_on,
payload_off, availability_topic, payload_available,
payload_not_available):
def __init__(self, hass, name, topic, value_templates, qos, retain,
mode_list, fan_mode_list, swing_mode_list,
target_temperature, away, hold, current_fan_mode,
current_swing_mode, current_operation, aux, send_if_off,
payload_on, payload_off, availability_topic,
payload_available, payload_not_available):
"""Initialize the climate device."""
super().__init__(availability_topic, qos, payload_available,
payload_not_available)
self.hass = hass
self._name = name
self._topic = topic
self._value_templates = value_templates
self._qos = qos
self._retain = retain
self._target_temperature = target_temperature
@ -184,6 +228,11 @@ class MqttClimate(MqttAvailability, ClimateDevice):
@callback
def handle_current_temp_received(topic, payload, qos):
"""Handle current temperature coming via MQTT."""
if CONF_CURRENT_TEMPERATURE_TEMPLATE in self._value_templates:
payload =\
self._value_templates[CONF_CURRENT_TEMPERATURE_TEMPLATE].\
async_render_with_possible_json_value(payload)
try:
self._current_temperature = float(payload)
self.async_schedule_update_ha_state()
@ -198,6 +247,10 @@ class MqttClimate(MqttAvailability, ClimateDevice):
@callback
def handle_mode_received(topic, payload, qos):
"""Handle receiving mode via MQTT."""
if CONF_MODE_STATE_TEMPLATE in self._value_templates:
payload = self._value_templates[CONF_MODE_STATE_TEMPLATE].\
async_render_with_possible_json_value(payload)
if payload not in self._operation_list:
_LOGGER.error("Invalid mode: %s", payload)
else:
@ -212,6 +265,11 @@ class MqttClimate(MqttAvailability, ClimateDevice):
@callback
def handle_temperature_received(topic, payload, qos):
"""Handle target temperature coming via MQTT."""
if CONF_TEMPERATURE_STATE_TEMPLATE in self._value_templates:
payload = \
self._value_templates[CONF_TEMPERATURE_STATE_TEMPLATE].\
async_render_with_possible_json_value(payload)
try:
self._target_temperature = float(payload)
self.async_schedule_update_ha_state()
@ -226,6 +284,11 @@ class MqttClimate(MqttAvailability, ClimateDevice):
@callback
def handle_fan_mode_received(topic, payload, qos):
"""Handle receiving fan mode via MQTT."""
if CONF_FAN_MODE_STATE_TEMPLATE in self._value_templates:
payload = \
self._value_templates[CONF_FAN_MODE_STATE_TEMPLATE].\
async_render_with_possible_json_value(payload)
if payload not in self._fan_list:
_LOGGER.error("Invalid fan mode: %s", payload)
else:
@ -240,6 +303,11 @@ class MqttClimate(MqttAvailability, ClimateDevice):
@callback
def handle_swing_mode_received(topic, payload, qos):
"""Handle receiving swing mode via MQTT."""
if CONF_SWING_MODE_STATE_TEMPLATE in self._value_templates:
payload = \
self._value_templates[CONF_SWING_MODE_STATE_TEMPLATE].\
async_render_with_possible_json_value(payload)
if payload not in self._swing_list:
_LOGGER.error("Invalid swing mode: %s", payload)
else:
@ -254,6 +322,15 @@ class MqttClimate(MqttAvailability, ClimateDevice):
@callback
def handle_away_mode_received(topic, payload, qos):
"""Handle receiving away mode via MQTT."""
if CONF_AWAY_MODE_STATE_TEMPLATE in self._value_templates:
payload = \
self._value_templates[CONF_AWAY_MODE_STATE_TEMPLATE].\
async_render_with_possible_json_value(payload)
if payload == "True":
payload = self._payload_on
elif payload == "False":
payload = self._payload_off
if payload == self._payload_on:
self._away = True
elif payload == self._payload_off:
@ -271,6 +348,14 @@ class MqttClimate(MqttAvailability, ClimateDevice):
@callback
def handle_aux_mode_received(topic, payload, qos):
"""Handle receiving aux mode via MQTT."""
if CONF_AUX_STATE_TEMPLATE in self._value_templates:
payload = self._value_templates[CONF_AUX_STATE_TEMPLATE].\
async_render_with_possible_json_value(payload)
if payload == "True":
payload = self._payload_on
elif payload == "False":
payload = self._payload_off
if payload == self._payload_on:
self._aux = True
elif payload == self._payload_off:
@ -288,6 +373,10 @@ class MqttClimate(MqttAvailability, ClimateDevice):
@callback
def handle_hold_mode_received(topic, payload, qos):
"""Handle receiving hold mode via MQTT."""
if CONF_HOLD_STATE_TEMPLATE in self._value_templates:
payload = self._value_templates[CONF_HOLD_STATE_TEMPLATE].\
async_render_with_possible_json_value(payload)
self._hold = payload
self.async_schedule_update_ha_state()

View File

@ -7,9 +7,10 @@ https://home-assistant.io/components/climate.mysensors/
from homeassistant.components import mysensors
from homeassistant.components.climate import (
ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_LOW, DOMAIN, STATE_AUTO,
STATE_COOL, STATE_HEAT, STATE_OFF, ClimateDevice,
SUPPORT_TARGET_TEMPERATURE, SUPPORT_TARGET_TEMPERATURE_HIGH,
SUPPORT_TARGET_TEMPERATURE_LOW, SUPPORT_FAN_MODE, SUPPORT_OPERATION_MODE)
STATE_COOL, STATE_HEAT, STATE_OFF, SUPPORT_FAN_MODE,
SUPPORT_OPERATION_MODE, SUPPORT_TARGET_TEMPERATURE,
SUPPORT_TARGET_TEMPERATURE_HIGH, SUPPORT_TARGET_TEMPERATURE_LOW,
ClimateDevice)
from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS, TEMP_FAHRENHEIT
DICT_HA_TO_MYS = {
@ -31,7 +32,7 @@ SUPPORT_FLAGS = (SUPPORT_TARGET_TEMPERATURE | SUPPORT_TARGET_TEMPERATURE_HIGH |
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the mysensors climate."""
"""Set up the MySensors climate."""
mysensors.setup_mysensors_platform(
hass, DOMAIN, discovery_info, MySensorsHVAC, add_devices=add_devices)
@ -52,8 +53,7 @@ class MySensorsHVAC(mysensors.MySensorsEntity, ClimateDevice):
@property
def temperature_unit(self):
"""Return the unit of measurement."""
return (TEMP_CELSIUS
if self.gateway.metric else TEMP_FAHRENHEIT)
return TEMP_CELSIUS if self.gateway.metric else TEMP_FAHRENHEIT
@property
def current_temperature(self):
@ -139,7 +139,8 @@ class MySensorsHVAC(mysensors.MySensorsEntity, ClimateDevice):
self.gateway.set_child_value(
self.node_id, self.child_id, value_type, value)
if self.gateway.optimistic:
# optimistically assume that device has changed state
# O
# ptimistically assume that device has changed state
self._values[value_type] = value
self.schedule_update_ha_state()
@ -149,7 +150,7 @@ class MySensorsHVAC(mysensors.MySensorsEntity, ClimateDevice):
self.gateway.set_child_value(
self.node_id, self.child_id, set_req.V_HVAC_SPEED, fan)
if self.gateway.optimistic:
# optimistically assume that device has changed state
# Optimistically assume that device has changed state
self._values[set_req.V_HVAC_SPEED] = fan
self.schedule_update_ha_state()
@ -159,7 +160,7 @@ class MySensorsHVAC(mysensors.MySensorsEntity, ClimateDevice):
self.node_id, self.child_id, self.value_type,
DICT_HA_TO_MYS[operation_mode])
if self.gateway.optimistic:
# optimistically assume that device has changed state
# Optimistically assume that device has changed state
self._values[self.value_type] = operation_mode
self.schedule_update_ha_state()

View File

@ -10,7 +10,7 @@ import voluptuous as vol
from homeassistant.components.nest import DATA_NEST
from homeassistant.components.climate import (
STATE_AUTO, STATE_COOL, STATE_HEAT, ClimateDevice,
STATE_AUTO, STATE_COOL, STATE_HEAT, STATE_ECO, ClimateDevice,
PLATFORM_SCHEMA, ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_LOW,
ATTR_TEMPERATURE, SUPPORT_TARGET_TEMPERATURE,
SUPPORT_TARGET_TEMPERATURE_HIGH, SUPPORT_TARGET_TEMPERATURE_LOW,
@ -27,8 +27,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.All(vol.Coerce(int), vol.Range(min=1)),
})
STATE_ECO = 'eco'
STATE_HEAT_COOL = 'heat-cool'
NEST_MODE_HEAT_COOL = 'heat-cool'
SUPPORT_FLAGS = (SUPPORT_TARGET_TEMPERATURE | SUPPORT_TARGET_TEMPERATURE_HIGH |
SUPPORT_TARGET_TEMPERATURE_LOW | SUPPORT_OPERATION_MODE |
@ -118,14 +117,14 @@ class NestThermostat(ClimateDevice):
"""Return current operation ie. heat, cool, idle."""
if self._mode in [STATE_HEAT, STATE_COOL, STATE_OFF, STATE_ECO]:
return self._mode
elif self._mode == STATE_HEAT_COOL:
elif self._mode == NEST_MODE_HEAT_COOL:
return STATE_AUTO
return STATE_UNKNOWN
@property
def target_temperature(self):
"""Return the temperature we try to reach."""
if self._mode != STATE_HEAT_COOL and not self.is_away_mode_on:
if self._mode != NEST_MODE_HEAT_COOL and not self.is_away_mode_on:
return self._target_temperature
return None
@ -136,7 +135,7 @@ class NestThermostat(ClimateDevice):
self._eco_temperature[0]:
# eco_temperature is always a low, high tuple
return self._eco_temperature[0]
if self._mode == STATE_HEAT_COOL:
if self._mode == NEST_MODE_HEAT_COOL:
return self._target_temperature[0]
return None
@ -147,7 +146,7 @@ class NestThermostat(ClimateDevice):
self._eco_temperature[1]:
# eco_temperature is always a low, high tuple
return self._eco_temperature[1]
if self._mode == STATE_HEAT_COOL:
if self._mode == NEST_MODE_HEAT_COOL:
return self._target_temperature[1]
return None
@ -160,7 +159,7 @@ class NestThermostat(ClimateDevice):
"""Set new target temperature."""
target_temp_low = kwargs.get(ATTR_TARGET_TEMP_LOW)
target_temp_high = kwargs.get(ATTR_TARGET_TEMP_HIGH)
if self._mode == STATE_HEAT_COOL:
if self._mode == NEST_MODE_HEAT_COOL:
if target_temp_low is not None and target_temp_high is not None:
temp = (target_temp_low, target_temp_high)
else:
@ -173,7 +172,7 @@ class NestThermostat(ClimateDevice):
if operation_mode in [STATE_HEAT, STATE_COOL, STATE_OFF, STATE_ECO]:
device_mode = operation_mode
elif operation_mode == STATE_AUTO:
device_mode = STATE_HEAT_COOL
device_mode = NEST_MODE_HEAT_COOL
self.device.mode = device_mode
@property

View File

@ -294,14 +294,14 @@ class SensiboClimate(ClimateDevice):
self._id, 'swing', swing_mode, self._ac_states)
@asyncio.coroutine
def async_on(self):
def async_turn_on(self):
"""Turn Sensibo unit on."""
with async_timeout.timeout(TIMEOUT):
yield from self._client.async_set_ac_state_property(
self._id, 'on', True, self._ac_states)
@asyncio.coroutine
def async_off(self):
def async_turn_off(self):
"""Turn Sensibo unit on."""
with async_timeout.timeout(TIMEOUT):
yield from self._client.async_set_ac_state_property(

View File

@ -6,13 +6,13 @@ https://home-assistant.io/components/climate.tesla/
"""
import logging
from homeassistant.const import STATE_ON, STATE_OFF
from homeassistant.components.climate import (
ClimateDevice, ENTITY_ID_FORMAT, SUPPORT_TARGET_TEMPERATURE,
SUPPORT_OPERATION_MODE)
from homeassistant.components.tesla import DOMAIN as TESLA_DOMAIN, TeslaDevice
ENTITY_ID_FORMAT, SUPPORT_OPERATION_MODE, SUPPORT_TARGET_TEMPERATURE,
ClimateDevice)
from homeassistant.components.tesla import DOMAIN as TESLA_DOMAIN
from homeassistant.components.tesla import TeslaDevice
from homeassistant.const import (
TEMP_FAHRENHEIT, TEMP_CELSIUS, ATTR_TEMPERATURE)
ATTR_TEMPERATURE, STATE_OFF, STATE_ON, TEMP_CELSIUS, TEMP_FAHRENHEIT)
_LOGGER = logging.getLogger(__name__)
@ -60,7 +60,7 @@ class TeslaThermostat(TeslaDevice, ClimateDevice):
return OPERATION_LIST
def update(self):
"""Called by the Tesla device callback to update state."""
"""Call by the Tesla device callback to update state."""
_LOGGER.debug("Updating: %s", self._name)
self.tesla_device.update()
self._target_temperature = self.tesla_device.get_goal_temp()

View File

@ -7,25 +7,25 @@ Eneco.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/climate.toon/
"""
import homeassistant.components.toon as toon_main
from homeassistant.components.climate import (
ClimateDevice, ATTR_TEMPERATURE, STATE_PERFORMANCE, STATE_HEAT, STATE_ECO,
STATE_COOL, SUPPORT_TARGET_TEMPERATURE, SUPPORT_OPERATION_MODE)
ATTR_TEMPERATURE, STATE_COOL, STATE_ECO, STATE_HEAT, STATE_PERFORMANCE,
SUPPORT_OPERATION_MODE, SUPPORT_TARGET_TEMPERATURE, ClimateDevice)
import homeassistant.components.toon as toon_main
from homeassistant.const import TEMP_CELSIUS
SUPPORT_FLAGS = SUPPORT_TARGET_TEMPERATURE | SUPPORT_OPERATION_MODE
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the Toon thermostat."""
"""Set up the Toon climate device."""
add_devices([ThermostatDevice(hass)], True)
class ThermostatDevice(ClimateDevice):
"""Interface class for the toon module and HA."""
"""Representation of a Toon climate device."""
def __init__(self, hass):
"""Initialize the device."""
"""Initialize the Toon climate device."""
self._name = 'Toon van Eneco'
self.hass = hass
self.thermos = hass.data[toon_main.TOON_HANDLE]
@ -47,12 +47,12 @@ class ThermostatDevice(ClimateDevice):
@property
def name(self):
"""Name of this Thermostat."""
"""Return the name of this thermostat."""
return self._name
@property
def temperature_unit(self):
"""The unit of measurement used by the platform."""
"""Return the unit of measurement used by the platform."""
return TEMP_CELSIUS
@property
@ -63,7 +63,7 @@ class ThermostatDevice(ClimateDevice):
@property
def operation_list(self):
"""List of available operation modes."""
"""Return a list of available operation modes."""
return self._operation_list
@property
@ -82,7 +82,7 @@ class ThermostatDevice(ClimateDevice):
self.thermos.set_temp(temp)
def set_operation_mode(self, operation_mode):
"""Set new operation mode as toonlib requires it."""
"""Set new operation mode."""
toonlib_values = {
STATE_PERFORMANCE: 'Comfort',
STATE_HEAT: 'Home',

View File

@ -0,0 +1,267 @@
"""
Support for Venstar WiFi Thermostats.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/climate.venstar/
"""
import logging
import voluptuous as vol
from homeassistant.components.climate import (
ATTR_OPERATION_MODE, ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_LOW,
PLATFORM_SCHEMA, STATE_AUTO, STATE_COOL, STATE_HEAT, SUPPORT_FAN_MODE,
SUPPORT_OPERATION_MODE, SUPPORT_TARGET_HUMIDITY,
SUPPORT_TARGET_TEMPERATURE, SUPPORT_TARGET_TEMPERATURE_HIGH,
SUPPORT_TARGET_TEMPERATURE_LOW, ClimateDevice)
from homeassistant.const import (
ATTR_TEMPERATURE, CONF_HOST, CONF_PASSWORD, CONF_SSL, CONF_TIMEOUT,
CONF_USERNAME, PRECISION_WHOLE, STATE_OFF, STATE_ON, TEMP_CELSIUS,
TEMP_FAHRENHEIT)
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['venstarcolortouch==0.5']
_LOGGER = logging.getLogger(__name__)
ATTR_FAN_STATE = 'fan_state'
ATTR_HVAC_STATE = 'hvac_state'
DEFAULT_SSL = False
VALID_FAN_STATES = [STATE_ON, STATE_AUTO]
VALID_THERMOSTAT_MODES = [STATE_HEAT, STATE_COOL, STATE_OFF, STATE_AUTO]
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_HOST): cv.string,
vol.Optional(CONF_PASSWORD): cv.string,
vol.Optional(CONF_SSL, default=DEFAULT_SSL): cv.boolean,
vol.Optional(CONF_TIMEOUT, default=5):
vol.All(vol.Coerce(int), vol.Range(min=1)),
vol.Optional(CONF_USERNAME): cv.string,
})
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the Venstar thermostat."""
import venstarcolortouch
username = config.get(CONF_USERNAME)
password = config.get(CONF_PASSWORD)
host = config.get(CONF_HOST)
timeout = config.get(CONF_TIMEOUT)
if config.get(CONF_SSL):
proto = 'https'
else:
proto = 'http'
client = venstarcolortouch.VenstarColorTouch(
addr=host, timeout=timeout, user=username, password=password,
proto=proto)
add_devices([VenstarThermostat(client)], True)
class VenstarThermostat(ClimateDevice):
"""Representation of a Venstar thermostat."""
def __init__(self, client):
"""Initialize the thermostat."""
self._client = client
def update(self):
"""Update the data from the thermostat."""
info_success = self._client.update_info()
sensor_success = self._client.update_sensors()
if not info_success or not sensor_success:
_LOGGER.error("Failed to update data")
@property
def supported_features(self):
"""Return the list of supported features."""
features = (SUPPORT_TARGET_TEMPERATURE | SUPPORT_FAN_MODE |
SUPPORT_OPERATION_MODE)
if self._client.mode == self._client.MODE_AUTO:
features |= (SUPPORT_TARGET_TEMPERATURE_HIGH |
SUPPORT_TARGET_TEMPERATURE_LOW)
if self._client.hum_active == 1:
features |= SUPPORT_TARGET_HUMIDITY
return features
@property
def name(self):
"""Return the name of the thermostat."""
return self._client.name
@property
def precision(self):
"""Return the precision of the system.
Venstar temperature values are passed back and forth in the
API as whole degrees C or F.
"""
return PRECISION_WHOLE
@property
def temperature_unit(self):
"""Return the unit of measurement, as defined by the API."""
if self._client.tempunits == self._client.TEMPUNITS_F:
return TEMP_FAHRENHEIT
else:
return TEMP_CELSIUS
@property
def fan_list(self):
"""Return the list of available fan modes."""
return VALID_FAN_STATES
@property
def operation_list(self):
"""Return the list of available operation modes."""
return VALID_THERMOSTAT_MODES
@property
def current_temperature(self):
"""Return the current temperature."""
return self._client.get_indoor_temp()
@property
def current_humidity(self):
"""Return the current humidity."""
return self._client.get_indoor_humidity()
@property
def current_operation(self):
"""Return current operation ie. heat, cool, idle."""
if self._client.mode == self._client.MODE_HEAT:
return STATE_HEAT
elif self._client.mode == self._client.MODE_COOL:
return STATE_COOL
elif self._client.mode == self._client.MODE_AUTO:
return STATE_AUTO
else:
return STATE_OFF
@property
def current_fan_mode(self):
"""Return the fan setting."""
if self._client.fan == self._client.FAN_AUTO:
return STATE_AUTO
else:
return STATE_ON
@property
def device_state_attributes(self):
"""Return the optional state attributes."""
return {
ATTR_FAN_STATE: self._client.fanstate,
ATTR_HVAC_STATE: self._client.state,
}
@property
def target_temperature(self):
"""Return the target temperature we try to reach."""
if self._client.mode == self._client.MODE_HEAT:
return self._client.heattemp
elif self._client.mode == self._client.MODE_COOL:
return self._client.cooltemp
else:
return None
@property
def target_temperature_low(self):
"""Return the lower bound temp if auto mode is on."""
if self._client.mode == self._client.MODE_AUTO:
return self._client.heattemp
else:
return None
@property
def target_temperature_high(self):
"""Return the upper bound temp if auto mode is on."""
if self._client.mode == self._client.MODE_AUTO:
return self._client.cooltemp
else:
return None
@property
def target_humidity(self):
"""Return the humidity we try to reach."""
return self._client.hum_setpoint
@property
def min_humidity(self):
"""Return the minimum humidity. Hardcoded to 0 in API."""
return 0
@property
def max_humidity(self):
"""Return the maximum humidity. Hardcoded to 60 in API."""
return 60
def _set_operation_mode(self, operation_mode):
"""Change the operation mode (internal)."""
if operation_mode == STATE_HEAT:
success = self._client.set_mode(self._client.MODE_HEAT)
elif operation_mode == STATE_COOL:
success = self._client.set_mode(self._client.MODE_COOL)
elif operation_mode == STATE_AUTO:
success = self._client.set_mode(self._client.MODE_AUTO)
else:
success = self._client.set_mode(self._client.MODE_OFF)
if not success:
_LOGGER.error("Failed to change the operation mode")
return success
def set_temperature(self, **kwargs):
"""Set a new target temperature."""
set_temp = True
operation_mode = kwargs.get(ATTR_OPERATION_MODE, self._client.mode)
temp_low = kwargs.get(ATTR_TARGET_TEMP_LOW)
temp_high = kwargs.get(ATTR_TARGET_TEMP_HIGH)
temperature = kwargs.get(ATTR_TEMPERATURE)
if operation_mode != self._client.mode:
set_temp = self._set_operation_mode(operation_mode)
if set_temp:
if operation_mode == self._client.MODE_HEAT:
success = self._client.set_setpoints(
temperature, self._client.cooltemp)
elif operation_mode == self._client.MODE_COOL:
success = self._client.set_setpoints(
self._client.heattemp, temperature)
elif operation_mode == self._client.MODE_AUTO:
success = self._client.set_setpoints(temp_low, temp_high)
else:
_LOGGER.error("The thermostat is currently not in a mode "
"that supports target temperature")
if not success:
_LOGGER.error("Failed to change the temperature")
def set_fan_mode(self, fan):
"""Set new target fan mode."""
if fan == STATE_ON:
success = self._client.set_fan(self._client.FAN_ON)
else:
success = self._client.set_fan(self._client.FAN_AUTO)
if not success:
_LOGGER.error("Failed to change the fan mode")
def set_operation_mode(self, operation_mode):
"""Set new target operation mode."""
self._set_operation_mode(operation_mode)
def set_humidity(self, humidity):
"""Set new target humidity."""
success = self._client.set_hum_setpoint(humidity)
if not success:
_LOGGER.error("Failed to change the target humidity level")

View File

@ -8,16 +8,16 @@ import asyncio
import logging
from homeassistant.components.climate import (
STATE_ECO, STATE_GAS, STATE_AUTO, STATE_COOL, STATE_HEAT, STATE_ELECTRIC,
STATE_FAN_ONLY, STATE_HEAT_PUMP, ATTR_TEMPERATURE, STATE_HIGH_DEMAND,
STATE_PERFORMANCE, ATTR_TARGET_TEMP_LOW, ATTR_CURRENT_HUMIDITY,
ATTR_TARGET_TEMP_HIGH, ClimateDevice, SUPPORT_TARGET_TEMPERATURE,
ATTR_CURRENT_HUMIDITY, ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_LOW,
ATTR_TEMPERATURE, STATE_AUTO, STATE_COOL, STATE_ECO, STATE_ELECTRIC,
STATE_FAN_ONLY, STATE_GAS, STATE_HEAT, STATE_HEAT_PUMP, STATE_HIGH_DEMAND,
STATE_PERFORMANCE, SUPPORT_AUX_HEAT, SUPPORT_AWAY_MODE, SUPPORT_FAN_MODE,
SUPPORT_OPERATION_MODE, SUPPORT_TARGET_TEMPERATURE,
SUPPORT_TARGET_TEMPERATURE_HIGH, SUPPORT_TARGET_TEMPERATURE_LOW,
SUPPORT_OPERATION_MODE, SUPPORT_AWAY_MODE, SUPPORT_FAN_MODE,
SUPPORT_AUX_HEAT)
ClimateDevice)
from homeassistant.components.wink import DOMAIN, WinkDevice
from homeassistant.const import (
STATE_ON, STATE_OFF, TEMP_CELSIUS, STATE_UNKNOWN, PRECISION_TENTHS)
PRECISION_TENTHS, STATE_OFF, STATE_ON, STATE_UNKNOWN, TEMP_CELSIUS)
from homeassistant.helpers.temperature import display_temp as show_temp
_LOGGER = logging.getLogger(__name__)
@ -30,6 +30,8 @@ ATTR_SCHEDULE_ENABLED = 'schedule_enabled'
ATTR_SMART_TEMPERATURE = 'smart_temperature'
ATTR_TOTAL_CONSUMPTION = 'total_consumption'
ATTR_VACATION_MODE = 'vacation_mode'
ATTR_HEAT_ON = 'heat_on'
ATTR_COOL_ON = 'cool_on'
DEPENDENCIES = ['wink']
@ -93,7 +95,7 @@ class WinkThermostat(WinkDevice, ClimateDevice):
@asyncio.coroutine
def async_added_to_hass(self):
"""Callback when entity is added to hass."""
"""Call when entity is added to hass."""
self.hass.data[DOMAIN]['entities']['climate'].append(self)
@property
@ -104,7 +106,7 @@ class WinkThermostat(WinkDevice, ClimateDevice):
@property
def device_state_attributes(self):
"""Return the optional state attributes."""
"""Return the optional device state attributes."""
data = {}
target_temp_high = self.target_temperature_high
target_temp_low = self.target_temperature_low
@ -131,6 +133,12 @@ class WinkThermostat(WinkDevice, ClimateDevice):
if self.eco_target:
data[ATTR_ECO_TARGET] = self.eco_target
if self.heat_on:
data[ATTR_HEAT_ON] = self.heat_on
if self.cool_on:
data[ATTR_COOL_ON] = self.cool_on
current_humidity = self.current_humidity
if current_humidity is not None:
data[ATTR_CURRENT_HUMIDITY] = current_humidity
@ -174,6 +182,16 @@ class WinkThermostat(WinkDevice, ClimateDevice):
"""Return status of if the thermostat has detected occupancy."""
return self.wink.occupied()
@property
def heat_on(self):
"""Return whether or not the heat is actually heating."""
return self.wink.heat_on()
@property
def cool_on(self):
"""Return whether or not the heat is actually heating."""
return self.wink.heat_on()
@property
def current_operation(self):
"""Return current operation ie. heat, cool, idle."""
@ -385,7 +403,7 @@ class WinkAC(WinkDevice, ClimateDevice):
@property
def device_state_attributes(self):
"""Return the optional state attributes."""
"""Return the optional device state attributes."""
data = {}
target_temp_high = self.target_temperature_high
target_temp_low = self.target_temperature_low
@ -508,7 +526,7 @@ class WinkWaterHeater(WinkDevice, ClimateDevice):
@property
def device_state_attributes(self):
"""Return the optional state attributes."""
"""Return the optional device state attributes."""
data = {}
data[ATTR_VACATION_MODE] = self.wink.vacation_mode_enabled()
data[ATTR_RHEEM_TYPE] = self.wink.rheem_type()

View File

@ -1,4 +1,9 @@
"""Component to integrate the Home Assistant cloud."""
"""
Component to integrate the Home Assistant cloud.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/cloud/
"""
import asyncio
from datetime import datetime
import json
@ -26,18 +31,18 @@ REQUIREMENTS = ['warrant==0.6.1']
_LOGGER = logging.getLogger(__name__)
CONF_ALEXA = 'alexa'
CONF_GOOGLE_ACTIONS = 'google_actions'
CONF_FILTER = 'filter'
CONF_ALIASES = 'aliases'
CONF_COGNITO_CLIENT_ID = 'cognito_client_id'
CONF_ENTITY_CONFIG = 'entity_config'
CONF_FILTER = 'filter'
CONF_GOOGLE_ACTIONS = 'google_actions'
CONF_RELAYER = 'relayer'
CONF_USER_POOL_ID = 'user_pool_id'
CONF_ALIASES = 'aliases'
MODE_DEV = 'development'
DEFAULT_MODE = 'production'
DEPENDENCIES = ['http']
CONF_ENTITY_CONFIG = 'entity_config'
MODE_DEV = 'development'
ALEXA_ENTITY_SCHEMA = vol.Schema({
vol.Optional(alexa_sh.CONF_DESCRIPTION): cv.string,
@ -149,7 +154,7 @@ class Cloud:
@property
def subscription_expired(self):
"""Return a boolen if the subscription has expired."""
"""Return a boolean if the subscription has expired."""
return dt_util.utcnow() > self.expiration_date
@property
@ -195,8 +200,8 @@ class Cloud:
if not jwt_success:
return False
self.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START,
self._start_cloud)
self.hass.bus.async_listen_once(
EVENT_HOMEASSISTANT_START, self._start_cloud)
return True
@ -248,7 +253,7 @@ class Cloud:
for token in 'id_token', 'access_token':
self._decode_claims(info[token])
except ValueError as err: # Raised when token is invalid
_LOGGER.warning('Found invalid token %s: %s', token, err)
_LOGGER.warning("Found invalid token %s: %s", token, err)
return
self.id_token = info['id_token']
@ -282,15 +287,15 @@ class Cloud:
header = jwt.get_unverified_header(token)
except jose_exceptions.JWTError as err:
raise ValueError(str(err)) from None
kid = header.get("kid")
kid = header.get('kid')
if kid is None:
raise ValueError('No kid in header')
raise ValueError("No kid in header")
# Locate the key for this kid
key = None
for key_dict in self.jwt_keyset["keys"]:
if key_dict["kid"] == kid:
for key_dict in self.jwt_keyset['keys']:
if key_dict['kid'] == kid:
key = key_dict
break
if not key:

View File

@ -1,7 +1,6 @@
"""Package to communicate with the authentication API."""
import logging
_LOGGER = logging.getLogger(__name__)
@ -22,7 +21,7 @@ class UserNotConfirmed(CloudError):
class ExpiredCode(CloudError):
"""Raised when an expired code is encoutered."""
"""Raised when an expired code is encountered."""
class InvalidCode(CloudError):
@ -38,7 +37,7 @@ class PasswordChangeRequired(CloudError):
class UnknownError(CloudError):
"""Raised when an unknown error occurrs."""
"""Raised when an unknown error occurs."""
AWS_EXCEPTIONS = {
@ -98,7 +97,7 @@ def resend_email_confirm(cloud, email):
def forgot_password(cloud, email):
"""Initiate forgotten password flow."""
"""Initialize forgotten password flow."""
from botocore.exceptions import ClientError
cognito = _cognito(cloud, username=email)

View File

@ -3,8 +3,8 @@ import asyncio
from functools import wraps
import logging
import voluptuous as vol
import async_timeout
import voluptuous as vol
from homeassistant.components.http import (
HomeAssistantView, RequestDataValidator)
@ -17,7 +17,7 @@ _LOGGER = logging.getLogger(__name__)
@asyncio.coroutine
def async_setup(hass):
"""Initialize the HTTP api."""
"""Initialize the HTTP API."""
hass.http.register_view(CloudLoginView)
hass.http.register_view(CloudLogoutView)
hass.http.register_view(CloudAccountView)
@ -40,7 +40,7 @@ _CLOUD_ERRORS = {
def _handle_cloud_errors(handler):
"""Helper method to handle auth errors."""
"""Handle auth errors."""
@asyncio.coroutine
@wraps(handler)
def error_handler(view, request, *args, **kwargs):

View File

@ -12,7 +12,6 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession
from . import auth_api
from .const import MESSAGE_EXPIRATION
HANDLERS = Registry()
_LOGGER = logging.getLogger(__name__)
@ -85,7 +84,7 @@ class CloudIoT:
})
self.tries = 0
_LOGGER.info('Connected')
_LOGGER.info("Connected")
self.state = STATE_CONNECTED
while not client.closed:
@ -107,7 +106,7 @@ class CloudIoT:
disconnect_warn = 'Received invalid JSON.'
break
_LOGGER.debug('Received message: %s', msg)
_LOGGER.debug("Received message: %s", msg)
response = {
'msgid': msg['msgid'],
@ -126,14 +125,14 @@ class CloudIoT:
response['error'] = 'unknown-handler'
except Exception: # pylint: disable=broad-except
_LOGGER.exception('Error handling message')
_LOGGER.exception("Error handling message")
response['error'] = 'exception'
_LOGGER.debug('Publishing message: %s', response)
_LOGGER.debug("Publishing message: %s", response)
yield from client.send_json(response)
except auth_api.CloudError:
_LOGGER.warning('Unable to connect: Unable to refresh token.')
_LOGGER.warning("Unable to connect: Unable to refresh token.")
except client_exceptions.WSServerHandshakeError as err:
if err.code == 401:
@ -141,18 +140,18 @@ class CloudIoT:
self.close_requested = True
# Should we notify user?
else:
_LOGGER.warning('Unable to connect: %s', err)
_LOGGER.warning("Unable to connect: %s", err)
except client_exceptions.ClientError as err:
_LOGGER.warning('Unable to connect: %s', err)
_LOGGER.warning("Unable to connect: %s", err)
except Exception: # pylint: disable=broad-except
if not self.close_requested:
_LOGGER.exception('Unexpected error')
_LOGGER.exception("Unexpected error")
finally:
if disconnect_warn is not None:
_LOGGER.warning('Connection closed: %s', disconnect_warn)
_LOGGER.warning("Connection closed: %s", disconnect_warn)
if remove_hass_stop_listener is not None:
remove_hass_stop_listener()
@ -169,7 +168,7 @@ class CloudIoT:
self.tries += 1
try:
# Sleep 0, 5, 10, 15 up to 30 seconds between retries
# Sleep 0, 5, 10, 15 ... up to 30 seconds between retries
self.retry_task = hass.async_add_job(asyncio.sleep(
min(30, (self.tries - 1) * 5), loop=hass.loop))
yield from self.retry_task
@ -205,8 +204,8 @@ def async_handle_message(hass, cloud, handler_name, payload):
@asyncio.coroutine
def async_handle_alexa(hass, cloud, payload):
"""Handle an incoming IoT message for Alexa."""
result = yield from alexa.async_handle_message(hass, cloud.alexa_config,
payload)
result = yield from alexa.async_handle_message(
hass, cloud.alexa_config, payload)
return result
@ -214,8 +213,8 @@ def async_handle_alexa(hass, cloud, payload):
@asyncio.coroutine
def async_handle_google_actions(hass, cloud, payload):
"""Handle an incoming IoT message for Google Actions."""
result = yield from ga.async_handle_message(hass, cloud.gactions_config,
payload)
result = yield from ga.async_handle_message(
hass, cloud.gactions_config, payload)
return result
@ -227,9 +226,9 @@ def async_handle_cloud(hass, cloud, payload):
if action == 'logout':
yield from cloud.logout()
_LOGGER.error('You have been logged out from Home Assistant cloud: %s',
_LOGGER.error("You have been logged out from Home Assistant cloud: %s",
payload['reason'])
else:
_LOGGER.warning('Received unknown cloud action: %s', action)
_LOGGER.warning("Received unknown cloud action: %s", action)
return None

View File

@ -8,11 +8,11 @@ import logging
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.const import (
CONF_HOST, CONF_NAME, CONF_TOKEN, CONF_PIN, EVENT_HOMEASSISTANT_STOP)
from homeassistant.helpers import (discovery)
from homeassistant.helpers.dispatcher import (dispatcher_send)
CONF_HOST, CONF_NAME, CONF_PIN, CONF_TOKEN, EVENT_HOMEASSISTANT_STOP)
from homeassistant.helpers import discovery
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.dispatcher import dispatcher_send
REQUIREMENTS = ['pycomfoconnect==0.3']
@ -115,7 +115,7 @@ class ComfoConnectBridge(object):
self.comfoconnect.disconnect()
def sensor_callback(self, var, value):
"""Callback function for sensor updates."""
"""Call function for sensor updates."""
_LOGGER.debug("Got value from bridge: %d = %d", var, value)
from pycomfoconnect import (

View File

@ -5,9 +5,11 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/cover.homematic/
"""
import logging
from homeassistant.const import STATE_UNKNOWN
from homeassistant.components.cover import CoverDevice, ATTR_POSITION
from homeassistant.components.cover import CoverDevice, ATTR_POSITION,\
ATTR_TILT_POSITION
from homeassistant.components.homematic import HMDevice, ATTR_DISCOVER_DEVICES
from homeassistant.const import STATE_UNKNOWN
_LOGGER = logging.getLogger(__name__)
@ -69,3 +71,40 @@ class HMCover(HMDevice, CoverDevice):
"""Generate a data dictoinary (self._data) from metadata."""
self._state = "LEVEL"
self._data.update({self._state: STATE_UNKNOWN})
if "LEVEL_2" in self._hmdevice.WRITENODE:
self._data.update(
{'LEVEL_2': STATE_UNKNOWN})
@property
def current_cover_tilt_position(self):
"""Return current position of cover tilt.
None is unknown, 0 is closed, 100 is fully open.
"""
if 'LEVEL_2' not in self._data:
return None
return int(self._data.get('LEVEL_2', 0) * 100)
def set_cover_tilt_position(self, **kwargs):
"""Move the cover tilt to a specific position."""
if "LEVEL_2" in self._data and ATTR_TILT_POSITION in kwargs:
position = float(kwargs[ATTR_TILT_POSITION])
position = min(100, max(0, position))
level = position / 100.0
self._hmdevice.set_cover_tilt_position(level, self._channel)
def open_cover_tilt(self, **kwargs):
"""Open the cover tilt."""
if "LEVEL_2" in self._data:
self._hmdevice.open_slats()
def close_cover_tilt(self, **kwargs):
"""Close the cover tilt."""
if "LEVEL_2" in self._data:
self._hmdevice.close_slats()
def stop_cover_tilt(self, **kwargs):
"""Stop cover tilt."""
if "LEVEL_2" in self._data:
self.stop_cover(**kwargs)

View File

@ -5,17 +5,18 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/cover.knx/
"""
import asyncio
import voluptuous as vol
from homeassistant.components.knx import DATA_KNX, ATTR_DISCOVER_DEVICES
from homeassistant.helpers.event import async_track_utc_time_change
from homeassistant.components.cover import (
CoverDevice, PLATFORM_SCHEMA, SUPPORT_OPEN, SUPPORT_CLOSE,
SUPPORT_SET_POSITION, SUPPORT_STOP, SUPPORT_SET_TILT_POSITION,
ATTR_POSITION, ATTR_TILT_POSITION)
from homeassistant.core import callback
ATTR_POSITION, ATTR_TILT_POSITION, PLATFORM_SCHEMA, SUPPORT_CLOSE,
SUPPORT_OPEN, SUPPORT_SET_POSITION, SUPPORT_SET_TILT_POSITION,
SUPPORT_STOP, CoverDevice)
from homeassistant.components.knx import ATTR_DISCOVER_DEVICES, DATA_KNX
from homeassistant.const import CONF_NAME
from homeassistant.core import callback
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.event import async_track_utc_time_change
CONF_MOVE_LONG_ADDRESS = 'move_long_address'
CONF_MOVE_SHORT_ADDRESS = 'move_short_address'
@ -50,20 +51,16 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices,
discovery_info=None):
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Set up cover(s) for KNX platform."""
if DATA_KNX not in hass.data \
or not hass.data[DATA_KNX].initialized:
return False
if DATA_KNX not in hass.data or not hass.data[DATA_KNX].initialized:
return
if discovery_info is not None:
async_add_devices_discovery(hass, discovery_info, async_add_devices)
else:
async_add_devices_config(hass, config, async_add_devices)
return True
@callback
def async_add_devices_discovery(hass, discovery_info, async_add_devices):
@ -114,7 +111,7 @@ class KNXCover(CoverDevice):
"""Register callbacks to update hass after device was changed."""
@asyncio.coroutine
def after_update_callback(device):
"""Callback after device was updated."""
"""Call after device was updated."""
# pylint: disable=unused-argument
yield from self.async_update_ha_state()
self.device.register_device_updated_cb(after_update_callback)
@ -209,7 +206,7 @@ class KNXCover(CoverDevice):
@callback
def auto_updater_hook(self, now):
"""Callback for autoupdater."""
"""Call for the autoupdater."""
# pylint: disable=unused-argument
self.async_schedule_update_ha_state()
if self.device.position_reached():

View File

@ -0,0 +1,76 @@
"""
Support for Lutron shades.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/cover.lutron/
"""
import logging
from homeassistant.components.cover import (
CoverDevice, SUPPORT_OPEN, SUPPORT_CLOSE, SUPPORT_SET_POSITION,
ATTR_POSITION)
from homeassistant.components.lutron import (
LutronDevice, LUTRON_DEVICES, LUTRON_CONTROLLER)
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['lutron']
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the Lutron shades."""
devs = []
for (area_name, device) in hass.data[LUTRON_DEVICES]['cover']:
dev = LutronCover(area_name, device, hass.data[LUTRON_CONTROLLER])
devs.append(dev)
add_devices(devs, True)
return True
class LutronCover(LutronDevice, CoverDevice):
"""Representation of a Lutron shade."""
@property
def supported_features(self):
"""Flag supported features."""
return SUPPORT_OPEN | SUPPORT_CLOSE | SUPPORT_SET_POSITION
@property
def is_closed(self):
"""Return if the cover is closed."""
return self._lutron_device.last_level() < 1
@property
def current_cover_position(self):
"""Return the current position of cover."""
return self._lutron_device.last_level()
def close_cover(self, **kwargs):
"""Close the cover."""
self._lutron_device.level = 0
def open_cover(self, **kwargs):
"""Open the cover."""
self._lutron_device.level = 100
def set_cover_position(self, **kwargs):
"""Move the shade to a specific position."""
if ATTR_POSITION in kwargs:
position = kwargs[ATTR_POSITION]
self._lutron_device.level = position
def update(self):
"""Call when forcing a refresh of the device."""
# Reading the property (rather than last_level()) fetchs value
level = self._lutron_device.level
_LOGGER.debug("Lutron ID: %d updated to %f",
self._lutron_device.id, level)
@property
def device_state_attributes(self):
"""Return the state attributes."""
attr = {}
attr['Lutron Integration ID'] = self._lutron_device.id
return attr

View File

@ -5,12 +5,12 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/cover.mysensors/
"""
from homeassistant.components import mysensors
from homeassistant.components.cover import CoverDevice, ATTR_POSITION, DOMAIN
from homeassistant.const import STATE_ON, STATE_OFF
from homeassistant.components.cover import ATTR_POSITION, DOMAIN, CoverDevice
from homeassistant.const import STATE_OFF, STATE_ON
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the mysensors platform for covers."""
"""Set up the MySensors platform for covers."""
mysensors.setup_mysensors_platform(
hass, DOMAIN, discovery_info, MySensorsCover, add_devices=add_devices)

View File

@ -29,12 +29,12 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
})
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the RFXtrx cover."""
import RFXtrx as rfxtrxmod
covers = rfxtrx.get_devices_from_config(config, RfxtrxCover)
add_devices_callback(covers)
add_devices(covers)
def cover_update(event):
"""Handle cover updates from the RFXtrx gateway."""
@ -45,7 +45,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
new_device = rfxtrx.get_new_device(event, config, RfxtrxCover)
if new_device:
add_devices_callback([new_device])
add_devices([new_device])
rfxtrx.apply_received_command(event)
@ -59,7 +59,7 @@ class RfxtrxCover(rfxtrx.RfxtrxDevice, CoverDevice):
@property
def should_poll(self):
"""No polling available in RFXtrx cover."""
"""Return the polling state. No polling available in RFXtrx cover."""
return False
@property

View File

@ -7,7 +7,7 @@ https://home-assistant.io/components/cover.tahoma/
import logging
from datetime import timedelta
from homeassistant.components.cover import CoverDevice, ENTITY_ID_FORMAT
from homeassistant.components.cover import CoverDevice
from homeassistant.components.tahoma import (
DOMAIN as TAHOMA_DOMAIN, TahomaDevice)
@ -30,11 +30,6 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
class TahomaCover(TahomaDevice, CoverDevice):
"""Representation a Tahoma Cover."""
def __init__(self, tahoma_device, controller):
"""Initialize the Tahoma device."""
super().__init__(tahoma_device, controller)
self.entity_id = ENTITY_ID_FORMAT.format(self.unique_id)
def update(self):
"""Update method."""
self.controller.get_states([self.tahoma_device])
@ -46,12 +41,16 @@ class TahomaCover(TahomaDevice, CoverDevice):
0 is closed, 100 is fully open.
"""
position = 100 - self.tahoma_device.active_states['core:ClosureState']
if position <= 5:
return 0
if position >= 95:
return 100
return position
try:
position = 100 - \
self.tahoma_device.active_states['core:ClosureState']
if position <= 5:
return 0
if position >= 95:
return 100
return position
except KeyError:
return None
def set_cover_position(self, position, **kwargs):
"""Move the cover to a specific position."""
@ -63,6 +62,14 @@ class TahomaCover(TahomaDevice, CoverDevice):
if self.current_cover_position is not None:
return self.current_cover_position == 0
@property
def device_class(self):
"""Return the class of the device."""
if self.tahoma_device.type == 'io:WindowOpenerVeluxIOComponent':
return 'window'
else:
return None
def open_cover(self, **kwargs):
"""Open the cover."""
self.apply_action('open')
@ -78,10 +85,3 @@ class TahomaCover(TahomaDevice, CoverDevice):
self.apply_action('setPosition', 'secured')
else:
self.apply_action('stopIdentify')
def device_class(self):
"""Return the class of this device, from component DEVICE_CLASSES."""
if self.tahoma_device.type == 'io:WindowOpenerVeluxIOComponent':
return 'window'
else:
return None

View File

@ -6,7 +6,7 @@ https://home-assistant.io/components/cover.wink/
"""
import asyncio
from homeassistant.components.cover import CoverDevice
from homeassistant.components.cover import CoverDevice, STATE_UNKNOWN
from homeassistant.components.wink import WinkDevice, DOMAIN
DEPENDENCIES = ['wink']
@ -31,25 +31,28 @@ class WinkCoverDevice(WinkDevice, CoverDevice):
@asyncio.coroutine
def async_added_to_hass(self):
"""Callback when entity is added to hass."""
"""Call when entity is added to hass."""
self.hass.data[DOMAIN]['entities']['cover'].append(self)
def close_cover(self, **kwargs):
"""Close the shade."""
"""Close the cover."""
self.wink.set_state(0)
def open_cover(self, **kwargs):
"""Open the shade."""
"""Open the cover."""
self.wink.set_state(1)
def set_cover_position(self, position, **kwargs):
"""Move the roller shutter to a specific position."""
"""Move the cover shutter to a specific position."""
self.wink.set_state(float(position)/100)
@property
def current_cover_position(self):
"""Return the current position of roller shutter."""
return int(self.wink.state()*100)
"""Return the current position of cover shutter."""
if self.wink.state() is not None:
return int(self.wink.state()*100)
else:
return STATE_UNKNOWN
@property
def is_closed(self):

View File

@ -59,7 +59,7 @@ class XiaomiGenericCover(XiaomiDevice, CoverDevice):
"""Move the cover to a specific position."""
self._write_to_hub(self._sid, **{self._data_key['pos']: str(position)})
def parse_data(self, data):
def parse_data(self, data, raw_data):
"""Parse data sent by gateway."""
if ATTR_CURTAIN_LEVEL in data:
self._pos = int(data[ATTR_CURTAIN_LEVEL])

View File

@ -5,11 +5,12 @@ For more details about this component, please refer to the documentation at
https://home-assistant.io/components/datadog/
"""
import logging
import voluptuous as vol
from homeassistant.const import (CONF_HOST, CONF_PORT, CONF_PREFIX,
EVENT_LOGBOOK_ENTRY, EVENT_STATE_CHANGED,
STATE_UNKNOWN)
from homeassistant.const import (
CONF_HOST, CONF_PORT, CONF_PREFIX, EVENT_LOGBOOK_ENTRY,
EVENT_STATE_CHANGED, STATE_UNKNOWN)
from homeassistant.helpers import state as state_helper
import homeassistant.helpers.config_validation as cv
@ -36,7 +37,7 @@ CONFIG_SCHEMA = vol.Schema({
def setup(hass, config):
"""Setup the Datadog component."""
"""Set up the Datadog component."""
from datadog import initialize, statsd
conf = config[DOMAIN]
@ -81,36 +82,19 @@ def setup(hass, config):
if isinstance(value, (float, int)):
attribute = "{}.{}".format(metric, key.replace(' ', '_'))
statsd.gauge(
attribute,
value,
sample_rate=sample_rate,
tags=tags
)
attribute, value, sample_rate=sample_rate, tags=tags)
_LOGGER.debug(
'Sent metric %s: %s (tags: %s)',
attribute,
value,
tags
)
"Sent metric %s: %s (tags: %s)", attribute, value, tags)
try:
value = state_helper.state_as_number(state)
except ValueError:
_LOGGER.debug(
'Error sending %s: %s (tags: %s)',
metric,
state.state,
tags
)
"Error sending %s: %s (tags: %s)", metric, state.state, tags)
return
statsd.gauge(
metric,
value,
sample_rate=sample_rate,
tags=tags
)
statsd.gauge(metric, value, sample_rate=sample_rate, tags=tags)
_LOGGER.debug('Sent metric %s: %s (tags: %s)', metric, value, tags)

View File

@ -4,20 +4,20 @@ Support for deCONZ devices.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/deconz/
"""
import asyncio
import logging
import voluptuous as vol
from homeassistant.components.discovery import SERVICE_DECONZ
from homeassistant.const import (
CONF_API_KEY, CONF_HOST, CONF_PORT, EVENT_HOMEASSISTANT_STOP)
from homeassistant.components.discovery import SERVICE_DECONZ
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers import discovery
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.util.json import load_json, save_json
REQUIREMENTS = ['pydeconz==23']
REQUIREMENTS = ['pydeconz==25']
_LOGGER = logging.getLogger(__name__)
@ -27,8 +27,8 @@ CONFIG_FILE = 'deconz.conf'
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
vol.Optional(CONF_HOST): cv.string,
vol.Optional(CONF_API_KEY): cv.string,
vol.Optional(CONF_HOST): cv.string,
vol.Optional(CONF_PORT, default=80): cv.port,
})
}, extra=vol.ALLOW_EXTRA)
@ -53,14 +53,14 @@ Unlock your deCONZ gateway to register with Home Assistant.
@asyncio.coroutine
def async_setup(hass, config):
"""Setup services and configuration for deCONZ component."""
"""Set up services and configuration for deCONZ component."""
result = False
config_file = yield from hass.async_add_job(
load_json, hass.config.path(CONFIG_FILE))
@asyncio.coroutine
def async_deconz_discovered(service, discovery_info):
"""Called when deCONZ gateway has been found."""
"""Call when deCONZ gateway has been found."""
deconz_config = {}
deconz_config[CONF_HOST] = discovery_info.get(CONF_HOST)
deconz_config[CONF_PORT] = discovery_info.get(CONF_PORT)
@ -85,17 +85,18 @@ def async_setup(hass, config):
@asyncio.coroutine
def async_setup_deconz(hass, config, deconz_config):
"""Setup deCONZ session.
"""Set up a deCONZ session.
Load config, group, light and sensor data for server information.
Start websocket for push notification of state changes from deCONZ.
"""
_LOGGER.debug("deCONZ config %s", deconz_config)
from pydeconz import DeconzSession
websession = async_get_clientsession(hass)
deconz = DeconzSession(hass.loop, websession, **deconz_config)
result = yield from deconz.async_load_parameters()
if result is False:
_LOGGER.error("Failed to communicate with deCONZ.")
_LOGGER.error("Failed to communicate with deCONZ")
return False
hass.data[DOMAIN] = deconz
@ -125,8 +126,7 @@ def async_setup_deconz(hass, config, deconz_config):
data = call.data.get(SERVICE_DATA)
yield from deconz.async_put_state(field, data)
hass.services.async_register(
DOMAIN, 'configure', async_configure,
schema=SERVICE_SCHEMA)
DOMAIN, 'configure', async_configure, schema=SERVICE_SCHEMA)
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, deconz.close)
return True
@ -146,9 +146,8 @@ def async_request_configuration(hass, config, deconz_config):
deconz_config[CONF_API_KEY] = api_key
result = yield from async_setup_deconz(hass, config, deconz_config)
if result:
yield from hass.async_add_job(save_json,
hass.config.path(CONFIG_FILE),
deconz_config)
yield from hass.async_add_job(
save_json, hass.config.path(CONFIG_FILE), deconz_config)
configurator.async_request_done(request_id)
return
else:

View File

@ -25,9 +25,7 @@ _LOGGER = logging.getLogger(__name__)
CONF_PUB_KEY = 'pub_key'
CONF_SSH_KEY = 'ssh_key'
DEFAULT_SSH_PORT = 22
SECRET_GROUP = 'Password or SSH Key'
PLATFORM_SCHEMA = vol.All(
@ -118,20 +116,10 @@ class AsusWrtDeviceScanner(DeviceScanner):
self.port = config[CONF_PORT]
if self.protocol == 'ssh':
if not (self.ssh_key or self.password):
_LOGGER.error("No password or private key specified")
self.success_init = False
return
self.connection = SshConnection(
self.host, self.port, self.username, self.password,
self.ssh_key, self.mode == 'ap')
else:
if not self.password:
_LOGGER.error("No password specified")
self.success_init = False
return
self.connection = TelnetConnection(
self.host, self.port, self.username, self.password,
self.mode == 'ap')
@ -177,11 +165,16 @@ class AsusWrtDeviceScanner(DeviceScanner):
"""
devices = {}
devices.update(self._get_wl())
devices = self._get_arp(devices)
devices = self._get_neigh(devices)
devices.update(self._get_arp())
devices.update(self._get_neigh(devices))
if not self.mode == 'ap':
devices.update(self._get_leases(devices))
return devices
ret_devices = {}
for key in devices:
if devices[key].ip is not None:
ret_devices[key] = devices[key]
return ret_devices
def _get_wl(self):
lines = self.connection.run_command(_WL_CMD)
@ -219,18 +212,13 @@ class AsusWrtDeviceScanner(DeviceScanner):
result = _parse_lines(lines, _IP_NEIGH_REGEX)
devices = {}
for device in result:
if device['mac']:
if device['mac'] is not None:
mac = device['mac'].upper()
devices[mac] = Device(mac, None, None)
else:
cur_devices = {
k: v for k, v in
cur_devices.items() if v.ip != device['ip']
}
cur_devices.update(devices)
return cur_devices
old_ip = cur_devices.get(mac, {}).ip or None
devices[mac] = Device(mac, device.get('ip', old_ip), None)
return devices
def _get_arp(self, cur_devices):
def _get_arp(self):
lines = self.connection.run_command(_ARP_CMD)
if not lines:
return {}
@ -240,13 +228,7 @@ class AsusWrtDeviceScanner(DeviceScanner):
if device['mac']:
mac = device['mac'].upper()
devices[mac] = Device(mac, device['ip'], None)
else:
cur_devices = {
k: v for k, v in
cur_devices.items() if v.ip != device['ip']
}
cur_devices.update(devices)
return cur_devices
return devices
class _Connection:
@ -272,7 +254,7 @@ class SshConnection(_Connection):
def __init__(self, host, port, username, password, ssh_key, ap):
"""Initialize the SSH connection properties."""
super(SshConnection, self).__init__()
super().__init__()
self._ssh = None
self._host = host
@ -322,7 +304,7 @@ class SshConnection(_Connection):
self._ssh.login(self._host, self._username,
password=self._password, port=self._port)
super(SshConnection, self).connect()
super().connect()
def disconnect(self): \
# pylint: disable=broad-except
@ -334,7 +316,7 @@ class SshConnection(_Connection):
finally:
self._ssh = None
super(SshConnection, self).disconnect()
super().disconnect()
class TelnetConnection(_Connection):
@ -342,7 +324,7 @@ class TelnetConnection(_Connection):
def __init__(self, host, port, username, password, ap):
"""Initialize the Telnet connection properties."""
super(TelnetConnection, self).__init__()
super().__init__()
self._telnet = None
self._host = host
@ -361,7 +343,6 @@ class TelnetConnection(_Connection):
try:
if not self.connected:
self.connect()
self._telnet.write('{}\n'.format(command).encode('ascii'))
data = (self._telnet.read_until(self._prompt_string).
split(b'\n')[1:-1])
@ -392,7 +373,7 @@ class TelnetConnection(_Connection):
self._telnet.write((self._password + '\n').encode('ascii'))
self._prompt_string = self._telnet.read_until(b'#').split(b'\n')[-1]
super(TelnetConnection, self).connect()
super().connect()
def disconnect(self): \
# pylint: disable=broad-except
@ -402,4 +383,4 @@ class TelnetConnection(_Connection):
except Exception:
pass
super(TelnetConnection, self).disconnect()
super().disconnect()

View File

@ -14,8 +14,8 @@ from aiohttp import web
import voluptuous as vol
from homeassistant.components.device_tracker import (
PLATFORM_SCHEMA, ATTR_ATTRIBUTES, ATTR_DEV_ID, ATTR_HOST_NAME, ATTR_MAC,
ATTR_GPS, ATTR_GPS_ACCURACY)
ATTR_ATTRIBUTES, ATTR_DEV_ID, ATTR_GPS, ATTR_GPS_ACCURACY, ATTR_HOST_NAME,
ATTR_MAC, PLATFORM_SCHEMA)
from homeassistant.components.http import HomeAssistantView
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
from homeassistant.core import callback
@ -24,35 +24,33 @@ import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.event import async_track_time_interval
REQUIREMENTS = ['aioautomatic==0.6.4']
DEPENDENCIES = ['http']
_LOGGER = logging.getLogger(__name__)
CONF_CLIENT_ID = 'client_id'
CONF_SECRET = 'secret'
CONF_DEVICES = 'devices'
CONF_CURRENT_LOCATION = 'current_location'
DEFAULT_TIMEOUT = 5
DEFAULT_SCOPE = ['location', 'trip', 'vehicle:events', 'vehicle:profile']
FULL_SCOPE = DEFAULT_SCOPE + ['current_location']
ATTR_FUEL_LEVEL = 'fuel_level'
EVENT_AUTOMATIC_UPDATE = 'automatic_update'
AUTOMATIC_CONFIG_FILE = '.automatic/session-{}.json'
CONF_CLIENT_ID = 'client_id'
CONF_CURRENT_LOCATION = 'current_location'
CONF_DEVICES = 'devices'
CONF_SECRET = 'secret'
DATA_CONFIGURING = 'automatic_configurator_clients'
DATA_REFRESH_TOKEN = 'refresh_token'
DEFAULT_SCOPE = ['location', 'trip', 'vehicle:events', 'vehicle:profile']
DEFAULT_TIMEOUT = 5
DEPENDENCIES = ['http']
EVENT_AUTOMATIC_UPDATE = 'automatic_update'
FULL_SCOPE = DEFAULT_SCOPE + ['current_location']
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_CLIENT_ID): cv.string,
vol.Required(CONF_SECRET): cv.string,
vol.Optional(CONF_CURRENT_LOCATION, default=False): cv.boolean,
vol.Optional(CONF_DEVICES, default=None): vol.All(
cv.ensure_list, [cv.string])
vol.Optional(CONF_DEVICES, default=None):
vol.All(cv.ensure_list, [cv.string]),
})
@ -142,7 +140,7 @@ def async_setup_scanner(hass, config, async_see, discovery_info=None):
@asyncio.coroutine
def initialize_callback(code, state):
"""Callback after OAuth2 response is returned."""
"""Call after OAuth2 response is returned."""
try:
session = yield from client.create_session_from_oauth_code(
code, state)
@ -181,12 +179,12 @@ class AutomaticAuthCallbackView(HomeAssistantView):
return response
else:
_LOGGER.error(
"Error authorizing Automatic. Invalid response returned.")
"Error authorizing Automatic. Invalid response returned")
return response
if DATA_CONFIGURING not in hass.data or \
params['state'] not in hass.data[DATA_CONFIGURING]:
_LOGGER.error("Automatic configuration request not found.")
_LOGGER.error("Automatic configuration request not found")
return response
code = params['code']
@ -220,16 +218,15 @@ class AutomaticData(object):
@asyncio.coroutine
def handle_event(self, name, event):
"""Coroutine to update state for a realtime event."""
"""Coroutine to update state for a real time event."""
import aioautomatic
# Fire a hass event
self.hass.bus.async_fire(EVENT_AUTOMATIC_UPDATE, event.data)
if event.vehicle.id not in self.vehicle_info:
# If vehicle hasn't been seen yet, request the detailed
# info for this vehicle.
_LOGGER.info("New vehicle found.")
_LOGGER.info("New vehicle found")
try:
vehicle = yield from event.get_vehicle()
except aioautomatic.exceptions.AutomaticError as err:
@ -240,7 +237,7 @@ class AutomaticData(object):
if event.created_at < self.vehicle_seen[event.vehicle.id]:
# Skip events received out of order
_LOGGER.debug("Skipping out of order event. Event Created %s. "
"Last seen event: %s.", event.created_at,
"Last seen event: %s", event.created_at,
self.vehicle_seen[event.vehicle.id])
return
self.vehicle_seen[event.vehicle.id] = event.created_at
@ -270,13 +267,13 @@ class AutomaticData(object):
self.ws_close_requested = False
if self.ws_reconnect_handle is not None:
_LOGGER.debug("Retrying websocket connection.")
_LOGGER.debug("Retrying websocket connection")
try:
ws_loop_future = yield from self.client.ws_connect()
except aioautomatic.exceptions.UnauthorizedClientError:
_LOGGER.error("Client unauthorized for websocket connection. "
"Ensure Websocket is selected in the Automatic "
"developer application event delivery preferences.")
"developer application event delivery preferences")
return
except aioautomatic.exceptions.AutomaticError as err:
if self.ws_reconnect_handle is None:
@ -290,14 +287,14 @@ class AutomaticData(object):
self.ws_reconnect_handle()
self.ws_reconnect_handle = None
_LOGGER.info("Websocket connected.")
_LOGGER.info("Websocket connected")
try:
yield from ws_loop_future
except aioautomatic.exceptions.AutomaticError as err:
_LOGGER.error(str(err))
_LOGGER.info("Websocket closed.")
_LOGGER.info("Websocket closed")
# If websocket was close was not requested, attempt to reconnect
if not self.ws_close_requested:

View File

@ -15,7 +15,10 @@ import voluptuous as vol
import homeassistant.components.mqtt as mqtt
import homeassistant.helpers.config_validation as cv
from homeassistant.components import zone as zone_comp
from homeassistant.components.device_tracker import PLATFORM_SCHEMA
from homeassistant.components.device_tracker import (
PLATFORM_SCHEMA, ATTR_SOURCE_TYPE, SOURCE_TYPE_BLUETOOTH_LE,
SOURCE_TYPE_GPS
)
from homeassistant.const import STATE_HOME
from homeassistant.core import callback
from homeassistant.util import slugify, decorator
@ -140,6 +143,11 @@ def _parse_see_args(message, subscribe_topic):
kwargs['attributes']['tid'] = message['tid']
if 'addr' in message:
kwargs['attributes']['address'] = message['addr']
if 't' in message:
if message['t'] == 'c':
kwargs['attributes'][ATTR_SOURCE_TYPE] = SOURCE_TYPE_GPS
if message['t'] == 'b':
kwargs['attributes'][ATTR_SOURCE_TYPE] = SOURCE_TYPE_BLUETOOTH_LE
return dev_id, kwargs

View File

@ -14,7 +14,9 @@ import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.components.device_tracker import (
DOMAIN, PLATFORM_SCHEMA, DeviceScanner)
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
from homeassistant.const import (
CONF_HOST, CONF_PORT, CONF_SSL, CONF_VERIFY_SSL,
CONF_PASSWORD, CONF_USERNAME)
CONF_HTTP_ID = 'http_id'
@ -22,6 +24,10 @@ _LOGGER = logging.getLogger(__name__)
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_HOST): cv.string,
vol.Optional(CONF_PORT, default=-1): cv.port,
vol.Optional(CONF_SSL, default=False): cv.boolean,
vol.Optional(CONF_VERIFY_SSL, default=True): vol.Any(
cv.boolean, cv.isfile),
vol.Required(CONF_PASSWORD): cv.string,
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_HTTP_ID): cv.string
@ -39,16 +45,23 @@ class TomatoDeviceScanner(DeviceScanner):
def __init__(self, config):
"""Initialize the scanner."""
host, http_id = config[CONF_HOST], config[CONF_HTTP_ID]
port = config[CONF_PORT]
username, password = config[CONF_USERNAME], config[CONF_PASSWORD]
self.ssl, self.verify_ssl = config[CONF_SSL], config[CONF_VERIFY_SSL]
if port == -1:
port = 80
if self.ssl:
port = 443
self.req = requests.Request(
'POST', 'http://{}/update.cgi'.format(host),
'POST', 'http{}://{}:{}/update.cgi'.format(
"s" if self.ssl else "", host, port
),
data={'_http_id': http_id, 'exec': 'devlist'},
auth=requests.auth.HTTPBasicAuth(username, password)).prepare()
self.parse_api_pattern = re.compile(r"(?P<param>\w*) = (?P<value>.*);")
self.logger = logging.getLogger("{}.{}".format(__name__, "Tomato"))
self.last_results = {"wldev": [], "dhcpd_lease": []}
self.success_init = self._update_tomato_info()
@ -74,10 +87,16 @@ class TomatoDeviceScanner(DeviceScanner):
Return boolean if scanning successful.
"""
self.logger.info("Scanning")
_LOGGER.info("Scanning")
try:
response = requests.Session().send(self.req, timeout=3)
if self.ssl:
response = requests.Session().send(self.req,
timeout=3,
verify=self.verify_ssl)
else:
response = requests.Session().send(self.req, timeout=3)
# Calling and parsing the Tomato api here. We only need the
# wldev and dhcpd_lease values.
if response.status_code == 200:
@ -92,7 +111,7 @@ class TomatoDeviceScanner(DeviceScanner):
elif response.status_code == 401:
# Authentication error
self.logger.exception((
_LOGGER.exception((
"Failed to authenticate, "
"please check your username and password"))
return False
@ -100,17 +119,17 @@ class TomatoDeviceScanner(DeviceScanner):
except requests.exceptions.ConnectionError:
# We get this if we could not connect to the router or
# an invalid http_id was supplied.
self.logger.exception("Failed to connect to the router or "
"invalid http_id supplied")
_LOGGER.exception("Failed to connect to the router or "
"invalid http_id supplied")
return False
except requests.exceptions.Timeout:
# We get this if we could not connect to the router or
# an invalid http_id was supplied.
self.logger.exception("Connection to the router timed out")
_LOGGER.exception("Connection to the router timed out")
return False
except ValueError:
# If JSON decoder could not parse the response.
self.logger.exception("Failed to parse response from router")
_LOGGER.exception("Failed to parse response from router")
return False

View File

@ -11,11 +11,11 @@ import re
import requests
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.components.device_tracker import (
DOMAIN, PLATFORM_SCHEMA, DeviceScanner)
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
from homeassistant.exceptions import HomeAssistantError
import homeassistant.helpers.config_validation as cv
_LOGGER = logging.getLogger(__name__)
@ -30,8 +30,8 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_HOST): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
vol.Required(CONF_USERNAME): cv.string,
vol.Optional(CONF_DHCP_SOFTWARE,
default=DEFAULT_DHCP_SOFTWARE): vol.In(DHCP_SOFTWARES)
vol.Optional(CONF_DHCP_SOFTWARE, default=DEFAULT_DHCP_SOFTWARE):
vol.In(DHCP_SOFTWARES),
})
@ -49,14 +49,14 @@ def get_scanner(hass, config):
def _refresh_on_acccess_denied(func):
"""If remove rebooted, it lost our session so rebuld one and try again."""
def decorator(self, *args, **kwargs):
"""Wrapper function to refresh session_id on PermissionError."""
"""Wrap the function to refresh session_id on PermissionError."""
try:
return func(self, *args, **kwargs)
except PermissionError:
_LOGGER.warning("Invalid session detected." +
" Tryign to refresh session_id and re-run the rpc")
self.session_id = _get_session_id(self.url, self.username,
self.password)
" Trying to refresh session_id and re-run RPC")
self.session_id = _get_session_id(
self.url, self.username, self.password)
return func(self, *args, **kwargs)
@ -80,8 +80,8 @@ class UbusDeviceScanner(DeviceScanner):
self.last_results = {}
self.url = 'http://{}/ubus'.format(host)
self.session_id = _get_session_id(self.url, self.username,
self.password)
self.session_id = _get_session_id(
self.url, self.username, self.password)
self.hostapd = []
self.mac2name = None
self.success_init = self.session_id is not None

View File

@ -98,11 +98,15 @@ class UnifiDeviceScanner(DeviceScanner):
self.connected = False
def _get_update(self):
from pexpect import pxssh
from pexpect import pxssh, exceptions
try:
if not self.connected:
self._connect()
# If we still aren't connected at this point
# don't try to send anything to the AP.
if not self.connected:
return None
self.ssh.sendline(UNIFI_COMMAND)
self.ssh.prompt()
return self.ssh.before
@ -110,7 +114,7 @@ class UnifiDeviceScanner(DeviceScanner):
_LOGGER.error("Unexpected SSH error: %s", str(err))
self._disconnect()
return None
except AssertionError as err:
except (AssertionError, exceptions.EOF) as err:
_LOGGER.error("Connection to AP unavailable: %s", str(err))
self._disconnect()
return None

View File

@ -84,7 +84,7 @@ class UPCDeviceScanner(DeviceScanner):
@asyncio.coroutine
def async_get_device_name(self, device):
"""The firmware doesn't save the name of the wireless device."""
"""Get the device name (the name of the wireless device not used)."""
return None
@asyncio.coroutine

View File

@ -21,7 +21,7 @@ from homeassistant.helpers.event import async_track_point_in_utc_time
from homeassistant.helpers.discovery import async_load_platform, async_discover
import homeassistant.util.dt as dt_util
REQUIREMENTS = ['netdisco==1.2.3']
REQUIREMENTS = ['netdisco==1.2.4']
DOMAIN = 'discovery'
@ -53,6 +53,7 @@ SERVICE_HANDLERS = {
SERVICE_TELLDUSLIVE: ('tellduslive', None),
SERVICE_HUE: ('hue', None),
SERVICE_DECONZ: ('deconz', None),
SERVICE_DAIKIN: ('daikin', None),
'google_cast': ('media_player', 'cast'),
'panasonic_viera': ('media_player', 'panasonic_viera'),
'plex_mediaserver': ('media_player', 'plex'),

Some files were not shown because too many files have changed in this diff Show More