mirror of
https://github.com/home-assistant/core.git
synced 2025-07-27 07:07:28 +00:00
commit
080f56e0f5
@ -39,7 +39,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
|||||||
username = config.get(CONF_USERNAME)
|
username = config.get(CONF_USERNAME)
|
||||||
password = config.get(CONF_PASSWORD)
|
password = config.get(CONF_PASSWORD)
|
||||||
|
|
||||||
add_devices([AlarmDotCom(hass, name, code, username, password)])
|
add_devices([AlarmDotCom(hass, name, code, username, password)], True)
|
||||||
|
|
||||||
|
|
||||||
class AlarmDotCom(alarm.AlarmControlPanel):
|
class AlarmDotCom(alarm.AlarmControlPanel):
|
||||||
@ -54,12 +54,17 @@ class AlarmDotCom(alarm.AlarmControlPanel):
|
|||||||
self._code = str(code) if code else None
|
self._code = str(code) if code else None
|
||||||
self._username = username
|
self._username = username
|
||||||
self._password = password
|
self._password = password
|
||||||
|
self._state = STATE_UNKNOWN
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def should_poll(self):
|
def should_poll(self):
|
||||||
"""No polling needed."""
|
"""No polling needed."""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
"""Fetch the latest state."""
|
||||||
|
self._state = self._alarm.state
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
"""Return the name of the alarm."""
|
"""Return the name of the alarm."""
|
||||||
@ -73,11 +78,11 @@ class AlarmDotCom(alarm.AlarmControlPanel):
|
|||||||
@property
|
@property
|
||||||
def state(self):
|
def state(self):
|
||||||
"""Return the state of the device."""
|
"""Return the state of the device."""
|
||||||
if self._alarm.state == 'Disarmed':
|
if self._state == 'Disarmed':
|
||||||
return STATE_ALARM_DISARMED
|
return STATE_ALARM_DISARMED
|
||||||
elif self._alarm.state == 'Armed Stay':
|
elif self._state == 'Armed Stay':
|
||||||
return STATE_ALARM_ARMED_HOME
|
return STATE_ALARM_ARMED_HOME
|
||||||
elif self._alarm.state == 'Armed Away':
|
elif self._state == 'Armed Away':
|
||||||
return STATE_ALARM_ARMED_AWAY
|
return STATE_ALARM_ARMED_AWAY
|
||||||
else:
|
else:
|
||||||
return STATE_UNKNOWN
|
return STATE_UNKNOWN
|
||||||
|
@ -91,7 +91,7 @@ class GenericCamera(Camera):
|
|||||||
if url == self._last_url and self._limit_refetch:
|
if url == self._last_url and self._limit_refetch:
|
||||||
return self._last_image
|
return self._last_image
|
||||||
|
|
||||||
# aiohttp don't support DigestAuth jet
|
# aiohttp don't support DigestAuth yet
|
||||||
if self._authentication == HTTP_DIGEST_AUTHENTICATION:
|
if self._authentication == HTTP_DIGEST_AUTHENTICATION:
|
||||||
def fetch():
|
def fetch():
|
||||||
"""Read image from a URL."""
|
"""Read image from a URL."""
|
||||||
@ -109,15 +109,17 @@ class GenericCamera(Camera):
|
|||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
with async_timeout.timeout(10, loop=self.hass.loop):
|
with async_timeout.timeout(10, loop=self.hass.loop):
|
||||||
respone = yield from self.hass.websession.get(
|
response = yield from self.hass.websession.get(
|
||||||
url,
|
url, auth=self._auth)
|
||||||
auth=self._auth
|
self._last_image = yield from response.read()
|
||||||
)
|
yield from response.release()
|
||||||
self._last_image = yield from respone.read()
|
|
||||||
yield from respone.release()
|
|
||||||
except asyncio.TimeoutError:
|
except asyncio.TimeoutError:
|
||||||
_LOGGER.error('Timeout getting camera image')
|
_LOGGER.error('Timeout getting camera image')
|
||||||
return self._last_image
|
return self._last_image
|
||||||
|
except (aiohttp.errors.ClientError,
|
||||||
|
aiohttp.errors.ClientDisconnectedError) as err:
|
||||||
|
_LOGGER.error('Error getting new camera image: %s', err)
|
||||||
|
return self._last_image
|
||||||
|
|
||||||
self._last_url = url
|
self._last_url = url
|
||||||
return self._last_image
|
return self._last_image
|
||||||
|
@ -9,13 +9,14 @@ import logging
|
|||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
|
import aiohttp
|
||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
from aiohttp.web_exceptions import HTTPGatewayTimeout
|
from aiohttp.web_exceptions import HTTPGatewayTimeout
|
||||||
import async_timeout
|
import async_timeout
|
||||||
|
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_NAME, CONF_USERNAME, CONF_PASSWORD,
|
CONF_NAME, CONF_USERNAME, CONF_PASSWORD,
|
||||||
CONF_URL, CONF_WHITELIST, CONF_VERIFY_SSL)
|
CONF_URL, CONF_WHITELIST, CONF_VERIFY_SSL, EVENT_HOMEASSISTANT_STOP)
|
||||||
from homeassistant.components.camera import (
|
from homeassistant.components.camera import (
|
||||||
Camera, PLATFORM_SCHEMA)
|
Camera, PLATFORM_SCHEMA)
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
@ -57,6 +58,16 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
|||||||
@asyncio.coroutine
|
@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):
|
||||||
"""Setup a Synology IP Camera."""
|
"""Setup a Synology IP Camera."""
|
||||||
|
if not config.get(CONF_VERIFY_SSL):
|
||||||
|
connector = aiohttp.TCPConnector(verify_ssl=False)
|
||||||
|
else:
|
||||||
|
connector = None
|
||||||
|
|
||||||
|
websession_init = aiohttp.ClientSession(
|
||||||
|
loop=hass.loop,
|
||||||
|
connector=connector
|
||||||
|
)
|
||||||
|
|
||||||
# Determine API to use for authentication
|
# Determine API to use for authentication
|
||||||
syno_api_url = SYNO_API_URL.format(
|
syno_api_url = SYNO_API_URL.format(
|
||||||
config.get(CONF_URL), WEBAPI_PATH, QUERY_CGI)
|
config.get(CONF_URL), WEBAPI_PATH, QUERY_CGI)
|
||||||
@ -69,13 +80,12 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||||||
}
|
}
|
||||||
try:
|
try:
|
||||||
with async_timeout.timeout(TIMEOUT, loop=hass.loop):
|
with async_timeout.timeout(TIMEOUT, loop=hass.loop):
|
||||||
query_req = yield from hass.websession.get(
|
query_req = yield from websession_init.get(
|
||||||
syno_api_url,
|
syno_api_url,
|
||||||
params=query_payload,
|
params=query_payload
|
||||||
verify_ssl=config.get(CONF_VERIFY_SSL)
|
|
||||||
)
|
)
|
||||||
except asyncio.TimeoutError:
|
except (asyncio.TimeoutError, aiohttp.errors.ClientError):
|
||||||
_LOGGER.error("Timeout on %s", syno_api_url)
|
_LOGGER.exception("Error on %s", syno_api_url)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
query_resp = yield from query_req.json()
|
query_resp = yield from query_req.json()
|
||||||
@ -93,12 +103,26 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||||||
|
|
||||||
session_id = yield from get_session_id(
|
session_id = yield from get_session_id(
|
||||||
hass,
|
hass,
|
||||||
|
websession_init,
|
||||||
config.get(CONF_USERNAME),
|
config.get(CONF_USERNAME),
|
||||||
config.get(CONF_PASSWORD),
|
config.get(CONF_PASSWORD),
|
||||||
syno_auth_url,
|
syno_auth_url
|
||||||
config.get(CONF_VERIFY_SSL)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
websession_init.detach()
|
||||||
|
|
||||||
|
# init websession
|
||||||
|
websession = aiohttp.ClientSession(
|
||||||
|
loop=hass.loop, connector=connector, cookies={'id': session_id})
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def _async_close_websession(event):
|
||||||
|
"""Close webssesion on shutdown."""
|
||||||
|
yield from websession.close()
|
||||||
|
|
||||||
|
hass.bus.async_listen_once(
|
||||||
|
EVENT_HOMEASSISTANT_STOP, _async_close_websession)
|
||||||
|
|
||||||
# Use SessionID to get cameras in system
|
# Use SessionID to get cameras in system
|
||||||
syno_camera_url = SYNO_API_URL.format(
|
syno_camera_url = SYNO_API_URL.format(
|
||||||
config.get(CONF_URL), WEBAPI_PATH, camera_api)
|
config.get(CONF_URL), WEBAPI_PATH, camera_api)
|
||||||
@ -110,14 +134,12 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||||||
}
|
}
|
||||||
try:
|
try:
|
||||||
with async_timeout.timeout(TIMEOUT, loop=hass.loop):
|
with async_timeout.timeout(TIMEOUT, loop=hass.loop):
|
||||||
camera_req = yield from hass.websession.get(
|
camera_req = yield from websession.get(
|
||||||
syno_camera_url,
|
syno_camera_url,
|
||||||
params=camera_payload,
|
params=camera_payload
|
||||||
verify_ssl=config.get(CONF_VERIFY_SSL),
|
|
||||||
cookies={'id': session_id}
|
|
||||||
)
|
)
|
||||||
except asyncio.TimeoutError:
|
except (asyncio.TimeoutError, aiohttp.errors.ClientError):
|
||||||
_LOGGER.error("Timeout on %s", syno_camera_url)
|
_LOGGER.exception("Error on %s", syno_camera_url)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
camera_resp = yield from camera_req.json()
|
camera_resp = yield from camera_req.json()
|
||||||
@ -126,13 +148,14 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||||||
|
|
||||||
# add cameras
|
# add cameras
|
||||||
devices = []
|
devices = []
|
||||||
tasks = []
|
|
||||||
for camera in cameras:
|
for camera in cameras:
|
||||||
if not config.get(CONF_WHITELIST):
|
if not config.get(CONF_WHITELIST):
|
||||||
camera_id = camera['id']
|
camera_id = camera['id']
|
||||||
snapshot_path = camera['snapshot_path']
|
snapshot_path = camera['snapshot_path']
|
||||||
|
|
||||||
device = SynologyCamera(
|
device = SynologyCamera(
|
||||||
|
hass,
|
||||||
|
websession,
|
||||||
config,
|
config,
|
||||||
camera_id,
|
camera_id,
|
||||||
camera['name'],
|
camera['name'],
|
||||||
@ -141,15 +164,13 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||||||
camera_path,
|
camera_path,
|
||||||
auth_path
|
auth_path
|
||||||
)
|
)
|
||||||
tasks.append(device.async_read_sid())
|
|
||||||
devices.append(device)
|
devices.append(device)
|
||||||
|
|
||||||
yield from asyncio.gather(*tasks, loop=hass.loop)
|
yield from async_add_devices(devices)
|
||||||
hass.loop.create_task(async_add_devices(devices))
|
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def get_session_id(hass, username, password, login_url, valid_cert):
|
def get_session_id(hass, websession, username, password, login_url):
|
||||||
"""Get a session id."""
|
"""Get a session id."""
|
||||||
auth_payload = {
|
auth_payload = {
|
||||||
'api': AUTH_API,
|
'api': AUTH_API,
|
||||||
@ -162,13 +183,12 @@ def get_session_id(hass, username, password, login_url, valid_cert):
|
|||||||
}
|
}
|
||||||
try:
|
try:
|
||||||
with async_timeout.timeout(TIMEOUT, loop=hass.loop):
|
with async_timeout.timeout(TIMEOUT, loop=hass.loop):
|
||||||
auth_req = yield from hass.websession.get(
|
auth_req = yield from websession.get(
|
||||||
login_url,
|
login_url,
|
||||||
params=auth_payload,
|
params=auth_payload
|
||||||
verify_ssl=valid_cert
|
|
||||||
)
|
)
|
||||||
except asyncio.TimeoutError:
|
except (asyncio.TimeoutError, aiohttp.errors.ClientError):
|
||||||
_LOGGER.error("Timeout on %s", login_url)
|
_LOGGER.exception("Error on %s", login_url)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
auth_resp = yield from auth_req.json()
|
auth_resp = yield from auth_req.json()
|
||||||
@ -180,36 +200,22 @@ def get_session_id(hass, username, password, login_url, valid_cert):
|
|||||||
class SynologyCamera(Camera):
|
class SynologyCamera(Camera):
|
||||||
"""An implementation of a Synology NAS based IP camera."""
|
"""An implementation of a Synology NAS based IP camera."""
|
||||||
|
|
||||||
def __init__(self, config, camera_id, camera_name,
|
def __init__(self, hass, websession, config, camera_id,
|
||||||
snapshot_path, streaming_path, camera_path, auth_path):
|
camera_name, snapshot_path, streaming_path, camera_path,
|
||||||
|
auth_path):
|
||||||
"""Initialize a Synology Surveillance Station camera."""
|
"""Initialize a Synology Surveillance Station camera."""
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
self.hass = hass
|
||||||
|
self._websession = websession
|
||||||
self._name = camera_name
|
self._name = camera_name
|
||||||
self._username = config.get(CONF_USERNAME)
|
|
||||||
self._password = config.get(CONF_PASSWORD)
|
|
||||||
self._synology_url = config.get(CONF_URL)
|
self._synology_url = config.get(CONF_URL)
|
||||||
self._api_url = config.get(CONF_URL) + 'webapi/'
|
|
||||||
self._login_url = config.get(CONF_URL) + '/webapi/' + 'auth.cgi'
|
|
||||||
self._camera_name = config.get(CONF_CAMERA_NAME)
|
self._camera_name = config.get(CONF_CAMERA_NAME)
|
||||||
self._stream_id = config.get(CONF_STREAM_ID)
|
self._stream_id = config.get(CONF_STREAM_ID)
|
||||||
self._valid_cert = config.get(CONF_VERIFY_SSL)
|
|
||||||
self._camera_id = camera_id
|
self._camera_id = camera_id
|
||||||
self._snapshot_path = snapshot_path
|
self._snapshot_path = snapshot_path
|
||||||
self._streaming_path = streaming_path
|
self._streaming_path = streaming_path
|
||||||
self._camera_path = camera_path
|
self._camera_path = camera_path
|
||||||
self._auth_path = auth_path
|
self._auth_path = auth_path
|
||||||
self._session_id = None
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
|
||||||
def async_read_sid(self):
|
|
||||||
"""Get a session id."""
|
|
||||||
self._session_id = yield from get_session_id(
|
|
||||||
self.hass,
|
|
||||||
self._username,
|
|
||||||
self._password,
|
|
||||||
self._login_url,
|
|
||||||
self._valid_cert
|
|
||||||
)
|
|
||||||
|
|
||||||
def camera_image(self):
|
def camera_image(self):
|
||||||
"""Return bytes of camera image."""
|
"""Return bytes of camera image."""
|
||||||
@ -230,14 +236,12 @@ class SynologyCamera(Camera):
|
|||||||
}
|
}
|
||||||
try:
|
try:
|
||||||
with async_timeout.timeout(TIMEOUT, loop=self.hass.loop):
|
with async_timeout.timeout(TIMEOUT, loop=self.hass.loop):
|
||||||
response = yield from self.hass.websession.get(
|
response = yield from self._websession.get(
|
||||||
image_url,
|
image_url,
|
||||||
params=image_payload,
|
params=image_payload
|
||||||
verify_ssl=self._valid_cert,
|
|
||||||
cookies={'id': self._session_id}
|
|
||||||
)
|
)
|
||||||
except asyncio.TimeoutError:
|
except (asyncio.TimeoutError, aiohttp.errors.ClientError):
|
||||||
_LOGGER.error("Timeout on %s", image_url)
|
_LOGGER.exception("Error on %s", image_url)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
image = yield from response.read()
|
image = yield from response.read()
|
||||||
@ -260,13 +264,12 @@ class SynologyCamera(Camera):
|
|||||||
}
|
}
|
||||||
try:
|
try:
|
||||||
with async_timeout.timeout(TIMEOUT, loop=self.hass.loop):
|
with async_timeout.timeout(TIMEOUT, loop=self.hass.loop):
|
||||||
stream = yield from self.hass.websession.get(
|
stream = yield from self._websession.get(
|
||||||
streaming_url,
|
streaming_url,
|
||||||
payload=streaming_payload,
|
params=streaming_payload
|
||||||
verify_ssl=self._valid_cert,
|
|
||||||
cookies={'id': self._session_id}
|
|
||||||
)
|
)
|
||||||
except asyncio.TimeoutError:
|
except (asyncio.TimeoutError, aiohttp.errors.ClientError):
|
||||||
|
_LOGGER.exception("Error on %s", streaming_url)
|
||||||
raise HTTPGatewayTimeout()
|
raise HTTPGatewayTimeout()
|
||||||
|
|
||||||
response = web.StreamResponse()
|
response = web.StreamResponse()
|
||||||
@ -281,7 +284,7 @@ class SynologyCamera(Camera):
|
|||||||
break
|
break
|
||||||
response.write(data)
|
response.write(data)
|
||||||
finally:
|
finally:
|
||||||
self.hass.loop.create_task(stream.release())
|
self.hass.async_add_job(stream.release())
|
||||||
yield from response.write_eof()
|
yield from response.write_eof()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -56,6 +56,8 @@ class KNXThermostat(KNXMultiAddressDevice, ClimateDevice):
|
|||||||
self._unit_of_measurement = TEMP_CELSIUS # KNX always used celsius
|
self._unit_of_measurement = TEMP_CELSIUS # KNX always used celsius
|
||||||
self._away = False # not yet supported
|
self._away = False # not yet supported
|
||||||
self._is_fan_on = False # not yet supported
|
self._is_fan_on = False # not yet supported
|
||||||
|
self._current_temp = None
|
||||||
|
self._target_temp = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def should_poll(self):
|
def should_poll(self):
|
||||||
@ -70,16 +72,12 @@ class KNXThermostat(KNXMultiAddressDevice, ClimateDevice):
|
|||||||
@property
|
@property
|
||||||
def current_temperature(self):
|
def current_temperature(self):
|
||||||
"""Return the current temperature."""
|
"""Return the current temperature."""
|
||||||
from knxip.conversion import knx2_to_float
|
return self._current_temp
|
||||||
|
|
||||||
return knx2_to_float(self.value('temperature'))
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def target_temperature(self):
|
def target_temperature(self):
|
||||||
"""Return the temperature we try to reach."""
|
"""Return the temperature we try to reach."""
|
||||||
from knxip.conversion import knx2_to_float
|
return self._target_temp
|
||||||
|
|
||||||
return knx2_to_float(self.value('setpoint'))
|
|
||||||
|
|
||||||
def set_temperature(self, **kwargs):
|
def set_temperature(self, **kwargs):
|
||||||
"""Set new target temperature."""
|
"""Set new target temperature."""
|
||||||
@ -94,3 +92,12 @@ class KNXThermostat(KNXMultiAddressDevice, ClimateDevice):
|
|||||||
def set_operation_mode(self, operation_mode):
|
def set_operation_mode(self, operation_mode):
|
||||||
"""Set operation mode."""
|
"""Set operation mode."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
"""Update KNX climate."""
|
||||||
|
from knxip.conversion import knx2_to_float
|
||||||
|
|
||||||
|
super().update()
|
||||||
|
|
||||||
|
self._current_temp = knx2_to_float(self.value('temperature'))
|
||||||
|
self._target_temp = knx2_to_float(self.value('setpoint'))
|
||||||
|
@ -161,8 +161,6 @@ class KNXGroupAddress(Entity):
|
|||||||
@property
|
@property
|
||||||
def is_on(self):
|
def is_on(self):
|
||||||
"""Return True if the value is not 0 is on, else False."""
|
"""Return True if the value is not 0 is on, else False."""
|
||||||
if self.should_poll:
|
|
||||||
self.update()
|
|
||||||
return self._state != 0
|
return self._state != 0
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -23,6 +23,7 @@ REQUIREMENTS = ['https://github.com/Danielhiversen/flux_led/archive/0.8.zip'
|
|||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
CONF_AUTOMATIC_ADD = 'automatic_add'
|
CONF_AUTOMATIC_ADD = 'automatic_add'
|
||||||
|
ATTR_MODE = 'mode'
|
||||||
|
|
||||||
DOMAIN = 'flux_led'
|
DOMAIN = 'flux_led'
|
||||||
|
|
||||||
@ -31,6 +32,8 @@ SUPPORT_FLUX_LED = (SUPPORT_BRIGHTNESS | SUPPORT_EFFECT |
|
|||||||
|
|
||||||
DEVICE_SCHEMA = vol.Schema({
|
DEVICE_SCHEMA = vol.Schema({
|
||||||
vol.Optional(CONF_NAME): cv.string,
|
vol.Optional(CONF_NAME): cv.string,
|
||||||
|
vol.Optional(ATTR_MODE, default='rgbw'):
|
||||||
|
vol.All(cv.string, vol.In(['rgbw', 'rgb'])),
|
||||||
})
|
})
|
||||||
|
|
||||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||||
@ -48,6 +51,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
|||||||
device = {}
|
device = {}
|
||||||
device['name'] = device_config[CONF_NAME]
|
device['name'] = device_config[CONF_NAME]
|
||||||
device['ipaddr'] = ipaddr
|
device['ipaddr'] = ipaddr
|
||||||
|
device[ATTR_MODE] = device_config[ATTR_MODE]
|
||||||
light = FluxLight(device)
|
light = FluxLight(device)
|
||||||
if light.is_valid:
|
if light.is_valid:
|
||||||
lights.append(light)
|
lights.append(light)
|
||||||
@ -65,6 +69,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
|||||||
if ipaddr in light_ips:
|
if ipaddr in light_ips:
|
||||||
continue
|
continue
|
||||||
device['name'] = device['id'] + " " + ipaddr
|
device['name'] = device['id'] + " " + ipaddr
|
||||||
|
device[ATTR_MODE] = 'rgbw'
|
||||||
light = FluxLight(device)
|
light = FluxLight(device)
|
||||||
if light.is_valid:
|
if light.is_valid:
|
||||||
lights.append(light)
|
lights.append(light)
|
||||||
@ -82,6 +87,7 @@ class FluxLight(Light):
|
|||||||
|
|
||||||
self._name = device['name']
|
self._name = device['name']
|
||||||
self._ipaddr = device['ipaddr']
|
self._ipaddr = device['ipaddr']
|
||||||
|
self._mode = device[ATTR_MODE]
|
||||||
self.is_valid = True
|
self.is_valid = True
|
||||||
self._bulb = None
|
self._bulb = None
|
||||||
try:
|
try:
|
||||||
@ -132,7 +138,11 @@ class FluxLight(Light):
|
|||||||
if rgb:
|
if rgb:
|
||||||
self._bulb.setRgb(*tuple(rgb))
|
self._bulb.setRgb(*tuple(rgb))
|
||||||
elif brightness:
|
elif brightness:
|
||||||
|
if self._mode == 'rgbw':
|
||||||
self._bulb.setWarmWhite255(brightness)
|
self._bulb.setWarmWhite255(brightness)
|
||||||
|
elif self._mode == 'rgb':
|
||||||
|
(red, green, blue) = self._bulb.getRgb()
|
||||||
|
self._bulb.setRgb(red, green, blue, brightness=brightness)
|
||||||
elif effect == EFFECT_RANDOM:
|
elif effect == EFFECT_RANDOM:
|
||||||
self._bulb.setRgb(random.randrange(0, 255),
|
self._bulb.setRgb(random.randrange(0, 255),
|
||||||
random.randrange(0, 255),
|
random.randrange(0, 255),
|
||||||
|
@ -79,16 +79,16 @@ class PanasonicVieraTVDevice(MediaPlayerDevice):
|
|||||||
self._playing = True
|
self._playing = True
|
||||||
self._state = STATE_UNKNOWN
|
self._state = STATE_UNKNOWN
|
||||||
self._remote = remote
|
self._remote = remote
|
||||||
|
self._volume = 0
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
"""Retrieve the latest data."""
|
"""Retrieve the latest data."""
|
||||||
try:
|
try:
|
||||||
self._muted = self._remote.get_mute()
|
self._muted = self._remote.get_mute()
|
||||||
|
self._volume = self._remote.get_volume() / 100
|
||||||
self._state = STATE_ON
|
self._state = STATE_ON
|
||||||
except OSError:
|
except OSError:
|
||||||
self._state = STATE_OFF
|
self._state = STATE_OFF
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
def send_key(self, key):
|
def send_key(self, key):
|
||||||
"""Send a key to the tv and handles exceptions."""
|
"""Send a key to the tv and handles exceptions."""
|
||||||
@ -113,13 +113,7 @@ class PanasonicVieraTVDevice(MediaPlayerDevice):
|
|||||||
@property
|
@property
|
||||||
def volume_level(self):
|
def volume_level(self):
|
||||||
"""Volume level of the media player (0..1)."""
|
"""Volume level of the media player (0..1)."""
|
||||||
volume = 0
|
return self._volume
|
||||||
try:
|
|
||||||
volume = self._remote.get_volume() / 100
|
|
||||||
self._state = STATE_ON
|
|
||||||
except OSError:
|
|
||||||
self._state = STATE_OFF
|
|
||||||
return volume
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_volume_muted(self):
|
def is_volume_muted(self):
|
||||||
|
@ -21,9 +21,7 @@ from homeassistant.const import (
|
|||||||
from homeassistant.config import load_yaml_config_file
|
from homeassistant.config import load_yaml_config_file
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
|
||||||
REQUIREMENTS = ['https://github.com/SoCo/SoCo/archive/'
|
REQUIREMENTS = ['SoCo==0.12']
|
||||||
'cf8c2701165562eccbf1ecc879bf7060ceb0993e.zip#'
|
|
||||||
'SoCo==0.12']
|
|
||||||
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
@ -18,7 +18,7 @@ from homeassistant.const import (CONF_NAME, CONF_HOST, STATE_OFF, STATE_ON,
|
|||||||
STATE_PLAYING, STATE_IDLE)
|
STATE_PLAYING, STATE_IDLE)
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
|
||||||
REQUIREMENTS = ['rxv==0.3.0']
|
REQUIREMENTS = ['rxv==0.3.1']
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -35,6 +35,7 @@ CONF_SOURCE_IGNORE = 'source_ignore'
|
|||||||
CONF_ZONE_IGNORE = 'zone_ignore'
|
CONF_ZONE_IGNORE = 'zone_ignore'
|
||||||
|
|
||||||
DEFAULT_NAME = 'Yamaha Receiver'
|
DEFAULT_NAME = 'Yamaha Receiver'
|
||||||
|
KNOWN = 'yamaha_known_receivers'
|
||||||
|
|
||||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||||
@ -50,6 +51,11 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
|||||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
"""Setup the Yamaha platform."""
|
"""Setup the Yamaha platform."""
|
||||||
import rxv
|
import rxv
|
||||||
|
# keep track of configured receivers so that we don't end up
|
||||||
|
# discovering a receiver dynamically that we have static config
|
||||||
|
# for.
|
||||||
|
if hass.data.get(KNOWN, None) is None:
|
||||||
|
hass.data[KNOWN] = set()
|
||||||
|
|
||||||
name = config.get(CONF_NAME)
|
name = config.get(CONF_NAME)
|
||||||
host = config.get(CONF_HOST)
|
host = config.get(CONF_HOST)
|
||||||
@ -62,12 +68,17 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
|||||||
model = discovery_info[1]
|
model = discovery_info[1]
|
||||||
ctrl_url = discovery_info[2]
|
ctrl_url = discovery_info[2]
|
||||||
desc_url = discovery_info[3]
|
desc_url = discovery_info[3]
|
||||||
|
if ctrl_url in hass.data[KNOWN]:
|
||||||
|
_LOGGER.info("%s already manually configured", ctrl_url)
|
||||||
|
return
|
||||||
receivers = rxv.RXV(
|
receivers = rxv.RXV(
|
||||||
ctrl_url,
|
ctrl_url,
|
||||||
model_name=model,
|
model_name=model,
|
||||||
friendly_name=name,
|
friendly_name=name,
|
||||||
unit_desc_url=desc_url).zone_controllers()
|
unit_desc_url=desc_url).zone_controllers()
|
||||||
_LOGGER.info("Receivers: %s", receivers)
|
_LOGGER.info("Receivers: %s", receivers)
|
||||||
|
# when we are dynamically discovered config is empty
|
||||||
|
zone_ignore = []
|
||||||
elif host is None:
|
elif host is None:
|
||||||
receivers = []
|
receivers = []
|
||||||
for recv in rxv.find():
|
for recv in rxv.find():
|
||||||
@ -78,6 +89,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
|||||||
|
|
||||||
for receiver in receivers:
|
for receiver in receivers:
|
||||||
if receiver.zone not in zone_ignore:
|
if receiver.zone not in zone_ignore:
|
||||||
|
hass.data[KNOWN].add(receiver.ctrl_url)
|
||||||
add_devices([
|
add_devices([
|
||||||
YamahaDevice(name, receiver, source_ignore, source_names)])
|
YamahaDevice(name, receiver, source_ignore, source_names)])
|
||||||
|
|
||||||
|
@ -113,15 +113,24 @@ class KNXSensorFloatClass(KNXGroupAddress, KNXSensorBaseClass):
|
|||||||
self._unit_of_measurement = unit_of_measurement
|
self._unit_of_measurement = unit_of_measurement
|
||||||
self._minimum_value = minimum_sensor_value
|
self._minimum_value = minimum_sensor_value
|
||||||
self._maximum_value = maximum_sensor_value
|
self._maximum_value = maximum_sensor_value
|
||||||
|
self._value = None
|
||||||
|
|
||||||
KNXGroupAddress.__init__(self, hass, config)
|
KNXGroupAddress.__init__(self, hass, config)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def state(self):
|
def state(self):
|
||||||
"""Return the Value of the KNX Sensor."""
|
"""Return the Value of the KNX Sensor."""
|
||||||
if self._data:
|
return self._value
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
"""Update KNX sensor."""
|
||||||
from knxip.conversion import knx2_to_float
|
from knxip.conversion import knx2_to_float
|
||||||
|
|
||||||
|
super().update()
|
||||||
|
|
||||||
|
self._value = None
|
||||||
|
|
||||||
|
if self._data:
|
||||||
value = knx2_to_float(self._data)
|
value = knx2_to_float(self._data)
|
||||||
if self._minimum_value <= value <= self._maximum_value:
|
if self._minimum_value <= value <= self._maximum_value:
|
||||||
return value
|
self._value = value
|
||||||
return None
|
|
||||||
|
@ -98,6 +98,7 @@ class TellstickSensor(Entity):
|
|||||||
self.datatype = datatype
|
self.datatype = datatype
|
||||||
self.sensor = sensor
|
self.sensor = sensor
|
||||||
self._unit_of_measurement = sensor_info.unit or None
|
self._unit_of_measurement = sensor_info.unit or None
|
||||||
|
self._value = None
|
||||||
|
|
||||||
self._name = '{} {}'.format(name, sensor_info.name)
|
self._name = '{} {}'.format(name, sensor_info.name)
|
||||||
|
|
||||||
@ -109,9 +110,13 @@ class TellstickSensor(Entity):
|
|||||||
@property
|
@property
|
||||||
def state(self):
|
def state(self):
|
||||||
"""Return the state of the sensor."""
|
"""Return the state of the sensor."""
|
||||||
return self.sensor.value(self.datatype).value
|
return self._value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def unit_of_measurement(self):
|
def unit_of_measurement(self):
|
||||||
"""Return the unit of measurement of this entity, if any."""
|
"""Return the unit of measurement of this entity, if any."""
|
||||||
return self._unit_of_measurement
|
return self._unit_of_measurement
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
"""Update tellstick sensor."""
|
||||||
|
self._value = self.sensor.value(self.datatype).value
|
||||||
|
@ -10,7 +10,7 @@ import logging
|
|||||||
from xml.parsers.expat import ExpatError
|
from xml.parsers.expat import ExpatError
|
||||||
|
|
||||||
import async_timeout
|
import async_timeout
|
||||||
from aiohttp.web import HTTPException
|
import aiohttp
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
@ -154,12 +154,9 @@ class YrData(object):
|
|||||||
try_again('{} returned {}'.format(self._url, resp.status))
|
try_again('{} returned {}'.format(self._url, resp.status))
|
||||||
return
|
return
|
||||||
text = yield from resp.text()
|
text = yield from resp.text()
|
||||||
self.hass.loop.create_task(resp.release())
|
self.hass.async_add_job(resp.release)
|
||||||
except asyncio.TimeoutError as err:
|
except (asyncio.TimeoutError, aiohttp.errors.ClientError,
|
||||||
try_again(err)
|
aiohttp.errors.ClientDisconnectedError) as err:
|
||||||
return
|
|
||||||
except HTTPException as err:
|
|
||||||
resp.close()
|
|
||||||
try_again(err)
|
try_again(err)
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -218,4 +215,5 @@ class YrData(object):
|
|||||||
dev._state = new_state
|
dev._state = new_state
|
||||||
tasks.append(dev.async_update_ha_state())
|
tasks.append(dev.async_update_ha_state())
|
||||||
|
|
||||||
yield from asyncio.gather(*tasks, loop=self.hass.loop)
|
if tasks:
|
||||||
|
yield from asyncio.wait(tasks, loop=self.hass.loop)
|
||||||
|
@ -27,10 +27,10 @@ DEPENDENCIES = ['pilight']
|
|||||||
COMMAND_SCHEMA = pilight.RF_CODE_SCHEMA.extend({
|
COMMAND_SCHEMA = pilight.RF_CODE_SCHEMA.extend({
|
||||||
vol.Optional('on'): cv.positive_int,
|
vol.Optional('on'): cv.positive_int,
|
||||||
vol.Optional('off'): cv.positive_int,
|
vol.Optional('off'): cv.positive_int,
|
||||||
vol.Optional(CONF_UNIT): cv.string,
|
vol.Optional(CONF_UNIT): cv.positive_int,
|
||||||
vol.Optional(CONF_ID): cv.positive_int,
|
vol.Optional(CONF_ID): cv.positive_int,
|
||||||
vol.Optional(CONF_STATE): cv.string,
|
vol.Optional(CONF_STATE): cv.string,
|
||||||
vol.Optional(CONF_SYSTEMCODE): cv.string,
|
vol.Optional(CONF_SYSTEMCODE): cv.positive_int,
|
||||||
})
|
})
|
||||||
|
|
||||||
SWITCHES_SCHEMA = vol.Schema({
|
SWITCHES_SCHEMA = vol.Schema({
|
||||||
|
@ -12,11 +12,12 @@ import voluptuous as vol
|
|||||||
from homeassistant.components.switch import (SwitchDevice, PLATFORM_SCHEMA)
|
from homeassistant.components.switch import (SwitchDevice, PLATFORM_SCHEMA)
|
||||||
from homeassistant.const import (CONF_NAME, CONF_RESOURCE, CONF_TIMEOUT)
|
from homeassistant.const import (CONF_NAME, CONF_RESOURCE, CONF_TIMEOUT)
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
from homeassistant.helpers.template import Template
|
||||||
|
|
||||||
CONF_BODY_OFF = 'body_off'
|
CONF_BODY_OFF = 'body_off'
|
||||||
CONF_BODY_ON = 'body_on'
|
CONF_BODY_ON = 'body_on'
|
||||||
DEFAULT_BODY_OFF = 'OFF'
|
DEFAULT_BODY_OFF = Template('OFF')
|
||||||
DEFAULT_BODY_ON = 'ON'
|
DEFAULT_BODY_ON = Template('ON')
|
||||||
DEFAULT_NAME = 'REST Switch'
|
DEFAULT_NAME = 'REST Switch'
|
||||||
DEFAULT_TIMEOUT = 10
|
DEFAULT_TIMEOUT = 10
|
||||||
CONF_IS_ON_TEMPLATE = 'is_on_template'
|
CONF_IS_ON_TEMPLATE = 'is_on_template'
|
||||||
|
@ -67,7 +67,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
|||||||
data = WeatherData(owm, latitude, longitude)
|
data = WeatherData(owm, latitude, longitude)
|
||||||
|
|
||||||
add_devices([OpenWeatherMapWeather(
|
add_devices([OpenWeatherMapWeather(
|
||||||
name, data, hass.config.units.temperature_unit)])
|
name, data, hass.config.units.temperature_unit)], True)
|
||||||
|
|
||||||
|
|
||||||
class OpenWeatherMapWeather(WeatherEntity):
|
class OpenWeatherMapWeather(WeatherEntity):
|
||||||
@ -78,8 +78,7 @@ class OpenWeatherMapWeather(WeatherEntity):
|
|||||||
self._name = name
|
self._name = name
|
||||||
self._owm = owm
|
self._owm = owm
|
||||||
self._temperature_unit = temperature_unit
|
self._temperature_unit = temperature_unit
|
||||||
self.date = None
|
self.data = None
|
||||||
self.update()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
"""Constants used by Home Assistant components."""
|
"""Constants used by Home Assistant components."""
|
||||||
MAJOR_VERSION = 0
|
MAJOR_VERSION = 0
|
||||||
MINOR_VERSION = 32
|
MINOR_VERSION = 32
|
||||||
PATCH_VERSION = '2'
|
PATCH_VERSION = '3'
|
||||||
__short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION)
|
__short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION)
|
||||||
__version__ = '{}.{}'.format(__short_version__, PATCH_VERSION)
|
__version__ = '{}.{}'.format(__short_version__, PATCH_VERSION)
|
||||||
REQUIRED_PYTHON_VER = (3, 4, 2)
|
REQUIRED_PYTHON_VER = (3, 4, 2)
|
||||||
|
@ -242,7 +242,7 @@ class Entity(object):
|
|||||||
|
|
||||||
end = timer()
|
end = timer()
|
||||||
|
|
||||||
if end - start > 0.2:
|
if end - start > 0.4:
|
||||||
_LOGGER.warning('Updating state for %s took %.3f seconds. '
|
_LOGGER.warning('Updating state for %s took %.3f seconds. '
|
||||||
'Please report platform to the developers at '
|
'Please report platform to the developers at '
|
||||||
'https://goo.gl/Nvioub', self.entity_id,
|
'https://goo.gl/Nvioub', self.entity_id,
|
||||||
|
@ -24,6 +24,9 @@ PyMata==2.13
|
|||||||
# homeassistant.components.rpi_gpio
|
# homeassistant.components.rpi_gpio
|
||||||
# RPi.GPIO==0.6.1
|
# RPi.GPIO==0.6.1
|
||||||
|
|
||||||
|
# homeassistant.components.media_player.sonos
|
||||||
|
SoCo==0.12
|
||||||
|
|
||||||
# homeassistant.components.notify.twitter
|
# homeassistant.components.notify.twitter
|
||||||
TwitterAPI==2.4.2
|
TwitterAPI==2.4.2
|
||||||
|
|
||||||
@ -157,9 +160,6 @@ https://github.com/GadgetReactor/pyHS100/archive/1f771b7d8090a91c6a58931532e4273
|
|||||||
# homeassistant.components.switch.dlink
|
# homeassistant.components.switch.dlink
|
||||||
https://github.com/LinuxChristian/pyW215/archive/v0.3.5.zip#pyW215==0.3.5
|
https://github.com/LinuxChristian/pyW215/archive/v0.3.5.zip#pyW215==0.3.5
|
||||||
|
|
||||||
# homeassistant.components.media_player.sonos
|
|
||||||
https://github.com/SoCo/SoCo/archive/cf8c2701165562eccbf1ecc879bf7060ceb0993e.zip#SoCo==0.12
|
|
||||||
|
|
||||||
# homeassistant.components.media_player.webostv
|
# homeassistant.components.media_player.webostv
|
||||||
# homeassistant.components.notify.webostv
|
# homeassistant.components.notify.webostv
|
||||||
https://github.com/TheRealLink/pylgtv/archive/v0.1.2.zip#pylgtv==0.1.2
|
https://github.com/TheRealLink/pylgtv/archive/v0.1.2.zip#pylgtv==0.1.2
|
||||||
@ -464,7 +464,7 @@ radiotherm==1.2
|
|||||||
# rpi-rf==0.9.5
|
# rpi-rf==0.9.5
|
||||||
|
|
||||||
# homeassistant.components.media_player.yamaha
|
# homeassistant.components.media_player.yamaha
|
||||||
rxv==0.3.0
|
rxv==0.3.1
|
||||||
|
|
||||||
# homeassistant.components.media_player.samsungtv
|
# homeassistant.components.media_player.samsungtv
|
||||||
samsungctl==0.5.1
|
samsungctl==0.5.1
|
||||||
|
Loading…
x
Reference in New Issue
Block a user