Move elevation to core config and clean up HTTP mocking in tests (#2378)
* Stick version numbers * Move elevation to core config * Migrate forecast test to requests-mock * Migrate YR tests to requests-mock * Add requests_mock to requirements_test.txt * Move conf code from bootstrap to config * More config fixes * Fix some more issues * Add test for set config and failing auto detect
This commit is contained in:
parent
dc75b28b90
commit
6714392e9c
26 changed files with 1779 additions and 337 deletions
|
@ -3,7 +3,6 @@
|
|||
import logging
|
||||
import logging.handlers
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from threading import RLock
|
||||
|
@ -12,21 +11,15 @@ import voluptuous as vol
|
|||
|
||||
import homeassistant.components as core_components
|
||||
from homeassistant.components import group, persistent_notification
|
||||
import homeassistant.config as config_util
|
||||
import homeassistant.config as conf_util
|
||||
import homeassistant.core as core
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
import homeassistant.loader as loader
|
||||
import homeassistant.util.dt as date_util
|
||||
import homeassistant.util.location as loc_util
|
||||
import homeassistant.util.package as pkg_util
|
||||
from homeassistant.const import (
|
||||
CONF_CUSTOMIZE, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME,
|
||||
CONF_TEMPERATURE_UNIT, CONF_TIME_ZONE, EVENT_COMPONENT_LOADED,
|
||||
TEMP_CELSIUS, TEMP_FAHRENHEIT, PLATFORM_FORMAT, __version__)
|
||||
from homeassistant.const import EVENT_COMPONENT_LOADED, PLATFORM_FORMAT
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import (
|
||||
event_decorators, service, config_per_platform, extract_domain_configs,
|
||||
entity)
|
||||
event_decorators, service, config_per_platform, extract_domain_configs)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_SETUP_LOCK = RLock()
|
||||
|
@ -208,11 +201,6 @@ def prepare_setup_platform(hass, config, domain, platform_name):
|
|||
return platform
|
||||
|
||||
|
||||
def mount_local_lib_path(config_dir):
|
||||
"""Add local library to Python Path."""
|
||||
sys.path.insert(0, os.path.join(config_dir, 'deps'))
|
||||
|
||||
|
||||
# pylint: disable=too-many-branches, too-many-statements, too-many-arguments
|
||||
def from_config_dict(config, hass=None, config_dir=None, enable_log=True,
|
||||
verbose=False, skip_pip=False,
|
||||
|
@ -226,18 +214,17 @@ def from_config_dict(config, hass=None, config_dir=None, enable_log=True,
|
|||
if config_dir is not None:
|
||||
config_dir = os.path.abspath(config_dir)
|
||||
hass.config.config_dir = config_dir
|
||||
mount_local_lib_path(config_dir)
|
||||
_mount_local_lib_path(config_dir)
|
||||
|
||||
core_config = config.get(core.DOMAIN, {})
|
||||
|
||||
try:
|
||||
process_ha_core_config(hass, config_util.CORE_CONFIG_SCHEMA(
|
||||
core_config))
|
||||
except vol.MultipleInvalid as ex:
|
||||
conf_util.process_ha_core_config(hass, core_config)
|
||||
except vol.Invalid as ex:
|
||||
cv.log_exception(_LOGGER, ex, 'homeassistant', core_config)
|
||||
return None
|
||||
|
||||
process_ha_config_upgrade(hass)
|
||||
conf_util.process_ha_config_upgrade(hass)
|
||||
|
||||
if enable_log:
|
||||
enable_logging(hass, verbose, log_rotate_days)
|
||||
|
@ -292,12 +279,12 @@ def from_config_file(config_path, hass=None, verbose=False, skip_pip=True,
|
|||
# Set config dir to directory holding config file
|
||||
config_dir = os.path.abspath(os.path.dirname(config_path))
|
||||
hass.config.config_dir = config_dir
|
||||
mount_local_lib_path(config_dir)
|
||||
_mount_local_lib_path(config_dir)
|
||||
|
||||
enable_logging(hass, verbose, log_rotate_days)
|
||||
|
||||
try:
|
||||
config_dict = config_util.load_yaml_config_file(config_path)
|
||||
config_dict = conf_util.load_yaml_config_file(config_path)
|
||||
except HomeAssistantError:
|
||||
return None
|
||||
|
||||
|
@ -356,100 +343,12 @@ def enable_logging(hass, verbose=False, log_rotate_days=None):
|
|||
'Unable to setup error log %s (access denied)', err_log_path)
|
||||
|
||||
|
||||
def process_ha_config_upgrade(hass):
|
||||
"""Upgrade config if necessary."""
|
||||
version_path = hass.config.path('.HA_VERSION')
|
||||
|
||||
try:
|
||||
with open(version_path, 'rt') as inp:
|
||||
conf_version = inp.readline().strip()
|
||||
except FileNotFoundError:
|
||||
# Last version to not have this file
|
||||
conf_version = '0.7.7'
|
||||
|
||||
if conf_version == __version__:
|
||||
return
|
||||
|
||||
_LOGGER.info('Upgrading config directory from %s to %s', conf_version,
|
||||
__version__)
|
||||
|
||||
# This was where dependencies were installed before v0.18
|
||||
# Probably should keep this around until ~v0.20.
|
||||
lib_path = hass.config.path('lib')
|
||||
if os.path.isdir(lib_path):
|
||||
shutil.rmtree(lib_path)
|
||||
|
||||
lib_path = hass.config.path('deps')
|
||||
if os.path.isdir(lib_path):
|
||||
shutil.rmtree(lib_path)
|
||||
|
||||
with open(version_path, 'wt') as outp:
|
||||
outp.write(__version__)
|
||||
|
||||
|
||||
def process_ha_core_config(hass, config):
|
||||
"""Process the [homeassistant] section from the config."""
|
||||
hac = hass.config
|
||||
|
||||
def set_time_zone(time_zone_str):
|
||||
"""Helper method to set time zone."""
|
||||
if time_zone_str is None:
|
||||
return
|
||||
|
||||
time_zone = date_util.get_time_zone(time_zone_str)
|
||||
|
||||
if time_zone:
|
||||
hac.time_zone = time_zone
|
||||
date_util.set_default_time_zone(time_zone)
|
||||
else:
|
||||
_LOGGER.error('Received invalid time zone %s', time_zone_str)
|
||||
|
||||
for key, attr in ((CONF_LATITUDE, 'latitude'),
|
||||
(CONF_LONGITUDE, 'longitude'),
|
||||
(CONF_NAME, 'location_name')):
|
||||
if key in config:
|
||||
setattr(hac, attr, config[key])
|
||||
|
||||
if CONF_TIME_ZONE in config:
|
||||
set_time_zone(config.get(CONF_TIME_ZONE))
|
||||
|
||||
entity.set_customize(config.get(CONF_CUSTOMIZE))
|
||||
|
||||
if CONF_TEMPERATURE_UNIT in config:
|
||||
hac.temperature_unit = config[CONF_TEMPERATURE_UNIT]
|
||||
|
||||
# If we miss some of the needed values, auto detect them
|
||||
if None not in (
|
||||
hac.latitude, hac.longitude, hac.temperature_unit, hac.time_zone):
|
||||
return
|
||||
|
||||
_LOGGER.warning('Incomplete core config. Auto detecting location and '
|
||||
'temperature unit')
|
||||
|
||||
info = loc_util.detect_location_info()
|
||||
|
||||
if info is None:
|
||||
_LOGGER.error('Could not detect location information')
|
||||
return
|
||||
|
||||
if hac.latitude is None and hac.longitude is None:
|
||||
hac.latitude = info.latitude
|
||||
hac.longitude = info.longitude
|
||||
|
||||
if hac.temperature_unit is None:
|
||||
if info.use_fahrenheit:
|
||||
hac.temperature_unit = TEMP_FAHRENHEIT
|
||||
else:
|
||||
hac.temperature_unit = TEMP_CELSIUS
|
||||
|
||||
if hac.location_name is None:
|
||||
hac.location_name = info.city
|
||||
|
||||
if hac.time_zone is None:
|
||||
set_time_zone(info.time_zone)
|
||||
|
||||
|
||||
def _ensure_loader_prepared(hass):
|
||||
"""Ensure Home Assistant loader is prepared."""
|
||||
if not loader.PREPARED:
|
||||
loader.prepare(hass)
|
||||
|
||||
|
||||
def _mount_local_lib_path(config_dir):
|
||||
"""Add local library to Python Path."""
|
||||
sys.path.insert(0, os.path.join(config_dir, 'deps'))
|
||||
|
|
|
@ -121,16 +121,16 @@ def setup(hass, config):
|
|||
def handle_reload_config(call):
|
||||
"""Service handler for reloading core config."""
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant import config, bootstrap
|
||||
from homeassistant import config as conf_util
|
||||
|
||||
try:
|
||||
path = config.find_config_file(hass.config.config_dir)
|
||||
conf = config.load_yaml_config_file(path)
|
||||
path = conf_util.find_config_file(hass.config.config_dir)
|
||||
conf = conf_util.load_yaml_config_file(path)
|
||||
except HomeAssistantError as err:
|
||||
_LOGGER.error(err)
|
||||
return
|
||||
|
||||
bootstrap.process_ha_core_config(hass, conf.get(ha.DOMAIN) or {})
|
||||
conf_util.process_ha_core_config(hass, conf.get(ha.DOMAIN) or {})
|
||||
|
||||
hass.services.register(ha.DOMAIN, SERVICE_RELOAD_CORE_CONFIG,
|
||||
handle_reload_config)
|
||||
|
|
|
@ -17,7 +17,7 @@ from homeassistant.const import (STATE_OFF, STATE_PAUSED, STATE_PLAYING,
|
|||
CONF_PORT)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
REQUIREMENTS = ['pycmus>=0.1.0']
|
||||
REQUIREMENTS = ['pycmus==0.1.0']
|
||||
|
||||
SUPPORT_CMUS = SUPPORT_PAUSE | SUPPORT_VOLUME_SET | SUPPORT_TURN_OFF | \
|
||||
SUPPORT_TURN_ON | SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK | \
|
||||
|
|
|
@ -15,7 +15,6 @@ from homeassistant.const import (
|
|||
)
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.util import dt as dt_util
|
||||
from homeassistant.util import location
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -54,16 +53,12 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
|||
"""Setup the Yr.no sensor."""
|
||||
latitude = config.get(CONF_LATITUDE, hass.config.latitude)
|
||||
longitude = config.get(CONF_LONGITUDE, hass.config.longitude)
|
||||
elevation = config.get(CONF_ELEVATION)
|
||||
elevation = config.get(CONF_ELEVATION, hass.config.elevation or 0)
|
||||
|
||||
if None in (latitude, longitude):
|
||||
_LOGGER.error("Latitude or longitude not set in Home Assistant config")
|
||||
return False
|
||||
|
||||
if elevation is None:
|
||||
elevation = location.elevation(latitude,
|
||||
longitude)
|
||||
|
||||
coordinates = dict(lat=latitude,
|
||||
lon=longitude,
|
||||
msl=elevation)
|
||||
|
|
|
@ -12,7 +12,6 @@ from homeassistant.helpers.entity import Entity
|
|||
from homeassistant.helpers.event import (
|
||||
track_point_in_utc_time, track_utc_time_change)
|
||||
from homeassistant.util import dt as dt_util
|
||||
from homeassistant.util import location as location_util
|
||||
from homeassistant.const import CONF_ELEVATION
|
||||
|
||||
REQUIREMENTS = ['astral==1.2']
|
||||
|
@ -108,7 +107,7 @@ def setup(hass, config):
|
|||
|
||||
elevation = platform_config.get(CONF_ELEVATION)
|
||||
if elevation is None:
|
||||
elevation = location_util.elevation(latitude, longitude)
|
||||
elevation = hass.config.elevation or 0
|
||||
|
||||
from astral import Location
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ from homeassistant.components.thermostat import ThermostatDevice
|
|||
from homeassistant.const import TEMP_CELCIUS
|
||||
from homeassistant.helpers.temperature import convert
|
||||
|
||||
REQUIREMENTS = ['bluepy_devices>=0.2.0']
|
||||
REQUIREMENTS = ['bluepy_devices==0.2.0']
|
||||
|
||||
CONF_MAC = 'mac'
|
||||
CONF_DEVICES = 'devices'
|
||||
|
|
|
@ -1,31 +1,35 @@
|
|||
"""Module to help with parsing and generating configuration files."""
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
from types import MappingProxyType
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
import homeassistant.util.location as loc_util
|
||||
from homeassistant.const import (
|
||||
CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME, CONF_TEMPERATURE_UNIT,
|
||||
CONF_TIME_ZONE, CONF_CUSTOMIZE)
|
||||
CONF_TIME_ZONE, CONF_CUSTOMIZE, CONF_ELEVATION, TEMP_FAHRENHEIT,
|
||||
TEMP_CELSIUS, __version__)
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.util.yaml import load_yaml
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.entity import valid_entity_id
|
||||
from homeassistant.helpers.entity import valid_entity_id, set_customize
|
||||
from homeassistant.util import dt as date_util, location as loc_util
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
YAML_CONFIG_FILE = 'configuration.yaml'
|
||||
VERSION_FILE = '.HA_VERSION'
|
||||
CONFIG_DIR_NAME = '.homeassistant'
|
||||
|
||||
DEFAULT_CONFIG = (
|
||||
# Tuples (attribute, default, auto detect property, description)
|
||||
(CONF_NAME, 'Home', None, 'Name of the location where Home Assistant is '
|
||||
'running'),
|
||||
(CONF_LATITUDE, None, 'latitude', 'Location required to calculate the time'
|
||||
(CONF_LATITUDE, 0, 'latitude', 'Location required to calculate the time'
|
||||
' the sun rises and sets'),
|
||||
(CONF_LONGITUDE, None, 'longitude', None),
|
||||
(CONF_LONGITUDE, 0, 'longitude', None),
|
||||
(CONF_ELEVATION, 0, None, 'Impacts weather/sunrise data'),
|
||||
(CONF_TEMPERATURE_UNIT, 'C', None, 'C for Celsius, F for Fahrenheit'),
|
||||
(CONF_TIME_ZONE, 'UTC', 'time_zone', 'Pick yours from here: http://en.wiki'
|
||||
'pedia.org/wiki/List_of_tz_database_time_zones'),
|
||||
|
@ -39,7 +43,7 @@ DEFAULT_COMPONENTS = {
|
|||
'history:': 'Enables support for tracking state changes over time.',
|
||||
'logbook:': 'View all events in a logbook',
|
||||
'sun:': 'Track the sun',
|
||||
'sensor:\n platform: yr': 'Prediction of weather',
|
||||
'sensor:\n platform: yr': 'Weather Prediction',
|
||||
}
|
||||
|
||||
|
||||
|
@ -61,6 +65,7 @@ CORE_CONFIG_SCHEMA = vol.Schema({
|
|||
CONF_NAME: vol.Coerce(str),
|
||||
CONF_LATITUDE: cv.latitude,
|
||||
CONF_LONGITUDE: cv.longitude,
|
||||
CONF_ELEVATION: vol.Coerce(float),
|
||||
CONF_TEMPERATURE_UNIT: cv.temperature_unit,
|
||||
CONF_TIME_ZONE: cv.time_zone,
|
||||
vol.Required(CONF_CUSTOMIZE,
|
||||
|
@ -97,6 +102,7 @@ def create_default_config(config_dir, detect_location=True):
|
|||
Return path to new config file if success, None if failed.
|
||||
"""
|
||||
config_path = os.path.join(config_dir, YAML_CONFIG_FILE)
|
||||
version_path = os.path.join(config_dir, VERSION_FILE)
|
||||
|
||||
info = {attr: default for attr, default, _, _ in DEFAULT_CONFIG}
|
||||
|
||||
|
@ -111,6 +117,10 @@ def create_default_config(config_dir, detect_location=True):
|
|||
continue
|
||||
info[attr] = getattr(location_info, prop) or default
|
||||
|
||||
if location_info.latitude and location_info.longitude:
|
||||
info[CONF_ELEVATION] = loc_util.elevation(location_info.latitude,
|
||||
location_info.longitude)
|
||||
|
||||
# Writing files with YAML does not create the most human readable results
|
||||
# So we're hard coding a YAML template.
|
||||
try:
|
||||
|
@ -130,6 +140,9 @@ def create_default_config(config_dir, detect_location=True):
|
|||
config_file.write("# {}\n".format(description))
|
||||
config_file.write("{}\n\n".format(component))
|
||||
|
||||
with open(version_path, 'wt') as version_file:
|
||||
version_file.write(__version__)
|
||||
|
||||
return config_path
|
||||
|
||||
except IOError:
|
||||
|
@ -155,3 +168,112 @@ def load_yaml_config_file(config_path):
|
|||
raise HomeAssistantError(msg)
|
||||
|
||||
return conf_dict
|
||||
|
||||
|
||||
def process_ha_config_upgrade(hass):
|
||||
"""Upgrade config if necessary."""
|
||||
version_path = hass.config.path(VERSION_FILE)
|
||||
|
||||
try:
|
||||
with open(version_path, 'rt') as inp:
|
||||
conf_version = inp.readline().strip()
|
||||
except FileNotFoundError:
|
||||
# Last version to not have this file
|
||||
conf_version = '0.7.7'
|
||||
|
||||
if conf_version == __version__:
|
||||
return
|
||||
|
||||
_LOGGER.info('Upgrading config directory from %s to %s', conf_version,
|
||||
__version__)
|
||||
|
||||
lib_path = hass.config.path('deps')
|
||||
if os.path.isdir(lib_path):
|
||||
shutil.rmtree(lib_path)
|
||||
|
||||
with open(version_path, 'wt') as outp:
|
||||
outp.write(__version__)
|
||||
|
||||
|
||||
def process_ha_core_config(hass, config):
|
||||
"""Process the [homeassistant] section from the config."""
|
||||
# pylint: disable=too-many-branches
|
||||
config = CORE_CONFIG_SCHEMA(config)
|
||||
hac = hass.config
|
||||
|
||||
def set_time_zone(time_zone_str):
|
||||
"""Helper method to set time zone."""
|
||||
if time_zone_str is None:
|
||||
return
|
||||
|
||||
time_zone = date_util.get_time_zone(time_zone_str)
|
||||
|
||||
if time_zone:
|
||||
hac.time_zone = time_zone
|
||||
date_util.set_default_time_zone(time_zone)
|
||||
else:
|
||||
_LOGGER.error('Received invalid time zone %s', time_zone_str)
|
||||
|
||||
for key, attr in ((CONF_LATITUDE, 'latitude'),
|
||||
(CONF_LONGITUDE, 'longitude'),
|
||||
(CONF_NAME, 'location_name'),
|
||||
(CONF_ELEVATION, 'elevation')):
|
||||
if key in config:
|
||||
setattr(hac, attr, config[key])
|
||||
|
||||
if CONF_TIME_ZONE in config:
|
||||
set_time_zone(config.get(CONF_TIME_ZONE))
|
||||
|
||||
set_customize(config.get(CONF_CUSTOMIZE) or {})
|
||||
|
||||
if CONF_TEMPERATURE_UNIT in config:
|
||||
hac.temperature_unit = config[CONF_TEMPERATURE_UNIT]
|
||||
|
||||
# Shortcut if no auto-detection necessary
|
||||
if None not in (hac.latitude, hac.longitude, hac.temperature_unit,
|
||||
hac.time_zone, hac.elevation):
|
||||
return
|
||||
|
||||
discovered = []
|
||||
|
||||
# If we miss some of the needed values, auto detect them
|
||||
if None in (hac.latitude, hac.longitude, hac.temperature_unit,
|
||||
hac.time_zone):
|
||||
info = loc_util.detect_location_info()
|
||||
|
||||
if info is None:
|
||||
_LOGGER.error('Could not detect location information')
|
||||
return
|
||||
|
||||
if hac.latitude is None and hac.longitude is None:
|
||||
hac.latitude = info.latitude
|
||||
hac.longitude = info.longitude
|
||||
discovered.append(('latitude', hac.latitude))
|
||||
discovered.append(('longitude', hac.longitude))
|
||||
|
||||
if hac.temperature_unit is None:
|
||||
if info.use_fahrenheit:
|
||||
hac.temperature_unit = TEMP_FAHRENHEIT
|
||||
discovered.append(('temperature_unit', 'F'))
|
||||
else:
|
||||
hac.temperature_unit = TEMP_CELSIUS
|
||||
discovered.append(('temperature_unit', 'C'))
|
||||
|
||||
if hac.location_name is None:
|
||||
hac.location_name = info.city
|
||||
discovered.append(('name', info.city))
|
||||
|
||||
if hac.time_zone is None:
|
||||
set_time_zone(info.time_zone)
|
||||
discovered.append(('time_zone', info.time_zone))
|
||||
|
||||
if hac.elevation is None and hac.latitude is not None and \
|
||||
hac.longitude is not None:
|
||||
elevation = loc_util.elevation(hac.latitude, hac.longitude)
|
||||
hac.elevation = elevation
|
||||
discovered.append(('elevation', elevation))
|
||||
|
||||
if discovered:
|
||||
_LOGGER.warning(
|
||||
'Incomplete core config. Auto detected %s',
|
||||
', '.join('{}: {}'.format(key, val) for key, val in discovered))
|
||||
|
|
|
@ -681,6 +681,7 @@ class Config(object):
|
|||
"""Initialize a new config object."""
|
||||
self.latitude = None
|
||||
self.longitude = None
|
||||
self.elevation = None
|
||||
self.temperature_unit = None
|
||||
self.location_name = None
|
||||
self.time_zone = None
|
||||
|
|
|
@ -8,7 +8,8 @@ import math
|
|||
import requests
|
||||
|
||||
ELEVATION_URL = 'http://maps.googleapis.com/maps/api/elevation/json'
|
||||
DATA_SOURCE = ['https://freegeoip.io/json/', 'http://ip-api.com/json']
|
||||
FREEGEO_API = 'https://freegeoip.io/json/'
|
||||
IP_API = 'http://ip-api.com/json'
|
||||
|
||||
# Constants from https://github.com/maurycyp/vincenty
|
||||
# Earth ellipsoid according to WGS 84
|
||||
|
@ -32,30 +33,13 @@ LocationInfo = collections.namedtuple(
|
|||
|
||||
def detect_location_info():
|
||||
"""Detect location information."""
|
||||
success = None
|
||||
data = _get_freegeoip()
|
||||
|
||||
for source in DATA_SOURCE:
|
||||
try:
|
||||
raw_info = requests.get(source, timeout=5).json()
|
||||
success = source
|
||||
break
|
||||
except (requests.RequestException, ValueError):
|
||||
success = False
|
||||
if data is None:
|
||||
data = _get_ip_api()
|
||||
|
||||
if success is False:
|
||||
if data is None:
|
||||
return None
|
||||
else:
|
||||
data = {key: raw_info.get(key) for key in LocationInfo._fields}
|
||||
if success is DATA_SOURCE[1]:
|
||||
data['ip'] = raw_info.get('query')
|
||||
data['country_code'] = raw_info.get('countryCode')
|
||||
data['country_name'] = raw_info.get('country')
|
||||
data['region_code'] = raw_info.get('region')
|
||||
data['region_name'] = raw_info.get('regionName')
|
||||
data['zip_code'] = raw_info.get('zip')
|
||||
data['time_zone'] = raw_info.get('timezone')
|
||||
data['latitude'] = raw_info.get('lat')
|
||||
data['longitude'] = raw_info.get('lon')
|
||||
|
||||
# From Wikipedia: Fahrenheit is used in the Bahamas, Belize,
|
||||
# the Cayman Islands, Palau, and the United States and associated
|
||||
|
@ -73,11 +57,16 @@ def distance(lat1, lon1, lat2, lon2):
|
|||
|
||||
def elevation(latitude, longitude):
|
||||
"""Return elevation for given latitude and longitude."""
|
||||
req = requests.get(ELEVATION_URL,
|
||||
params={'locations': '{},{}'.format(latitude,
|
||||
longitude),
|
||||
'sensor': 'false'},
|
||||
timeout=10)
|
||||
try:
|
||||
req = requests.get(
|
||||
ELEVATION_URL,
|
||||
params={
|
||||
'locations': '{},{}'.format(latitude, longitude),
|
||||
'sensor': 'false',
|
||||
},
|
||||
timeout=10)
|
||||
except requests.RequestException:
|
||||
return 0
|
||||
|
||||
if req.status_code != 200:
|
||||
return 0
|
||||
|
@ -157,3 +146,45 @@ def vincenty(point1, point2, miles=False):
|
|||
s *= MILES_PER_KILOMETER # kilometers to miles
|
||||
|
||||
return round(s, 6)
|
||||
|
||||
|
||||
def _get_freegeoip():
|
||||
"""Query freegeoip.io for location data."""
|
||||
try:
|
||||
raw_info = requests.get(FREEGEO_API, timeout=5).json()
|
||||
except (requests.RequestException, ValueError):
|
||||
return None
|
||||
|
||||
return {
|
||||
'ip': raw_info.get('ip'),
|
||||
'country_code': raw_info.get('country_code'),
|
||||
'country_name': raw_info.get('country_name'),
|
||||
'region_code': raw_info.get('region_code'),
|
||||
'region_name': raw_info.get('region_name'),
|
||||
'city': raw_info.get('city'),
|
||||
'zip_code': raw_info.get('zip_code'),
|
||||
'time_zone': raw_info.get('time_zone'),
|
||||
'latitude': raw_info.get('latitude'),
|
||||
'longitude': raw_info.get('longitude'),
|
||||
}
|
||||
|
||||
|
||||
def _get_ip_api():
|
||||
"""Query ip-api.com for location data."""
|
||||
try:
|
||||
raw_info = requests.get(IP_API, timeout=5).json()
|
||||
except (requests.RequestException, ValueError):
|
||||
return None
|
||||
|
||||
return {
|
||||
'ip': raw_info.get('query'),
|
||||
'country_code': raw_info.get('countryCode'),
|
||||
'country_name': raw_info.get('country'),
|
||||
'region_code': raw_info.get('region'),
|
||||
'region_name': raw_info.get('regionName'),
|
||||
'city': raw_info.get('city'),
|
||||
'zip_code': raw_info.get('zip'),
|
||||
'time_zone': raw_info.get('timezone'),
|
||||
'latitude': raw_info.get('lat'),
|
||||
'longitude': raw_info.get('lon'),
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ blinkstick==1.1.7
|
|||
blockchain==1.3.3
|
||||
|
||||
# homeassistant.components.thermostat.eq3btsmart
|
||||
# bluepy_devices>=0.2.0
|
||||
# bluepy_devices==0.2.0
|
||||
|
||||
# homeassistant.components.notify.aws_lambda
|
||||
# homeassistant.components.notify.aws_sns
|
||||
|
@ -245,7 +245,7 @@ pyasn1==0.1.9
|
|||
pychromecast==0.7.2
|
||||
|
||||
# homeassistant.components.media_player.cmus
|
||||
pycmus>=0.1.0
|
||||
pycmus==0.1.0
|
||||
|
||||
# homeassistant.components.envisalink
|
||||
# homeassistant.components.zwave
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
flake8>=2.5.4
|
||||
pylint>=1.5.5
|
||||
flake8>=2.6.0
|
||||
pylint>=1.5.6
|
||||
coveralls>=1.1
|
||||
pytest>=2.9.1
|
||||
pytest-cov>=2.2.0
|
||||
pytest>=2.9.2
|
||||
pytest-cov>=2.2.1
|
||||
pytest-timeout>=1.0.0
|
||||
pytest-capturelog>=0.7
|
||||
betamax==0.7.0
|
||||
pydocstyle>=1.0.0
|
||||
httpretty==0.8.14
|
||||
requests_mock>=1.0
|
||||
|
|
|
@ -1,27 +1,25 @@
|
|||
"""Test the initialization."""
|
||||
import betamax
|
||||
"""Setup some common test helper things."""
|
||||
import functools
|
||||
|
||||
from homeassistant import util
|
||||
from homeassistant.util import location
|
||||
|
||||
with betamax.Betamax.configure() as config:
|
||||
config.cassette_library_dir = 'tests/cassettes'
|
||||
|
||||
# Automatically called during different setups. Too often forgotten
|
||||
# so mocked by default.
|
||||
location.detect_location_info = lambda: location.LocationInfo(
|
||||
ip='1.1.1.1',
|
||||
country_code='US',
|
||||
country_name='United States',
|
||||
region_code='CA',
|
||||
region_name='California',
|
||||
city='San Diego',
|
||||
zip_code='92122',
|
||||
time_zone='America/Los_Angeles',
|
||||
latitude='2.0',
|
||||
longitude='1.0',
|
||||
use_fahrenheit=True,
|
||||
)
|
||||
def test_real(func):
|
||||
"""Force a function to require a keyword _test_real to be passed in."""
|
||||
@functools.wraps(func)
|
||||
def guard_func(*args, **kwargs):
|
||||
real = kwargs.pop('_test_real', None)
|
||||
|
||||
location.elevation = lambda latitude, longitude: 0
|
||||
if not real:
|
||||
raise Exception('Forgot to mock or pass "_test_real=True" to %s',
|
||||
func.__name__)
|
||||
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return guard_func
|
||||
|
||||
# Guard a few functions that would make network connections
|
||||
location.detect_location_info = test_real(location.detect_location_info)
|
||||
location.elevation = test_real(location.elevation)
|
||||
util.get_local_ip = lambda: '127.0.0.1'
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -35,6 +35,7 @@ def get_test_home_assistant(num_threads=None):
|
|||
hass.config.config_dir = get_test_config_dir()
|
||||
hass.config.latitude = 32.87336
|
||||
hass.config.longitude = -117.22743
|
||||
hass.config.elevation = 0
|
||||
hass.config.time_zone = date_util.get_time_zone('US/Pacific')
|
||||
hass.config.temperature_unit = TEMP_CELSIUS
|
||||
|
||||
|
@ -105,6 +106,13 @@ def ensure_sun_set(hass):
|
|||
fire_time_changed(hass, sun.next_setting_utc(hass) + timedelta(seconds=10))
|
||||
|
||||
|
||||
def load_fixture(filename):
|
||||
"""Helper to load a fixture."""
|
||||
path = os.path.join(os.path.dirname(__file__), 'fixtures', filename)
|
||||
with open(path) as fp:
|
||||
return fp.read()
|
||||
|
||||
|
||||
def mock_state_change_event(hass, new_state, old_state=None):
|
||||
"""Mock state change envent."""
|
||||
event_data = {
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
"""The tests for the forecast.io platform."""
|
||||
import json
|
||||
import re
|
||||
import os
|
||||
import unittest
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import forecastio
|
||||
import httpretty
|
||||
from requests.exceptions import HTTPError
|
||||
import requests_mock
|
||||
|
||||
from homeassistant.components.sensor import forecast
|
||||
from homeassistant import core as ha
|
||||
|
||||
from tests.common import load_fixture
|
||||
|
||||
|
||||
class TestForecastSetup(unittest.TestCase):
|
||||
"""Test the forecast.io platform."""
|
||||
|
@ -48,29 +48,14 @@ class TestForecastSetup(unittest.TestCase):
|
|||
response = forecast.setup_platform(self.hass, self.config, MagicMock())
|
||||
self.assertFalse(response)
|
||||
|
||||
@httpretty.activate
|
||||
@requests_mock.Mocker()
|
||||
@patch('forecastio.api.get_forecast', wraps=forecastio.api.get_forecast)
|
||||
def test_setup(self, mock_get_forecast):
|
||||
def test_setup(self, m, mock_get_forecast):
|
||||
"""Test for successfully setting up the forecast.io platform."""
|
||||
def load_fixture_from_json():
|
||||
cwd = os.path.dirname(__file__)
|
||||
fixture_path = os.path.join(cwd, '..', 'fixtures', 'forecast.json')
|
||||
with open(fixture_path) as file:
|
||||
content = json.load(file)
|
||||
return json.dumps(content)
|
||||
|
||||
# Mock out any calls to the actual API and
|
||||
# return the fixture json instead
|
||||
uri = 'api.forecast.io\/forecast\/(\w+)\/(-?\d+\.?\d*),(-?\d+\.?\d*)'
|
||||
httpretty.register_uri(
|
||||
httpretty.GET,
|
||||
re.compile(uri),
|
||||
body=load_fixture_from_json(),
|
||||
)
|
||||
# The following will raise an error if the regex for the mock was
|
||||
# incorrect and we actually try to go out to the internet.
|
||||
httpretty.HTTPretty.allow_net_connect = False
|
||||
|
||||
uri = ('https://api.forecast.io\/forecast\/(\w+)\/'
|
||||
'(-?\d+\.?\d*),(-?\d+\.?\d*)')
|
||||
m.get(re.compile(uri),
|
||||
text=load_fixture('forecast.json'))
|
||||
forecast.setup_platform(self.hass, self.config, MagicMock())
|
||||
self.assertTrue(mock_get_forecast.called)
|
||||
self.assertEqual(mock_get_forecast.call_count, 1)
|
|
@ -1,39 +1,40 @@
|
|||
"""The tests for the Yr sensor platform."""
|
||||
from datetime import datetime
|
||||
from unittest import TestCase
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
import requests_mock
|
||||
|
||||
from homeassistant.bootstrap import _setup_component
|
||||
import homeassistant.util.dt as dt_util
|
||||
from tests.common import get_test_home_assistant
|
||||
from tests.common import get_test_home_assistant, load_fixture
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('betamax_session')
|
||||
class TestSensorYr:
|
||||
class TestSensorYr(TestCase):
|
||||
"""Test the Yr sensor."""
|
||||
|
||||
def setup_method(self, method):
|
||||
def setUp(self):
|
||||
"""Setup things to be run when tests are started."""
|
||||
self.hass = get_test_home_assistant()
|
||||
self.hass.config.latitude = 32.87336
|
||||
self.hass.config.longitude = 117.22743
|
||||
|
||||
def teardown_method(self, method):
|
||||
def tearDown(self):
|
||||
"""Stop everything that was started."""
|
||||
self.hass.stop()
|
||||
|
||||
def test_default_setup(self, betamax_session):
|
||||
@requests_mock.Mocker()
|
||||
def test_default_setup(self, m):
|
||||
"""Test the default setup."""
|
||||
m.get('http://api.yr.no/weatherapi/locationforecast/1.9/',
|
||||
text=load_fixture('yr.no.json'))
|
||||
now = datetime(2016, 6, 9, 1, tzinfo=dt_util.UTC)
|
||||
|
||||
with patch('homeassistant.components.sensor.yr.requests.Session',
|
||||
return_value=betamax_session):
|
||||
with patch('homeassistant.components.sensor.yr.dt_util.utcnow',
|
||||
return_value=now):
|
||||
assert _setup_component(self.hass, 'sensor', {
|
||||
'sensor': {'platform': 'yr',
|
||||
'elevation': 0}})
|
||||
with patch('homeassistant.components.sensor.yr.dt_util.utcnow',
|
||||
return_value=now):
|
||||
assert _setup_component(self.hass, 'sensor', {
|
||||
'sensor': {'platform': 'yr',
|
||||
'elevation': 0}})
|
||||
|
||||
state = self.hass.states.get('sensor.yr_symbol')
|
||||
|
||||
|
@ -41,23 +42,24 @@ class TestSensorYr:
|
|||
assert state.state.isnumeric()
|
||||
assert state.attributes.get('unit_of_measurement') is None
|
||||
|
||||
def test_custom_setup(self, betamax_session):
|
||||
@requests_mock.Mocker()
|
||||
def test_custom_setup(self, m):
|
||||
"""Test a custom setup."""
|
||||
m.get('http://api.yr.no/weatherapi/locationforecast/1.9/',
|
||||
text=load_fixture('yr.no.json'))
|
||||
now = datetime(2016, 6, 9, 1, tzinfo=dt_util.UTC)
|
||||
|
||||
with patch('homeassistant.components.sensor.yr.requests.Session',
|
||||
return_value=betamax_session):
|
||||
with patch('homeassistant.components.sensor.yr.dt_util.utcnow',
|
||||
return_value=now):
|
||||
assert _setup_component(self.hass, 'sensor', {
|
||||
'sensor': {'platform': 'yr',
|
||||
'elevation': 0,
|
||||
'monitored_conditions': [
|
||||
'pressure',
|
||||
'windDirection',
|
||||
'humidity',
|
||||
'fog',
|
||||
'windSpeed']}})
|
||||
with patch('homeassistant.components.sensor.yr.dt_util.utcnow',
|
||||
return_value=now):
|
||||
assert _setup_component(self.hass, 'sensor', {
|
||||
'sensor': {'platform': 'yr',
|
||||
'elevation': 0,
|
||||
'monitored_conditions': [
|
||||
'pressure',
|
||||
'windDirection',
|
||||
'humidity',
|
||||
'fog',
|
||||
'windSpeed']}})
|
||||
|
||||
state = self.hass.states.get('sensor.yr_pressure')
|
||||
assert 'hPa' == state.attributes.get('unit_of_measurement')
|
||||
|
|
|
@ -131,7 +131,7 @@ class TestComponentsCore(unittest.TestCase):
|
|||
assert state.attributes.get('hello') == 'world'
|
||||
|
||||
@patch('homeassistant.components._LOGGER.error')
|
||||
@patch('homeassistant.bootstrap.process_ha_core_config')
|
||||
@patch('homeassistant.config.process_ha_core_config')
|
||||
def test_reload_core_with_wrong_conf(self, mock_process, mock_error):
|
||||
"""Test reload core conf service."""
|
||||
with TemporaryDirectory() as conf_dir:
|
||||
|
|
13
tests/fixtures/freegeoip.io.json
vendored
Normal file
13
tests/fixtures/freegeoip.io.json
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"ip": "1.2.3.4",
|
||||
"country_code": "US",
|
||||
"country_name": "United States",
|
||||
"region_code": "CA",
|
||||
"region_name": "California",
|
||||
"city": "San Diego",
|
||||
"zip_code": "92122",
|
||||
"time_zone": "America\/Los_Angeles",
|
||||
"latitude": 32.8594,
|
||||
"longitude": -117.2073,
|
||||
"metro_code": 825
|
||||
}
|
13
tests/fixtures/google_maps_elevation.json
vendored
Normal file
13
tests/fixtures/google_maps_elevation.json
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"results" : [
|
||||
{
|
||||
"elevation" : 101.5,
|
||||
"location" : {
|
||||
"lat" : 32.54321,
|
||||
"lng" : -117.12345
|
||||
},
|
||||
"resolution" : 4.8
|
||||
}
|
||||
],
|
||||
"status" : "OK"
|
||||
}
|
16
tests/fixtures/ip-api.com.json
vendored
Normal file
16
tests/fixtures/ip-api.com.json
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"as": "AS20001 Time Warner Cable Internet LLC",
|
||||
"city": "San Diego",
|
||||
"country": "United States",
|
||||
"countryCode": "US",
|
||||
"isp": "Time Warner Cable",
|
||||
"lat": 32.8594,
|
||||
"lon": -117.2073,
|
||||
"org": "Time Warner Cable",
|
||||
"query": "1.2.3.4",
|
||||
"region": "CA",
|
||||
"regionName": "California",
|
||||
"status": "success",
|
||||
"timezone": "America\/Los_Angeles",
|
||||
"zip": "92122"
|
||||
}
|
1184
tests/fixtures/yr.no.json
vendored
Normal file
1184
tests/fixtures/yr.no.json
vendored
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,5 @@
|
|||
"""Test the bootstrapping."""
|
||||
# pylint: disable=too-many-public-methods,protected-access
|
||||
import os
|
||||
import tempfile
|
||||
from unittest import mock
|
||||
import threading
|
||||
|
@ -8,10 +7,7 @@ import threading
|
|||
import voluptuous as vol
|
||||
|
||||
from homeassistant import bootstrap, loader
|
||||
from homeassistant.const import (__version__, CONF_LATITUDE, CONF_LONGITUDE,
|
||||
CONF_NAME, CONF_CUSTOMIZE)
|
||||
import homeassistant.util.dt as dt_util
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA
|
||||
|
||||
from tests.common import get_test_home_assistant, MockModule, MockPlatform
|
||||
|
@ -24,23 +20,22 @@ class TestBootstrap:
|
|||
|
||||
def setup_method(self, method):
|
||||
"""Setup the test."""
|
||||
self.backup_cache = loader._COMPONENT_CACHE
|
||||
|
||||
if method == self.test_from_config_file:
|
||||
return
|
||||
|
||||
self.hass = get_test_home_assistant()
|
||||
self.backup_cache = loader._COMPONENT_CACHE
|
||||
|
||||
def teardown_method(self, method):
|
||||
"""Clean up."""
|
||||
dt_util.DEFAULT_TIME_ZONE = ORIG_TIMEZONE
|
||||
|
||||
if method == self.test_from_config_file:
|
||||
return
|
||||
|
||||
self.hass.stop()
|
||||
loader._COMPONENT_CACHE = self.backup_cache
|
||||
|
||||
def test_from_config_file(self):
|
||||
@mock.patch('homeassistant.util.location.detect_location_info',
|
||||
return_value=None)
|
||||
def test_from_config_file(self, mock_detect):
|
||||
"""Test with configuration file."""
|
||||
components = ['browser', 'conversation', 'script']
|
||||
with tempfile.NamedTemporaryFile() as fp:
|
||||
|
@ -48,71 +43,10 @@ class TestBootstrap:
|
|||
fp.write('{}:\n'.format(comp).encode('utf-8'))
|
||||
fp.flush()
|
||||
|
||||
hass = bootstrap.from_config_file(fp.name)
|
||||
self.hass = bootstrap.from_config_file(fp.name)
|
||||
|
||||
components.append('group')
|
||||
|
||||
assert sorted(components) == sorted(hass.config.components)
|
||||
|
||||
def test_remove_lib_on_upgrade(self):
|
||||
"""Test removal of library on upgrade."""
|
||||
with tempfile.TemporaryDirectory() as config_dir:
|
||||
version_path = os.path.join(config_dir, '.HA_VERSION')
|
||||
lib_dir = os.path.join(config_dir, 'deps')
|
||||
check_file = os.path.join(lib_dir, 'check')
|
||||
|
||||
with open(version_path, 'wt') as outp:
|
||||
outp.write('0.7.0')
|
||||
|
||||
os.mkdir(lib_dir)
|
||||
|
||||
with open(check_file, 'w'):
|
||||
pass
|
||||
|
||||
self.hass.config.config_dir = config_dir
|
||||
|
||||
assert os.path.isfile(check_file)
|
||||
bootstrap.process_ha_config_upgrade(self.hass)
|
||||
assert not os.path.isfile(check_file)
|
||||
|
||||
def test_not_remove_lib_if_not_upgrade(self):
|
||||
"""Test removal of library with no upgrade."""
|
||||
with tempfile.TemporaryDirectory() as config_dir:
|
||||
version_path = os.path.join(config_dir, '.HA_VERSION')
|
||||
lib_dir = os.path.join(config_dir, 'deps')
|
||||
check_file = os.path.join(lib_dir, 'check')
|
||||
|
||||
with open(version_path, 'wt') as outp:
|
||||
outp.write(__version__)
|
||||
|
||||
os.mkdir(lib_dir)
|
||||
|
||||
with open(check_file, 'w'):
|
||||
pass
|
||||
|
||||
self.hass.config.config_dir = config_dir
|
||||
|
||||
bootstrap.process_ha_config_upgrade(self.hass)
|
||||
|
||||
assert os.path.isfile(check_file)
|
||||
|
||||
def test_entity_customization(self):
|
||||
"""Test entity customization through configuration."""
|
||||
config = {CONF_LATITUDE: 50,
|
||||
CONF_LONGITUDE: 50,
|
||||
CONF_NAME: 'Test',
|
||||
CONF_CUSTOMIZE: {'test.test': {'hidden': True}}}
|
||||
|
||||
bootstrap.process_ha_core_config(self.hass, config)
|
||||
|
||||
entity = Entity()
|
||||
entity.entity_id = 'test.test'
|
||||
entity.hass = self.hass
|
||||
entity.update_ha_state()
|
||||
|
||||
state = self.hass.states.get('test.test')
|
||||
|
||||
assert state.attributes['hidden']
|
||||
components.append('group')
|
||||
assert sorted(components) == sorted(self.hass.config.components)
|
||||
|
||||
def test_handle_setup_circular_dependency(self):
|
||||
"""Test the setup of circular dependencies."""
|
||||
|
@ -302,8 +236,7 @@ class TestBootstrap:
|
|||
assert not bootstrap._setup_component(self.hass, 'comp', None)
|
||||
assert 'comp' not in self.hass.config.components
|
||||
|
||||
@mock.patch('homeassistant.bootstrap.process_ha_core_config')
|
||||
def test_home_assistant_core_config_validation(self, mock_process):
|
||||
def test_home_assistant_core_config_validation(self):
|
||||
"""Test if we pass in wrong information for HA conf."""
|
||||
# Extensive HA conf validation testing is done in test_config.py
|
||||
assert None is bootstrap.from_config_dict({
|
||||
|
@ -311,7 +244,6 @@ class TestBootstrap:
|
|||
'latitude': 'some string'
|
||||
}
|
||||
})
|
||||
assert not mock_process.called
|
||||
|
||||
def test_component_setup_with_validation_and_dependency(self):
|
||||
"""Test all config is passed to dependencies."""
|
||||
|
|
|
@ -1,22 +1,28 @@
|
|||
"""Test config utils."""
|
||||
# pylint: disable=too-many-public-methods,protected-access
|
||||
import os
|
||||
import tempfile
|
||||
import unittest
|
||||
import unittest.mock as mock
|
||||
import os
|
||||
|
||||
import pytest
|
||||
from voluptuous import MultipleInvalid
|
||||
|
||||
from homeassistant.core import DOMAIN, HomeAssistantError
|
||||
from homeassistant.core import DOMAIN, HomeAssistantError, Config
|
||||
import homeassistant.config as config_util
|
||||
from homeassistant.const import (
|
||||
CONF_LATITUDE, CONF_LONGITUDE, CONF_TEMPERATURE_UNIT, CONF_NAME,
|
||||
CONF_TIME_ZONE)
|
||||
CONF_TIME_ZONE, CONF_ELEVATION, CONF_CUSTOMIZE, __version__,
|
||||
TEMP_FAHRENHEIT)
|
||||
from homeassistant.util import location as location_util, dt as dt_util
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
from tests.common import get_test_config_dir
|
||||
from tests.common import (
|
||||
get_test_config_dir, get_test_home_assistant)
|
||||
|
||||
CONFIG_DIR = get_test_config_dir()
|
||||
YAML_PATH = os.path.join(CONFIG_DIR, config_util.YAML_CONFIG_FILE)
|
||||
ORIG_TIMEZONE = dt_util.DEFAULT_TIME_ZONE
|
||||
|
||||
|
||||
def create_file(path):
|
||||
|
@ -30,9 +36,14 @@ class TestConfig(unittest.TestCase):
|
|||
|
||||
def tearDown(self): # pylint: disable=invalid-name
|
||||
"""Clean up."""
|
||||
dt_util.DEFAULT_TIME_ZONE = ORIG_TIMEZONE
|
||||
|
||||
if os.path.isfile(YAML_PATH):
|
||||
os.remove(YAML_PATH)
|
||||
|
||||
if hasattr(self, 'hass'):
|
||||
self.hass.stop()
|
||||
|
||||
def test_create_default_config(self):
|
||||
"""Test creation of default config."""
|
||||
config_util.create_default_config(CONFIG_DIR, False)
|
||||
|
@ -108,8 +119,15 @@ class TestConfig(unittest.TestCase):
|
|||
[('hello', 0), ('world', 1)],
|
||||
list(config_util.load_yaml_config_file(YAML_PATH).items()))
|
||||
|
||||
@mock.patch('homeassistant.util.location.detect_location_info',
|
||||
return_value=location_util.LocationInfo(
|
||||
'0.0.0.0', 'US', 'United States', 'CA', 'California',
|
||||
'San Diego', '92122', 'America/Los_Angeles', 32.8594,
|
||||
-117.2073, True))
|
||||
@mock.patch('homeassistant.util.location.elevation', return_value=101)
|
||||
@mock.patch('builtins.print')
|
||||
def test_create_default_config_detect_location(self, mock_print):
|
||||
def test_create_default_config_detect_location(self, mock_detect,
|
||||
mock_elev, mock_print):
|
||||
"""Test that detect location sets the correct config keys."""
|
||||
config_util.ensure_config_exists(CONFIG_DIR)
|
||||
|
||||
|
@ -120,15 +138,16 @@ class TestConfig(unittest.TestCase):
|
|||
ha_conf = config[DOMAIN]
|
||||
|
||||
expected_values = {
|
||||
CONF_LATITUDE: 2.0,
|
||||
CONF_LONGITUDE: 1.0,
|
||||
CONF_LATITUDE: 32.8594,
|
||||
CONF_LONGITUDE: -117.2073,
|
||||
CONF_ELEVATION: 101,
|
||||
CONF_TEMPERATURE_UNIT: 'F',
|
||||
CONF_NAME: 'Home',
|
||||
CONF_TIME_ZONE: 'America/Los_Angeles'
|
||||
}
|
||||
|
||||
self.assertEqual(expected_values, ha_conf)
|
||||
self.assertTrue(mock_print.called)
|
||||
assert expected_values == ha_conf
|
||||
assert mock_print.called
|
||||
|
||||
@mock.patch('builtins.print')
|
||||
def test_create_default_config_returns_none_if_write_error(self,
|
||||
|
@ -166,3 +185,127 @@ class TestConfig(unittest.TestCase):
|
|||
},
|
||||
},
|
||||
})
|
||||
|
||||
def test_entity_customization(self):
|
||||
"""Test entity customization through configuration."""
|
||||
self.hass = get_test_home_assistant()
|
||||
|
||||
config = {CONF_LATITUDE: 50,
|
||||
CONF_LONGITUDE: 50,
|
||||
CONF_NAME: 'Test',
|
||||
CONF_CUSTOMIZE: {'test.test': {'hidden': True}}}
|
||||
|
||||
config_util.process_ha_core_config(self.hass, config)
|
||||
|
||||
entity = Entity()
|
||||
entity.entity_id = 'test.test'
|
||||
entity.hass = self.hass
|
||||
entity.update_ha_state()
|
||||
|
||||
state = self.hass.states.get('test.test')
|
||||
|
||||
assert state.attributes['hidden']
|
||||
|
||||
def test_remove_lib_on_upgrade(self):
|
||||
"""Test removal of library on upgrade."""
|
||||
with tempfile.TemporaryDirectory() as config_dir:
|
||||
version_path = os.path.join(config_dir, '.HA_VERSION')
|
||||
lib_dir = os.path.join(config_dir, 'deps')
|
||||
check_file = os.path.join(lib_dir, 'check')
|
||||
|
||||
with open(version_path, 'wt') as outp:
|
||||
outp.write('0.7.0')
|
||||
|
||||
os.mkdir(lib_dir)
|
||||
|
||||
with open(check_file, 'w'):
|
||||
pass
|
||||
|
||||
self.hass = get_test_home_assistant()
|
||||
self.hass.config.config_dir = config_dir
|
||||
|
||||
assert os.path.isfile(check_file)
|
||||
config_util.process_ha_config_upgrade(self.hass)
|
||||
assert not os.path.isfile(check_file)
|
||||
|
||||
def test_not_remove_lib_if_not_upgrade(self):
|
||||
"""Test removal of library with no upgrade."""
|
||||
with tempfile.TemporaryDirectory() as config_dir:
|
||||
version_path = os.path.join(config_dir, '.HA_VERSION')
|
||||
lib_dir = os.path.join(config_dir, 'deps')
|
||||
check_file = os.path.join(lib_dir, 'check')
|
||||
|
||||
with open(version_path, 'wt') as outp:
|
||||
outp.write(__version__)
|
||||
|
||||
os.mkdir(lib_dir)
|
||||
|
||||
with open(check_file, 'w'):
|
||||
pass
|
||||
|
||||
self.hass = get_test_home_assistant()
|
||||
self.hass.config.config_dir = config_dir
|
||||
|
||||
config_util.process_ha_config_upgrade(self.hass)
|
||||
|
||||
assert os.path.isfile(check_file)
|
||||
|
||||
def test_loading_configuration(self):
|
||||
"""Test loading core config onto hass object."""
|
||||
config = Config()
|
||||
hass = mock.Mock(config=config)
|
||||
|
||||
config_util.process_ha_core_config(hass, {
|
||||
'latitude': 60,
|
||||
'longitude': 50,
|
||||
'elevation': 25,
|
||||
'name': 'Huis',
|
||||
'temperature_unit': 'F',
|
||||
'time_zone': 'America/New_York',
|
||||
})
|
||||
|
||||
assert config.latitude == 60
|
||||
assert config.longitude == 50
|
||||
assert config.elevation == 25
|
||||
assert config.location_name == 'Huis'
|
||||
assert config.temperature_unit == TEMP_FAHRENHEIT
|
||||
assert config.time_zone.zone == 'America/New_York'
|
||||
|
||||
@mock.patch('homeassistant.util.location.detect_location_info',
|
||||
return_value=location_util.LocationInfo(
|
||||
'0.0.0.0', 'US', 'United States', 'CA', 'California',
|
||||
'San Diego', '92122', 'America/Los_Angeles', 32.8594,
|
||||
-117.2073, True))
|
||||
@mock.patch('homeassistant.util.location.elevation', return_value=101)
|
||||
def test_discovering_configuration(self, mock_detect, mock_elevation):
|
||||
"""Test auto discovery for missing core configs."""
|
||||
config = Config()
|
||||
hass = mock.Mock(config=config)
|
||||
|
||||
config_util.process_ha_core_config(hass, {})
|
||||
|
||||
assert config.latitude == 32.8594
|
||||
assert config.longitude == -117.2073
|
||||
assert config.elevation == 101
|
||||
assert config.location_name == 'San Diego'
|
||||
assert config.temperature_unit == TEMP_FAHRENHEIT
|
||||
assert config.time_zone.zone == 'America/Los_Angeles'
|
||||
|
||||
@mock.patch('homeassistant.util.location.detect_location_info',
|
||||
return_value=None)
|
||||
@mock.patch('homeassistant.util.location.elevation', return_value=0)
|
||||
def test_discovering_configuration_auto_detect_fails(self, mock_detect,
|
||||
mock_elevation):
|
||||
"""Test config remains unchanged if discovery fails."""
|
||||
config = Config()
|
||||
hass = mock.Mock(config=config)
|
||||
|
||||
config_util.process_ha_core_config(hass, {})
|
||||
|
||||
blankConfig = Config()
|
||||
assert config.latitude == blankConfig.latitude
|
||||
assert config.longitude == blankConfig.longitude
|
||||
assert config.elevation == blankConfig.elevation
|
||||
assert config.location_name == blankConfig.location_name
|
||||
assert config.temperature_unit == blankConfig.temperature_unit
|
||||
assert config.time_zone == blankConfig.time_zone
|
||||
|
|
|
@ -1,9 +1,15 @@
|
|||
"""Test Home Assistant location util methods."""
|
||||
# pylint: disable=too-many-public-methods
|
||||
import unittest
|
||||
from unittest import TestCase
|
||||
from unittest.mock import patch
|
||||
|
||||
import requests
|
||||
import requests_mock
|
||||
|
||||
import homeassistant.util.location as location_util
|
||||
|
||||
from tests.common import load_fixture
|
||||
|
||||
# Paris
|
||||
COORDINATES_PARIS = (48.864716, 2.349014)
|
||||
# New York
|
||||
|
@ -20,26 +26,124 @@ DISTANCE_KM = 5846.39
|
|||
DISTANCE_MILES = 3632.78
|
||||
|
||||
|
||||
class TestLocationUtil(unittest.TestCase):
|
||||
class TestLocationUtil(TestCase):
|
||||
"""Test util location methods."""
|
||||
|
||||
def test_get_distance_to_same_place(self):
|
||||
"""Test getting the distance."""
|
||||
meters = location_util.distance(COORDINATES_PARIS[0],
|
||||
COORDINATES_PARIS[1],
|
||||
COORDINATES_PARIS[0],
|
||||
COORDINATES_PARIS[1])
|
||||
|
||||
assert meters == 0
|
||||
|
||||
def test_get_distance(self):
|
||||
"""Test getting the distance."""
|
||||
meters = location_util.distance(COORDINATES_PARIS[0],
|
||||
COORDINATES_PARIS[1],
|
||||
COORDINATES_NEW_YORK[0],
|
||||
COORDINATES_NEW_YORK[1])
|
||||
self.assertAlmostEqual(meters / 1000, DISTANCE_KM, places=2)
|
||||
|
||||
assert meters/1000 - DISTANCE_KM < 0.01
|
||||
|
||||
def test_get_kilometers(self):
|
||||
"""Test getting the distance between given coordinates in km."""
|
||||
kilometers = location_util.vincenty(COORDINATES_PARIS,
|
||||
COORDINATES_NEW_YORK)
|
||||
self.assertEqual(round(kilometers, 2), DISTANCE_KM)
|
||||
assert round(kilometers, 2) == DISTANCE_KM
|
||||
|
||||
def test_get_miles(self):
|
||||
"""Test getting the distance between given coordinates in miles."""
|
||||
miles = location_util.vincenty(COORDINATES_PARIS,
|
||||
COORDINATES_NEW_YORK,
|
||||
miles=True)
|
||||
self.assertEqual(round(miles, 2), DISTANCE_MILES)
|
||||
assert round(miles, 2) == DISTANCE_MILES
|
||||
|
||||
@requests_mock.Mocker()
|
||||
def test_detect_location_info_freegeoip(self, m):
|
||||
"""Test detect location info using freegeoip."""
|
||||
m.get(location_util.FREEGEO_API,
|
||||
text=load_fixture('freegeoip.io.json'))
|
||||
|
||||
info = location_util.detect_location_info(_test_real=True)
|
||||
|
||||
assert info is not None
|
||||
assert info.ip == '1.2.3.4'
|
||||
assert info.country_code == 'US'
|
||||
assert info.country_name == 'United States'
|
||||
assert info.region_code == 'CA'
|
||||
assert info.region_name == 'California'
|
||||
assert info.city == 'San Diego'
|
||||
assert info.zip_code == '92122'
|
||||
assert info.time_zone == 'America/Los_Angeles'
|
||||
assert info.latitude == 32.8594
|
||||
assert info.longitude == -117.2073
|
||||
assert info.use_fahrenheit
|
||||
|
||||
@requests_mock.Mocker()
|
||||
@patch('homeassistant.util.location._get_freegeoip', return_value=None)
|
||||
def test_detect_location_info_ipapi(self, mock_req, mock_freegeoip):
|
||||
"""Test detect location info using freegeoip."""
|
||||
mock_req.get(location_util.IP_API,
|
||||
text=load_fixture('ip-api.com.json'))
|
||||
|
||||
info = location_util.detect_location_info(_test_real=True)
|
||||
|
||||
assert info is not None
|
||||
assert info.ip == '1.2.3.4'
|
||||
assert info.country_code == 'US'
|
||||
assert info.country_name == 'United States'
|
||||
assert info.region_code == 'CA'
|
||||
assert info.region_name == 'California'
|
||||
assert info.city == 'San Diego'
|
||||
assert info.zip_code == '92122'
|
||||
assert info.time_zone == 'America/Los_Angeles'
|
||||
assert info.latitude == 32.8594
|
||||
assert info.longitude == -117.2073
|
||||
assert info.use_fahrenheit
|
||||
|
||||
@patch('homeassistant.util.location.elevation', return_value=0)
|
||||
@patch('homeassistant.util.location._get_freegeoip', return_value=None)
|
||||
@patch('homeassistant.util.location._get_ip_api', return_value=None)
|
||||
def test_detect_location_info_both_queries_fail(self, mock_ipapi,
|
||||
mock_freegeoip,
|
||||
mock_elevation):
|
||||
"""Ensure we return None if both queries fail."""
|
||||
info = location_util.detect_location_info(_test_real=True)
|
||||
assert info is None
|
||||
|
||||
@patch('homeassistant.util.location.requests.get',
|
||||
side_effect=requests.RequestException)
|
||||
def test_freegeoip_query_raises(self, mock_get):
|
||||
"""Test freegeoip query when the request to API fails."""
|
||||
info = location_util._get_freegeoip()
|
||||
assert info is None
|
||||
|
||||
@patch('homeassistant.util.location.requests.get',
|
||||
side_effect=requests.RequestException)
|
||||
def test_ip_api_query_raises(self, mock_get):
|
||||
"""Test ip api query when the request to API fails."""
|
||||
info = location_util._get_ip_api()
|
||||
assert info is None
|
||||
|
||||
@patch('homeassistant.util.location.requests.get',
|
||||
side_effect=requests.RequestException)
|
||||
def test_elevation_query_raises(self, mock_get):
|
||||
"""Test elevation when the request to API fails."""
|
||||
elevation = location_util.elevation(10, 10, _test_real=True)
|
||||
assert elevation == 0
|
||||
|
||||
@requests_mock.Mocker()
|
||||
def test_elevation_query_fails(self, mock_req):
|
||||
"""Test elevation when the request to API fails."""
|
||||
mock_req.get(location_util.ELEVATION_URL, text='{}', status_code=401)
|
||||
elevation = location_util.elevation(10, 10, _test_real=True)
|
||||
assert elevation == 0
|
||||
|
||||
@requests_mock.Mocker()
|
||||
def test_elevation_query_nonjson(self, mock_req):
|
||||
"""Test if elevation API returns a non JSON value."""
|
||||
mock_req.get(location_util.ELEVATION_URL, text='{ I am not JSON }')
|
||||
elevation = location_util.elevation(10, 10, _test_real=True)
|
||||
assert elevation == 0
|
||||
|
|
|
@ -49,7 +49,7 @@ class TestPackageUtil(unittest.TestCase):
|
|||
self.assertTrue(package.check_package_exists(
|
||||
TEST_NEW_REQ, self.lib_dir))
|
||||
|
||||
bootstrap.mount_local_lib_path(self.tmp_dir.name)
|
||||
bootstrap._mount_local_lib_path(self.tmp_dir.name)
|
||||
|
||||
try:
|
||||
import pyhelloworld3
|
||||
|
|
Loading…
Add table
Reference in a new issue