Merge pull request #5384 from home-assistant/release-0-36-1

Release 0 36 1
This commit is contained in:
Paulus Schoutsen 2017-01-16 22:24:12 -08:00 committed by GitHub
commit 4d96b12424
14 changed files with 54 additions and 43 deletions

View File

@ -17,7 +17,7 @@ from homeassistant.const import (
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['python-eq3bt==0.1.2'] REQUIREMENTS = ['python-eq3bt==0.1.4']
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -29,6 +29,7 @@ ATTR_STATE_WINDOW_OPEN = "window_open"
ATTR_STATE_VALVE = "valve" ATTR_STATE_VALVE = "valve"
ATTR_STATE_LOCKED = "is_locked" ATTR_STATE_LOCKED = "is_locked"
ATTR_STATE_LOW_BAT = "low_battery" ATTR_STATE_LOW_BAT = "low_battery"
ATTR_STATE_AWAY_END = "away_end"
DEVICE_SCHEMA = vol.Schema({ DEVICE_SCHEMA = vol.Schema({
vol.Required(CONF_MAC): cv.string, vol.Required(CONF_MAC): cv.string,
@ -152,6 +153,7 @@ class EQ3BTSmartThermostat(ClimateDevice):
ATTR_STATE_LOW_BAT: self._thermostat.low_battery, ATTR_STATE_LOW_BAT: self._thermostat.low_battery,
ATTR_STATE_VALVE: self._thermostat.valve_state, ATTR_STATE_VALVE: self._thermostat.valve_state,
ATTR_STATE_WINDOW_OPEN: self._thermostat.window_open, ATTR_STATE_WINDOW_OPEN: self._thermostat.window_open,
ATTR_STATE_AWAY_END: self._thermostat.away_end,
} }
return dev_specific return dev_specific

View File

@ -1,14 +1,12 @@
"""Tracking for bluetooth low energy devices.""" """Tracking for bluetooth low energy devices."""
import logging import logging
from datetime import timedelta
import voluptuous as vol import voluptuous as vol
from homeassistant.helpers.event import track_point_in_utc_time from homeassistant.helpers.event import track_point_in_utc_time
from homeassistant.components.device_tracker import ( from homeassistant.components.device_tracker import (
YAML_DEVICES, CONF_TRACK_NEW, CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL, YAML_DEVICES, CONF_TRACK_NEW, CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL,
PLATFORM_SCHEMA, load_config, DEFAULT_TRACK_NEW PLATFORM_SCHEMA, load_config
) )
import homeassistant.util as util
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
@ -86,14 +84,13 @@ def setup_scanner(hass, config, see):
# if track new devices is true discover new devices # if track new devices is true discover new devices
# on every scan. # on every scan.
track_new = util.convert(config.get(CONF_TRACK_NEW), bool, track_new = config.get(CONF_TRACK_NEW)
DEFAULT_TRACK_NEW)
if not devs_to_track and not track_new: if not devs_to_track and not track_new:
_LOGGER.warning("No Bluetooth LE devices to track!") _LOGGER.warning("No Bluetooth LE devices to track!")
return False return False
interval = util.convert(config.get(CONF_SCAN_INTERVAL), int, interval = config.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL)
DEFAULT_SCAN_INTERVAL)
def update_ble(now): def update_ble(now):
"""Lookup Bluetooth LE devices and update status.""" """Lookup Bluetooth LE devices and update status."""
@ -113,8 +110,7 @@ def setup_scanner(hass, config, see):
_LOGGER.info("Discovered Bluetooth LE device %s", address) _LOGGER.info("Discovered Bluetooth LE device %s", address)
see_device(address, devs[address], new_device=True) see_device(address, devs[address], new_device=True)
track_point_in_utc_time(hass, update_ble, track_point_in_utc_time(hass, update_ble, now + interval)
now + timedelta(seconds=interval))
update_ble(dt_util.utcnow()) update_ble(dt_util.utcnow())

View File

@ -1,6 +1,5 @@
"""Tracking for bluetooth devices.""" """Tracking for bluetooth devices."""
import logging import logging
from datetime import timedelta
import voluptuous as vol import voluptuous as vol
@ -83,8 +82,7 @@ def setup_scanner(hass, config, see):
see_device((mac, result)) see_device((mac, result))
except bluetooth.BluetoothError: except bluetooth.BluetoothError:
_LOGGER.exception('Error looking up bluetooth device!') _LOGGER.exception('Error looking up bluetooth device!')
track_point_in_utc_time(hass, update_bluetooth, track_point_in_utc_time(hass, update_bluetooth, now + interval)
now + timedelta(seconds=interval))
update_bluetooth(dt_util.utcnow()) update_bluetooth(dt_util.utcnow())

View File

@ -74,8 +74,11 @@ class UPCDeviceScanner(DeviceScanner):
return [] return []
raw = yield from self._async_ws_function(CMD_DEVICES) raw = yield from self._async_ws_function(CMD_DEVICES)
xml_root = ET.fromstring(raw) if raw is None:
_LOGGER.warning("Can't read device from %s", self.host)
return
xml_root = ET.fromstring(raw)
return [mac.text for mac in xml_root.iter('MACAddr')] return [mac.text for mac in xml_root.iter('MACAddr')]
@asyncio.coroutine @asyncio.coroutine
@ -94,7 +97,8 @@ class UPCDeviceScanner(DeviceScanner):
"http://{}/common_page/login.html".format(self.host) "http://{}/common_page/login.html".format(self.host)
) )
self.token = self._async_get_token() yield from response.text()
self.token = response.cookies['sessionToken'].value
# login # login
data = yield from self._async_ws_function(CMD_LOGIN, { data = yield from self._async_ws_function(CMD_LOGIN, {
@ -144,7 +148,7 @@ class UPCDeviceScanner(DeviceScanner):
# load data, store token for next request # load data, store token for next request
raw = yield from response.text() raw = yield from response.text()
self.token = self._async_get_token() self.token = response.cookies['sessionToken'].value
return raw return raw
@ -155,10 +159,3 @@ class UPCDeviceScanner(DeviceScanner):
finally: finally:
if response is not None: if response is not None:
yield from response.release() yield from response.release()
def _async_get_token(self):
"""Extract token from cookies."""
cookie_manager = self.websession.cookie_jar.filter_cookies(
"http://{}".format(self.host))
return cookie_manager.get('sessionToken')

View File

@ -37,7 +37,7 @@ def setup_scanner(hass, config, see):
config.get(CONF_USERNAME), config.get(CONF_USERNAME),
config.get(CONF_PASSWORD)) config.get(CONF_PASSWORD))
interval = max(MIN_TIME_BETWEEN_SCANS.seconds, interval = max(MIN_TIME_BETWEEN_SCANS,
config.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL)) config.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL))
def _see_vehicle(vehicle): def _see_vehicle(vehicle):
@ -91,8 +91,7 @@ def setup_scanner(hass, config, see):
return True return True
finally: finally:
track_point_in_utc_time(hass, update, track_point_in_utc_time(hass, update, now + interval)
now + timedelta(seconds=interval))
_LOGGER.info('Logging in to service') _LOGGER.info('Logging in to service')
return update(utcnow()) return update(utcnow())

View File

@ -72,6 +72,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
continue continue
device['name'] = device['id'] + " " + ipaddr device['name'] = device['id'] + " " + ipaddr
device[ATTR_MODE] = 'rgbw' device[ATTR_MODE] = 'rgbw'
device[CONF_PROTOCOL] = None
light = FluxLight(device) light = FluxLight(device)
if light.is_valid: if light.is_valid:
lights.append(light) lights.append(light)

View File

@ -20,7 +20,7 @@ _LOGGER = logging.getLogger(__name__)
REQUIREMENTS = [ REQUIREMENTS = [
'http://github.com/technicalpickles/python-nest' 'http://github.com/technicalpickles/python-nest'
'/archive/e6c9d56a8df455d4d7746389811f2c1387e8cb33.zip' # nest-cam branch '/archive/e6c9d56a8df455d4d7746389811f2c1387e8cb33.zip' # nest-cam branch
'#python-nest==3.0.3'] '#python-nest==3.0.2']
DOMAIN = 'nest' DOMAIN = 'nest'

View File

@ -29,7 +29,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
def get_service(hass, config): def get_service(hass, config, discovery_info=None):
"""Get the Lannouncer notification service.""" """Get the Lannouncer notification service."""
host = config.get(CONF_HOST) host = config.get(CONF_HOST)
port = config.get(CONF_PORT) port = config.get(CONF_PORT)

View File

@ -22,6 +22,7 @@ REQUIREMENTS = ['myusps==1.0.1']
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
COOKIE = 'usps_cookies.pickle'
CONF_UPDATE_INTERVAL = 'update_interval' CONF_UPDATE_INTERVAL = 'update_interval'
ICON = 'mdi:package-variant-closed' ICON = 'mdi:package-variant-closed'
STATUS_DELIVERED = 'delivered' STATUS_DELIVERED = 'delivered'
@ -39,8 +40,10 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the USPS platform.""" """Setup the USPS platform."""
import myusps import myusps
try: try:
cookie = hass.config.path(COOKIE)
session = myusps.get_session(config.get(CONF_USERNAME), session = myusps.get_session(config.get(CONF_USERNAME),
config.get(CONF_PASSWORD)) config.get(CONF_PASSWORD),
cookie_path=cookie)
except myusps.USPSError: except myusps.USPSError:
_LOGGER.exception('Could not connect to My USPS') _LOGGER.exception('Could not connect to My USPS')
return False return False

View File

@ -2,7 +2,7 @@
"""Constants used by Home Assistant components.""" """Constants used by Home Assistant components."""
MAJOR_VERSION = 0 MAJOR_VERSION = 0
MINOR_VERSION = 36 MINOR_VERSION = 36
PATCH_VERSION = '0' PATCH_VERSION = '1'
__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)

View File

@ -60,7 +60,7 @@ def load_yaml(fname: str) -> Union[List, Dict]:
with open(fname, encoding='utf-8') as conf_file: with open(fname, encoding='utf-8') as conf_file:
# If configuration file is empty YAML returns None # If configuration file is empty YAML returns None
# We convert that to an empty dict # We convert that to an empty dict
return yaml.load(conf_file, Loader=SafeLineLoader) or {} return yaml.load(conf_file, Loader=SafeLineLoader) or OrderedDict()
except yaml.YAMLError as exc: except yaml.YAMLError as exc:
_LOGGER.error(exc) _LOGGER.error(exc)
raise HomeAssistantError(exc) raise HomeAssistantError(exc)

View File

@ -178,7 +178,7 @@ hikvision==0.4
# http://github.com/adafruit/Adafruit_Python_DHT/archive/310c59b0293354d07d94375f1365f7b9b9110c7d.zip#Adafruit_DHT==1.3.0 # http://github.com/adafruit/Adafruit_Python_DHT/archive/310c59b0293354d07d94375f1365f7b9b9110c7d.zip#Adafruit_DHT==1.3.0
# homeassistant.components.nest # homeassistant.components.nest
http://github.com/technicalpickles/python-nest/archive/e6c9d56a8df455d4d7746389811f2c1387e8cb33.zip#python-nest==3.0.3 http://github.com/technicalpickles/python-nest/archive/e6c9d56a8df455d4d7746389811f2c1387e8cb33.zip#python-nest==3.0.2
# homeassistant.components.switch.dlink # homeassistant.components.switch.dlink
https://github.com/LinuxChristian/pyW215/archive/v0.3.7.zip#pyW215==0.3.7 https://github.com/LinuxChristian/pyW215/archive/v0.3.7.zip#pyW215==0.3.7
@ -476,7 +476,7 @@ pysnmp==4.3.2
python-digitalocean==1.10.1 python-digitalocean==1.10.1
# homeassistant.components.climate.eq3btsmart # homeassistant.components.climate.eq3btsmart
python-eq3bt==0.1.2 python-eq3bt==0.1.4
# homeassistant.components.sensor.darksky # homeassistant.components.sensor.darksky
python-forecastio==1.3.5 python-forecastio==1.3.5

View File

@ -37,11 +37,9 @@ class AiohttpClientMocker:
content = b'' content = b''
if params: if params:
url = str(yarl.URL(url).with_query(params)) url = str(yarl.URL(url).with_query(params))
if cookies:
self._cookies.update(cookies)
self._mocks.append(AiohttpClientMockResponse( self._mocks.append(AiohttpClientMockResponse(
method, url, status, content, exc)) method, url, status, content, cookies, exc))
def get(self, *args, **kwargs): def get(self, *args, **kwargs):
"""Register a mock get request.""" """Register a mock get request."""
@ -68,10 +66,6 @@ class AiohttpClientMocker:
"""Number of requests made.""" """Number of requests made."""
return len(self.mock_calls) return len(self.mock_calls)
def filter_cookies(self, host):
"""Return hosts cookies."""
return self._cookies
def clear_requests(self): def clear_requests(self):
"""Reset mock calls.""" """Reset mock calls."""
self._mocks.clear() self._mocks.clear()
@ -97,7 +91,7 @@ class AiohttpClientMocker:
class AiohttpClientMockResponse: class AiohttpClientMockResponse:
"""Mock Aiohttp client response.""" """Mock Aiohttp client response."""
def __init__(self, method, url, status, response, exc=None): def __init__(self, method, url, status, response, cookies=None, exc=None):
"""Initialize a fake response.""" """Initialize a fake response."""
self.method = method self.method = method
self._url = url self._url = url
@ -107,6 +101,14 @@ class AiohttpClientMockResponse:
self.response = response self.response = response
self.exc = exc self.exc = exc
self._cookies = {}
if cookies:
for name, data in cookies.items():
cookie = mock.MagicMock()
cookie.value = data
self._cookies[name] = cookie
def match_request(self, method, url, params=None): def match_request(self, method, url, params=None):
"""Test if response answers request.""" """Test if response answers request."""
if method.lower() != self.method.lower(): if method.lower() != self.method.lower():
@ -140,6 +142,11 @@ class AiohttpClientMockResponse:
return True return True
@property
def cookies(self):
"""Return dict of cookies."""
return self._cookies
@asyncio.coroutine @asyncio.coroutine
def read(self): def read(self):
"""Return mock response.""" """Return mock response."""
@ -160,6 +167,10 @@ class AiohttpClientMockResponse:
"""Mock release.""" """Mock release."""
pass pass
def close(self):
"""Mock close."""
pass
@contextmanager @contextmanager
def mock_aiohttp_client(): def mock_aiohttp_client():
@ -173,6 +184,4 @@ def mock_aiohttp_client():
setattr(instance, method, setattr(instance, method,
functools.partial(mocker.match_request, method)) functools.partial(mocker.match_request, method))
instance.cookie_jar.filter_cookies = mocker.filter_cookies
yield mocker yield mocker

View File

@ -74,6 +74,12 @@ class TestYaml(unittest.TestCase):
doc = yaml.yaml.safe_load(file) doc = yaml.yaml.safe_load(file)
assert doc["key"] == "value" assert doc["key"] == "value"
with patch_yaml_files({'test.yaml': None}):
conf = 'key: !include test.yaml'
with io.StringIO(conf) as file:
doc = yaml.yaml.safe_load(file)
assert doc["key"] == {}
@patch('homeassistant.util.yaml.os.walk') @patch('homeassistant.util.yaml.os.walk')
def test_include_dir_list(self, mock_walk): def test_include_dir_list(self, mock_walk):
"""Test include dir list yaml.""" """Test include dir list yaml."""