Merge remote-tracking branch 'balloob/dev' into update_notify

Conflicts:
	homeassistant/components/frontend/version.py
	homeassistant/components/frontend/www_static/frontend.html
This commit is contained in:
Ryan Kraus 2015-11-15 17:00:35 -05:00
commit 9b5385c565
12 changed files with 232 additions and 32 deletions

View File

@ -84,6 +84,7 @@ omit =
homeassistant/components/sensor/glances.py homeassistant/components/sensor/glances.py
homeassistant/components/sensor/mysensors.py homeassistant/components/sensor/mysensors.py
homeassistant/components/sensor/openweathermap.py homeassistant/components/sensor/openweathermap.py
homeassistant/components/switch/orvibo.py
homeassistant/components/sensor/rest.py homeassistant/components/sensor/rest.py
homeassistant/components/sensor/rpi_gpio.py homeassistant/components/sensor/rpi_gpio.py
homeassistant/components/sensor/sabnzbd.py homeassistant/components/sensor/sabnzbd.py

View File

@ -87,7 +87,10 @@ def setup(hass, config):
if camera: if camera:
response = camera.camera_image() response = camera.camera_image()
handler.wfile.write(response) if response is not None:
handler.wfile.write(response)
else:
handler.send_response(HTTP_NOT_FOUND)
else: else:
handler.send_response(HTTP_NOT_FOUND) handler.send_response(HTTP_NOT_FOUND)
@ -129,7 +132,8 @@ def setup(hass, config):
while True: while True:
img_bytes = camera.camera_image() img_bytes = camera.camera_image()
if img_bytes is None:
continue
headers_str = '\r\n'.join(( headers_str = '\r\n'.join((
'Content-length: {}'.format(len(img_bytes)), 'Content-length: {}'.format(len(img_bytes)),
'Content-type: image/jpeg', 'Content-type: image/jpeg',

View File

@ -42,11 +42,19 @@ class GenericCamera(Camera):
def camera_image(self): def camera_image(self):
""" Return a still image reponse from the camera. """ """ Return a still image reponse from the camera. """
if self._username and self._password: if self._username and self._password:
response = requests.get( try:
self._still_image_url, response = requests.get(
auth=HTTPBasicAuth(self._username, self._password)) self._still_image_url,
auth=HTTPBasicAuth(self._username, self._password))
except requests.exceptions.RequestException as error:
_LOGGER.error('Error getting camera image: %s', error)
return None
else: else:
response = requests.get(self._still_image_url) try:
response = requests.get(self._still_image_url)
except requests.exceptions.RequestException as error:
_LOGGER.error('Error getting camera image: %s', error)
return None
return response.content return response.content

View File

@ -37,6 +37,7 @@ CONF_EVENT_DATA = "event_data"
CONF_DELAY = "delay" CONF_DELAY = "delay"
ATTR_LAST_ACTION = 'last_action' ATTR_LAST_ACTION = 'last_action'
ATTR_CAN_CANCEL = 'can_cancel'
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -113,6 +114,8 @@ class Script(ToggleEntity):
self._cur = -1 self._cur = -1
self._last_action = None self._last_action = None
self._listener = None self._listener = None
self._can_cancel = not any(CONF_DELAY in action for action
in self.sequence)
@property @property
def should_poll(self): def should_poll(self):
@ -126,7 +129,9 @@ class Script(ToggleEntity):
@property @property
def state_attributes(self): def state_attributes(self):
""" Returns the state attributes. """ """ Returns the state attributes. """
attrs = {} attrs = {
ATTR_CAN_CANCEL: self._can_cancel
}
if self._last_action: if self._last_action:
attrs[ATTR_LAST_ACTION] = self._last_action attrs[ATTR_LAST_ACTION] = self._last_action

View File

@ -70,6 +70,10 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
elif value.command_class == zwave.COMMAND_CLASS_SENSOR_MULTILEVEL: elif value.command_class == zwave.COMMAND_CLASS_SENSOR_MULTILEVEL:
add_devices([ZWaveMultilevelSensor(value)]) add_devices([ZWaveMultilevelSensor(value)])
elif (value.command_class == zwave.COMMAND_CLASS_METER and
value.type == zwave.TYPE_DECIMAL):
add_devices([ZWaveMultilevelSensor(value)])
class ZWaveSensor(Entity): class ZWaveSensor(Entity):
""" Represents a Z-Wave sensor. """ """ Represents a Z-Wave sensor. """

View File

@ -16,7 +16,8 @@ from homeassistant.helpers.entity import ToggleEntity
from homeassistant.const import ( from homeassistant.const import (
STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF, ATTR_ENTITY_ID) STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF, ATTR_ENTITY_ID)
from homeassistant.components import group, discovery, wink, isy994, verisure from homeassistant.components import (
group, discovery, wink, isy994, verisure, zwave)
DOMAIN = 'switch' DOMAIN = 'switch'
DEPENDENCIES = [] DEPENDENCIES = []
@ -38,7 +39,8 @@ DISCOVERY_PLATFORMS = {
discovery.SERVICE_WEMO: 'wemo', discovery.SERVICE_WEMO: 'wemo',
wink.DISCOVER_SWITCHES: 'wink', wink.DISCOVER_SWITCHES: 'wink',
isy994.DISCOVER_SWITCHES: 'isy994', isy994.DISCOVER_SWITCHES: 'isy994',
verisure.DISCOVER_SWITCHES: 'verisure' verisure.DISCOVER_SWITCHES: 'verisure',
zwave.DISCOVER_SWITCHES: 'zwave',
} }
PROP_TO_ATTR = { PROP_TO_ATTR = {

View File

@ -0,0 +1,75 @@
"""
homeassistant.components.switch.orvibo
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Support for Orvibo S20 Wifi Smart Switches.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/switch.orvibo/
"""
import logging
from homeassistant.components.switch import SwitchDevice
from orvibo.s20 import S20, S20Exception
DEFAULT_NAME = "Orvibo S20 Switch"
REQUIREMENTS = ['orvibo==1.0.0']
_LOGGER = logging.getLogger(__name__)
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
""" Find and return S20 switches. """
if config.get('host') is None:
_LOGGER.error("Missing required variable: host")
return
try:
s20 = S20(config.get('host'))
add_devices_callback([S20Switch(config.get('name', DEFAULT_NAME),
s20)])
except S20Exception:
_LOGGER.exception("S20 couldn't be initialized")
class S20Switch(SwitchDevice):
""" Represents an S20 switch. """
def __init__(self, name, s20):
self._name = name
self._s20 = s20
self._state = False
@property
def should_poll(self):
""" Poll. """
return True
@property
def name(self):
""" The name of the switch. """
return self._name
@property
def is_on(self):
""" True if device is on. """
return self._state
def update(self):
""" Update device state. """
try:
self._state = self._s20.on
except S20Exception:
_LOGGER.exception("Error while fetching S20 state")
def turn_on(self, **kwargs):
""" Turn the device on. """
try:
self._s20.on = True
except S20Exception:
_LOGGER.exception("Error while turning on S20")
def turn_off(self, **kwargs):
""" Turn the device off. """
try:
self._s20.on = False
except S20Exception:
_LOGGER.exception("Error while turning off S20")

View File

@ -0,0 +1,76 @@
"""
homeassistant.components.switch.zwave
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Zwave platform that handles simple binary switches.
"""
# pylint: disable=import-error
from openzwave.network import ZWaveNetwork
from pydispatch import dispatcher
import homeassistant.components.zwave as zwave
from homeassistant.components.switch import SwitchDevice
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Find and return demo switches. """
if discovery_info is None:
return
node = zwave.NETWORK.nodes[discovery_info[zwave.ATTR_NODE_ID]]
value = node.values[discovery_info[zwave.ATTR_VALUE_ID]]
if value.command_class != zwave.COMMAND_CLASS_SWITCH_BINARY:
return
if value.type != zwave.TYPE_BOOL:
return
if value.genre != zwave.GENRE_USER:
return
value.set_change_verified(False)
add_devices([ZwaveSwitch(value)])
class ZwaveSwitch(SwitchDevice):
""" Provides a zwave switch. """
def __init__(self, value):
self._value = value
self._node = value.node
self._state = value.data
dispatcher.connect(
self._value_changed, ZWaveNetwork.SIGNAL_VALUE_CHANGED)
def _value_changed(self, value):
""" Called when a value has changed on the network. """
if self._value.value_id == value.value_id:
self._state = value.data
self.update_ha_state()
@property
def should_poll(self):
""" No polling needed for a demo switch. """
return False
@property
def name(self):
""" Returns the name of the device if any. """
name = self._node.name or "{}".format(self._node.product_name)
return "{}".format(name or self._value.label)
@property
def is_on(self):
""" True if device is on. """
return self._state
def turn_on(self, **kwargs):
""" Turn the device on. """
self._node.set_switch(self._value.value_id, True)
def turn_off(self, **kwargs):
""" Turn the device off. """
self._node.set_switch(self._value.value_id, False)

View File

@ -20,13 +20,18 @@ REQUIREMENTS = ['pydispatcher==2.0.5']
CONF_USB_STICK_PATH = "usb_path" CONF_USB_STICK_PATH = "usb_path"
DEFAULT_CONF_USB_STICK_PATH = "/zwaveusbstick" DEFAULT_CONF_USB_STICK_PATH = "/zwaveusbstick"
CONF_DEBUG = "debug" CONF_DEBUG = "debug"
CONF_POLLING_INTERVAL = "polling_interval"
DISCOVER_SENSORS = "zwave.sensors" DISCOVER_SENSORS = "zwave.sensors"
DISCOVER_SWITCHES = "zwave.switch"
DISCOVER_LIGHTS = "zwave.light" DISCOVER_LIGHTS = "zwave.light"
COMMAND_CLASS_SWITCH_MULTILEVEL = 38 COMMAND_CLASS_SWITCH_MULTILEVEL = 38
COMMAND_CLASS_SWITCH_BINARY = 37
COMMAND_CLASS_SENSOR_BINARY = 48 COMMAND_CLASS_SENSOR_BINARY = 48
COMMAND_CLASS_SENSOR_MULTILEVEL = 49 COMMAND_CLASS_SENSOR_MULTILEVEL = 49
COMMAND_CLASS_METER = 50
COMMAND_CLASS_BATTERY = 128 COMMAND_CLASS_BATTERY = 128
GENRE_WHATEVER = None GENRE_WHATEVER = None
@ -35,20 +40,28 @@ GENRE_USER = "User"
TYPE_WHATEVER = None TYPE_WHATEVER = None
TYPE_BYTE = "Byte" TYPE_BYTE = "Byte"
TYPE_BOOL = "Bool" TYPE_BOOL = "Bool"
TYPE_DECIMAL = "Decimal"
# list of tuple (DOMAIN, discovered service, supported command # list of tuple (DOMAIN, discovered service, supported command
# classes, value type) # classes, value type)
DISCOVERY_COMPONENTS = [ DISCOVERY_COMPONENTS = [
('sensor', ('sensor',
DISCOVER_SENSORS, DISCOVER_SENSORS,
[COMMAND_CLASS_SENSOR_BINARY, COMMAND_CLASS_SENSOR_MULTILEVEL], [COMMAND_CLASS_SENSOR_BINARY,
COMMAND_CLASS_SENSOR_MULTILEVEL,
COMMAND_CLASS_METER],
TYPE_WHATEVER, TYPE_WHATEVER,
GENRE_WHATEVER), GENRE_USER),
('light', ('light',
DISCOVER_LIGHTS, DISCOVER_LIGHTS,
[COMMAND_CLASS_SWITCH_MULTILEVEL], [COMMAND_CLASS_SWITCH_MULTILEVEL],
TYPE_BYTE, TYPE_BYTE,
GENRE_USER), GENRE_USER),
('switch',
DISCOVER_SWITCHES,
[COMMAND_CLASS_SWITCH_BINARY],
TYPE_BOOL,
GENRE_USER),
] ]
ATTR_NODE_ID = "node_id" ATTR_NODE_ID = "node_id"
@ -165,6 +178,10 @@ def setup(hass, config):
""" Called when Home Assistant starts up. """ """ Called when Home Assistant starts up. """
NETWORK.start() NETWORK.start()
polling_interval = config[DOMAIN].get(CONF_POLLING_INTERVAL, None)
if polling_interval is not None:
NETWORK.setPollInterval(polling_interval)
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_zwave) hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_zwave)
hass.bus.listen_once(EVENT_HOMEASSISTANT_START, start_zwave) hass.bus.listen_once(EVENT_HOMEASSISTANT_START, start_zwave)

View File

@ -156,3 +156,6 @@ evohomeclient==0.2.3
# Pushetta (notify.pushetta) # Pushetta (notify.pushetta)
pushetta==1.0.15 pushetta==1.0.15
# Orvibo S10
orvibo==1.0.0

View File

@ -88,6 +88,8 @@ class TestScript(unittest.TestCase):
self.assertEqual(1, len(calls)) self.assertEqual(1, len(calls))
self.assertEqual('world', calls[0].data.get('hello')) self.assertEqual('world', calls[0].data.get('hello'))
self.assertEqual(
True, self.hass.states.get(ENTITY_ID).attributes.get('can_cancel'))
def test_calling_service_old(self): def test_calling_service_old(self):
calls = [] calls = []
@ -172,6 +174,9 @@ class TestScript(unittest.TestCase):
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
self.assertTrue(script.is_on(self.hass, ENTITY_ID)) self.assertTrue(script.is_on(self.hass, ENTITY_ID))
self.assertEqual(
False,
self.hass.states.get(ENTITY_ID).attributes.get('can_cancel'))
self.assertEqual( self.assertEqual(
event, event,