Add unit system support

Add unit symbol constants

Initial unit system object

Import more constants

Pydoc for unit system file

Import constants for configuration validation

Unit system validation method

Typing for constants

Inches are valid lengths too

Typings

Change base class to dict - needed for remote api call serialization

Validation

Use dictionary keys

Defined unit systems

Update location util to use metric instead of us fahrenheit

Update constant imports

Import defined unit systems

Update configuration to use unit system

Update schema to use unit system

Update constants

Add imports to core for unit system and distance

Type for config

Default unit system

Convert distance from HASS instance

Update temperature conversion to use unit system

Update temperature conversion

Set unit system based on configuration

Set info unit system

Return unit system dictionary with config dictionary

Auto discover unit system

Update location test for use metric

Update forecast unit system

Update mold indicator unit system

Update thermostat unit system

Update thermostat demo test

Unit tests around unit system

Update test common hass configuration

Update configuration unit tests

There should always be a unit system!

Update core unit tests

Constants typing

Linting issues

Remove unused import

Update fitbit sensor to use application unit system

Update google travel time to use application unit system

Update configuration example

Update dht sensor

Update DHT temperature conversion to use the utility function

Update swagger config

Update my sensors metric flag

Update hvac component temperature conversion

HVAC conversion for temperature

Pull unit from sensor type map

Pull unit from sensor type map

Update the temper sensor unit

Update yWeather sensor unit

Update hvac demo unit test

Set unit test config unit system to metric

Use hass unit system length for default in proximity

Use the name of the system instead of temperature

Use constants from const

Unused import

Forecasted temperature

Fix calculation in case furthest distance is greater than 1000000 units

Remove unneeded constants

Set default length to km or miles

Use constants

Linting doesn't like importing just for typing

Fix reference

Test is expecting meters - set config to meters

Use constant

Use constant

PyDoc for unit test

Should be not in

Rename to units

Change unit system to be an object - not a dictionary

Return tuple in conversion

Move convert to temperature util

Temperature conversion is now in unit system

Update imports

Rename to units

Units is now an object

Use temperature util conversion

Unit system is now an object

Validate and convert unit system config

Return the scalar value in template distance

Test is expecting meters

Update unit tests around unit system

Distance util returns tuple

Fix location info test

Set units

Update unit tests

Convert distance

DOH

Pull out the scalar from the vector

Linting

I really hate python linting

Linting again

BLARG

Unit test documentation

Unit test around is metric flag

Break ternary statement into if/else blocks

Don't use dictionary - use members

is metric flag

Rename constants

Use is metric flag

Move constants to CONST file

Move to const file

Raise error if unit is not expected

Typing

No need to return unit since only performing conversion if it can work

Use constants

Line wrapping

Raise error if invalid value

Remove subscripts from conversion as they are no longer returned as tuples

No longer tuples

No longer tuples

Check for numeric type

Fix string format to use correct variable

Typing

Assert errors raised

Remove subscript

Only convert temperature if we know the unit

If no unit of measurement set - default to HASS config

Convert only if we know the unit

Remove subscription

Fix not in clause

Linting fixes

Wants a boolean

Clearer if-block

Check if the key is in the config first

Missed a couple expecting tuples

Backwards compatibility

No like-y ternary!

Error handling around state setting

Pretty unit system configuration validation

More tuple crap

Use is metric flag

Error handling around min/max temp

Explode if no unit

Pull unit from config

Celsius has a decimal

Unused import

Check if it's a temperature before we try to convert it to a temperature

Linting says too many statements - combine lat/long in a fairly reasonable manner

Backwards compatibility unit test

Better doc
This commit is contained in:
Teagan M. Glenn 2016-07-31 14:24:49 -06:00 committed by Paulus Schoutsen
parent 792154a6a7
commit 26526ca57a
39 changed files with 597 additions and 263 deletions

View File

@ -10,8 +10,8 @@ homeassistant:
# Impacts weather/sunrise data
elevation: 665
# C for Celsius, F for Fahrenheit
temperature_unit: C
# 'metric' for Metric System, 'imperial' for imperial system
unit_system: metric
# Pick yours from here:
# http://en.wikipedia.org/wiki/List_of_tz_database_time_zones

View File

@ -419,8 +419,9 @@ definitions:
description: Longitude of Home Assistant server
location_name:
type: string
temperature_unit:
unit_system:
type: string
description: The system for measurement units
time_zone:
type: string
version:

View File

@ -6,13 +6,14 @@ https://home-assistant.io/components/hvac/
"""
import logging
import os
from numbers import Number
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.config import load_yaml_config_file
import homeassistant.util as util
from homeassistant.util.temperature import convert as convert_temperature
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.temperature import convert
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
from homeassistant.const import (
ATTR_ENTITY_ID, ATTR_TEMPERATURE, STATE_ON, STATE_OFF, STATE_UNKNOWN,
@ -204,8 +205,8 @@ def setup(hass, config):
return
for hvac in target_hvacs:
hvac.set_temperature(convert(
temperature, hass.config.temperature_unit,
hvac.set_temperature(convert_temperature(
temperature, hass.config.units.temperature_unit,
hvac.unit_of_measurement))
if hvac.should_poll:
@ -462,12 +463,12 @@ class HvacDevice(Entity):
@property
def min_temp(self):
"""Return the minimum temperature."""
return convert(19, TEMP_CELSIUS, self.unit_of_measurement)
return convert_temperature(19, TEMP_CELSIUS, self.unit_of_measurement)
@property
def max_temp(self):
"""Return the maximum temperature."""
return convert(30, TEMP_CELSIUS, self.unit_of_measurement)
return convert_temperature(30, TEMP_CELSIUS, self.unit_of_measurement)
@property
def min_humidity(self):
@ -481,13 +482,13 @@ class HvacDevice(Entity):
def _convert_for_display(self, temp):
"""Convert temperature into preferred units for display purposes."""
if temp is None:
return None
if temp is None or not isinstance(temp, Number):
return temp
value = convert(temp, self.unit_of_measurement,
self.hass.config.temperature_unit)
value = convert_temperature(temp, self.unit_of_measurement,
self.hass.config.units.temperature_unit)
if self.hass.config.temperature_unit is TEMP_CELSIUS:
if self.hass.config.units.temperature_unit is TEMP_CELSIUS:
decimal_count = 1
else:
# Users of fahrenheit generally expect integer units.

View File

@ -10,7 +10,7 @@ import socket
from homeassistant.const import (ATTR_BATTERY_LEVEL, CONF_OPTIMISTIC,
EVENT_HOMEASSISTANT_START,
EVENT_HOMEASSISTANT_STOP,
STATE_OFF, STATE_ON, TEMP_CELSIUS)
STATE_OFF, STATE_ON)
from homeassistant.helpers import validate_config, discovery
CONF_GATEWAYS = 'gateways'
@ -53,7 +53,7 @@ def setup(hass, config): # pylint: disable=too-many-locals
import mysensors.mysensors as mysensors
version = str(config[DOMAIN].get(CONF_VERSION, DEFAULT_VERSION))
is_metric = (hass.config.temperature_unit == TEMP_CELSIUS)
is_metric = hass.config.units.is_metric
persistence = config[DOMAIN].get(CONF_PERSISTENCE, True)
def setup_gateway(device, persistence_file, baud_rate, tcp_port):

View File

@ -27,9 +27,6 @@ DEFAULT_TOLERANCE = 1
# Default zone
DEFAULT_PROXIMITY_ZONE = 'home'
# Default unit of measure
DEFAULT_UNIT_OF_MEASUREMENT = 'km'
# Default distance to zone
DEFAULT_DIST_TO_ZONE = NOT_SET
@ -71,7 +68,7 @@ def setup_proximity_component(hass, config):
# Get the unit of measurement from configuration.yaml.
unit_of_measure = config.get(ATTR_UNIT_OF_MEASUREMENT,
DEFAULT_UNIT_OF_MEASUREMENT)
hass.config.units.length_unit)
zone_id = 'zone.{}'.format(proximity_zone)
state = hass.states.get(zone_id)
@ -216,11 +213,11 @@ class Proximity(Entity): # pylint: disable=too-many-instance-attributes
# Loop through each of the distances collected and work out the
# closest.
closest_device = ''
dist_to_zone = 1000000
closest_device = None # type: str
dist_to_zone = None # type: float
for device in distances_to_zone:
if distances_to_zone[device] < dist_to_zone:
if not dist_to_zone or distances_to_zone[device] < dist_to_zone:
closest_device = device
dist_to_zone = distances_to_zone[device]

View File

@ -10,6 +10,7 @@ from datetime import timedelta
from homeassistant.const import TEMP_FAHRENHEIT
from homeassistant.helpers.entity import Entity
from homeassistant.util import Throttle
from homeassistant.util.temperature import celsius_to_fahrenheit
# Update this requirement to upstream as soon as it supports Python 3.
REQUIREMENTS = ['http://github.com/mala-zaba/Adafruit_Python_DHT/archive/'
@ -32,8 +33,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
# pylint: disable=import-error
import Adafruit_DHT
SENSOR_TYPES['temperature'][1] = hass.config.temperature_unit
unit = hass.config.temperature_unit
SENSOR_TYPES['temperature'][1] = hass.config.units.temperature_unit
available_sensors = {
"DHT11": Adafruit_DHT.DHT11,
"DHT22": Adafruit_DHT.DHT22,
@ -58,7 +58,8 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
if variable not in SENSOR_TYPES:
_LOGGER.error('Sensor type: "%s" does not exist', variable)
else:
dev.append(DHTSensor(data, variable, unit, name))
dev.append(
DHTSensor(data, variable, SENSOR_TYPES[variable][1], name))
except KeyError:
pass
@ -103,7 +104,8 @@ class DHTSensor(Entity):
if self.type == 'temperature':
self._state = round(data['temperature'], 1)
if self.temp_unit == TEMP_FAHRENHEIT:
self._state = round(data['temperature'] * 1.8 + 32, 1)
self._state = round(celsius_to_fahrenheit(data['temperature']),
1)
elif self.type == 'humidity':
self._state = round(data['humidity'], 1)

View File

@ -10,7 +10,6 @@ import logging
import datetime
import time
from homeassistant.const import TEMP_CELSIUS
from homeassistant.util import Throttle
from homeassistant.helpers.entity import Entity
from homeassistant.loader import get_component
@ -238,8 +237,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
for resource in config.get("monitored_resources",
FITBIT_DEFAULT_RESOURCE_LIST):
dev.append(FitbitSensor(authd_client, config_path, resource,
hass.config.temperature_unit ==
TEMP_CELSIUS))
hass.config.units.is_metric))
add_devices(dev)
else:

View File

@ -10,7 +10,7 @@ from requests.exceptions import ConnectionError as ConnectError, \
HTTPError, Timeout
from homeassistant.components.sensor import DOMAIN
from homeassistant.const import CONF_API_KEY, TEMP_CELSIUS
from homeassistant.const import CONF_API_KEY
from homeassistant.helpers import validate_config
from homeassistant.helpers.entity import Entity
from homeassistant.util import Throttle
@ -62,7 +62,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
if 'units' in config:
units = config['units']
elif hass.config.temperature_unit == TEMP_CELSIUS:
elif hass.config.units.is_metric:
units = 'si'
else:
units = 'us'

View File

@ -11,8 +11,7 @@ import voluptuous as vol
from homeassistant.helpers.entity import Entity
from homeassistant.const import (
CONF_API_KEY, TEMP_CELSIUS, TEMP_FAHRENHEIT,
EVENT_HOMEASSISTANT_START, ATTR_LATITUDE, ATTR_LONGITUDE)
CONF_API_KEY, EVENT_HOMEASSISTANT_START, ATTR_LATITUDE, ATTR_LONGITUDE)
from homeassistant.util import Throttle
import homeassistant.helpers.config_validation as cv
@ -92,10 +91,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
options = config.get(CONF_OPTIONS)
if options.get('units') is None:
if hass.config.temperature_unit is TEMP_CELSIUS:
options['units'] = 'metric'
elif hass.config.temperature_unit is TEMP_FAHRENHEIT:
options['units'] = 'imperial'
options['units'] = hass.config.units.name
travel_mode = config.get(CONF_TRAVEL_MODE)
mode = options.get(CONF_MODE)

View File

@ -65,7 +65,7 @@ class MoldIndicator(Entity):
self._indoor_humidity_sensor = indoor_humidity_sensor
self._outdoor_temp_sensor = outdoor_temp_sensor
self._calib_factor = calib_factor
self._is_metric = (hass.config.temperature_unit == TEMP_CELSIUS)
self._is_metric = hass.config.units.is_metric
self._dewpoint = None
self._indoor_temp = None

View File

@ -48,8 +48,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
from pyowm import OWM
SENSOR_TYPES['temperature'][1] = hass.config.temperature_unit
unit = hass.config.temperature_unit
SENSOR_TYPES['temperature'][1] = hass.config.units.temperature_unit
forecast = config.get('forecast')
owm = OWM(config.get(CONF_API_KEY, None))
@ -67,13 +66,15 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
if variable not in SENSOR_TYPES:
_LOGGER.error('Sensor type: "%s" does not exist', variable)
else:
dev.append(OpenWeatherMapSensor(data, variable, unit))
dev.append(OpenWeatherMapSensor(data, variable,
SENSOR_TYPES[variable][1]))
except KeyError:
pass
if forecast:
SENSOR_TYPES['forecast'] = ['Forecast', None]
dev.append(OpenWeatherMapSensor(data, 'forecast', unit))
dev.append(OpenWeatherMapSensor(data, 'forecast',
SENSOR_TYPES['temperature'][1]))
add_devices(dev)

View File

@ -21,7 +21,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
"""Setup the Temper sensors."""
from temperusb.temper import TemperHandler
temp_unit = hass.config.temperature_unit
temp_unit = hass.config.units.temperature_unit
name = config.get(CONF_NAME, DEVICE_DEFAULT_NAME)
temper_devices = TemperHandler().get_devices()
add_devices_callback([TemperSensor(dev, temp_unit, name + '_' + str(idx))

View File

@ -61,11 +61,11 @@ class VeraSensor(VeraDevice, Entity):
self._temperature_units = TEMP_CELSIUS
if self.hass:
temp = self.hass.config.temperature(
temp = self.hass.config.units.temperature(
current_temp,
self._temperature_units)
current_temp, self._temperature_units = temp
current_temp = temp
self.current_value = current_temp
elif self.vera_device.category == "Light Sensor":

View File

@ -45,7 +45,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Yahoo! weather sensor."""
from yahooweather import get_woeid, UNIT_C, UNIT_F
unit = hass.config.temperature_unit
unit = hass.config.units.temperature_unit
woeid = config.get("woeid", None)
forecast = config.get("forecast", 0)

View File

@ -6,6 +6,7 @@ https://home-assistant.io/components/thermostat/
"""
import logging
import os
from numbers import Number
import voluptuous as vol
@ -13,9 +14,9 @@ from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.config import load_yaml_config_file
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.temperature import convert
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
import homeassistant.helpers.config_validation as cv
from homeassistant.util.temperature import convert
from homeassistant.const import (
ATTR_ENTITY_ID, ATTR_TEMPERATURE, STATE_ON, STATE_OFF, STATE_UNKNOWN,
TEMP_CELSIUS)
@ -146,9 +147,13 @@ def setup(hass, config):
temperature = service.data[ATTR_TEMPERATURE]
for thermostat in target_thermostats:
thermostat.set_temperature(convert(
temperature, hass.config.temperature_unit,
thermostat.unit_of_measurement))
if thermostat.unit_of_measurement is not None:
converted_temperature = convert(
temperature, hass.config.units.temperature_unit,
thermostat.unit_of_measurement)
else:
converted_temperature = temperature
thermostat.set_temperature(converted_temperature)
thermostat.update_ha_state(True)
@ -301,22 +306,30 @@ class ThermostatDevice(Entity):
@property
def min_temp(self):
"""Return the minimum temperature."""
return convert(7, TEMP_CELSIUS, self.unit_of_measurement)
try:
unit = self.unit_of_measurement
return convert(7, TEMP_CELSIUS, unit)
except ValueError:
return STATE_UNKNOWN
@property
def max_temp(self):
"""Return the maximum temperature."""
return convert(35, TEMP_CELSIUS, self.unit_of_measurement)
try:
unit = self.unit_of_measurement
return convert(35, TEMP_CELSIUS, unit)
except ValueError:
return STATE_UNKNOWN
def _convert_for_display(self, temp):
"""Convert temperature into preferred units for display purposes."""
if temp is None:
return None
if temp is None or not isinstance(temp, Number):
return temp
value = convert(temp, self.unit_of_measurement,
self.hass.config.temperature_unit)
value = self.hass.config.units.temperature(temp,
self.unit_of_measurement)
if self.hass.config.temperature_unit is TEMP_CELSIUS:
if self.hass.config.units.is_metric:
decimal_count = 1
else:
# Users of fahrenheit generally expect integer units.

View File

@ -8,7 +8,7 @@ import logging
from homeassistant.components.thermostat import ThermostatDevice
from homeassistant.const import TEMP_CELSIUS
from homeassistant.helpers.temperature import convert
from homeassistant.util.temperature import convert
REQUIREMENTS = ['bluepy_devices==0.2.0']

View File

@ -75,7 +75,7 @@ class HeatControl(ThermostatDevice):
self._min_temp = min_temp
self._max_temp = max_temp
self._target_temp = target_temp
self._unit = None
self._unit = hass.config.units.temperature_unit
track_state_change(hass, sensor_entity_id, self._sensor_changed)

View File

@ -7,7 +7,7 @@ https://home-assistant.io/components/thermostat.homematic/
import logging
import homeassistant.components.homematic as homematic
from homeassistant.components.thermostat import ThermostatDevice
from homeassistant.helpers.temperature import convert
from homeassistant.util.temperature import convert
from homeassistant.const import TEMP_CELSIUS, STATE_UNKNOWN
DEPENDENCIES = ['homematic']

View File

@ -7,12 +7,14 @@ from types import MappingProxyType
import voluptuous as vol
from homeassistant.const import (
CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME, CONF_TEMPERATURE_UNIT,
CONF_TIME_ZONE, CONF_CUSTOMIZE, CONF_ELEVATION, TEMP_FAHRENHEIT,
TEMP_CELSIUS, __version__)
CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME, CONF_UNIT_SYSTEM,
CONF_TIME_ZONE, CONF_CUSTOMIZE, CONF_ELEVATION, CONF_UNIT_SYSTEM_METRIC,
CONF_UNIT_SYSTEM_IMPERIAL, CONF_TEMPERATURE_UNIT, 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.unit_system import (IMPERIAL_SYSTEM, METRIC_SYSTEM)
from homeassistant.helpers.entity import valid_entity_id, set_customize
from homeassistant.util import dt as date_util, location as loc_util
@ -30,7 +32,9 @@ DEFAULT_CORE_CONFIG = (
' the sun rises and sets'),
(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_UNIT_SYSTEM, CONF_UNIT_SYSTEM_METRIC, None,
'{} for Metric, {} for Imperial'.format(CONF_UNIT_SYSTEM_METRIC,
CONF_UNIT_SYSTEM_IMPERIAL)),
(CONF_TIME_ZONE, 'UTC', 'time_zone', 'Pick yours from here: http://en.wiki'
'pedia.org/wiki/List_of_tz_database_time_zones'),
)
@ -88,7 +92,8 @@ CORE_CONFIG_SCHEMA = vol.Schema({
CONF_LATITUDE: cv.latitude,
CONF_LONGITUDE: cv.longitude,
CONF_ELEVATION: vol.Coerce(int),
CONF_TEMPERATURE_UNIT: cv.temperature_unit,
vol.Optional(CONF_TEMPERATURE_UNIT): cv.temperature_unit,
CONF_UNIT_SYSTEM: cv.unit_system,
CONF_TIME_ZONE: cv.time_zone,
vol.Required(CONF_CUSTOMIZE,
default=MappingProxyType({})): _valid_customize,
@ -131,8 +136,10 @@ def create_default_config(config_dir, detect_location=True):
location_info = detect_location and loc_util.detect_location_info()
if location_info:
if location_info.use_fahrenheit:
info[CONF_TEMPERATURE_UNIT] = 'F'
if location_info.use_metric:
info[CONF_UNIT_SYSTEM] = CONF_UNIT_SYSTEM_METRIC
else:
info[CONF_UNIT_SYSTEM] = CONF_UNIT_SYSTEM_IMPERIAL
for attr, default, prop, _ in DEFAULT_CORE_CONFIG:
if prop is None:
@ -244,18 +251,30 @@ def process_ha_core_config(hass, config):
set_customize(config.get(CONF_CUSTOMIZE) or {})
if CONF_TEMPERATURE_UNIT in config:
hac.temperature_unit = config[CONF_TEMPERATURE_UNIT]
if CONF_UNIT_SYSTEM in config:
if config[CONF_UNIT_SYSTEM] == CONF_UNIT_SYSTEM_IMPERIAL:
hac.units = IMPERIAL_SYSTEM
else:
hac.units = METRIC_SYSTEM
elif CONF_TEMPERATURE_UNIT in config:
unit = config[CONF_TEMPERATURE_UNIT]
if unit == TEMP_CELSIUS:
hac.units = METRIC_SYSTEM
else:
hac.units = IMPERIAL_SYSTEM
_LOGGER.warning("Found deprecated temperature unit in core config, "
"expected unit system. Replace 'temperature: %s' with "
"'unit_system: %s'", unit, hac.units.name)
# Shortcut if no auto-detection necessary
if None not in (hac.latitude, hac.longitude, hac.temperature_unit,
if None not in (hac.latitude, hac.longitude, hac.units,
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,
if None in (hac.latitude, hac.longitude, hac.units,
hac.time_zone):
info = loc_util.detect_location_info()
@ -264,18 +283,13 @@ def process_ha_core_config(hass, config):
return
if hac.latitude is None and hac.longitude is None:
hac.latitude = info.latitude
hac.longitude = info.longitude
hac.latitude, hac.longitude = (info.latitude, 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.units is None:
hac.units = METRIC_SYSTEM if info.use_metric else IMPERIAL_SYSTEM
discovered.append((CONF_UNIT_SYSTEM, hac.units.name))
if hac.location_name is None:
hac.location_name = info.city

View File

@ -47,6 +47,7 @@ CONF_PORT = 'port'
CONF_SCAN_INTERVAL = 'scan_interval'
CONF_STATE = 'state'
CONF_TEMPERATURE_UNIT = 'temperature_unit'
CONF_UNIT_SYSTEM = 'unit_system'
CONF_TIME_ZONE = 'time_zone'
CONF_USERNAME = 'username'
CONF_VALUE_TEMPLATE = 'value_template'
@ -112,11 +113,38 @@ ATTR_ICON = "icon"
# The unit of measurement if applicable
ATTR_UNIT_OF_MEASUREMENT = "unit_of_measurement"
CONF_UNIT_SYSTEM_METRIC = 'metric' # type: str
CONF_UNIT_SYSTEM_IMPERIAL = 'imperial' # type: str
# Temperature attribute
ATTR_TEMPERATURE = "temperature"
TEMP_CELSIUS = "°C"
TEMP_FAHRENHEIT = "°F"
# Length units
LENGTH_CENTIMETERS = "cm" # type: str
LENGTH_METERS = "m" # type: str
LENGTH_KILOMETERS = "km" # type: str
LENGTH_INCHES = "in" # type: str
LENGTH_FEET = "ft" # type: str
LENGTH_YARD = "yd" # type: str
LENGTH_MILES = "mi" # type: str
# Volume units
VOLUME_LITERS = "L" # type: str
VOLUME_MILLILITERS = "mL" # type: str
VOLUME_GALLONS = "gal" # type: str
VOLUME_FLUID_OUNCE = "fl. oz." # type: str
# Mass units
MASS_GRAMS = "g" # type: str
MASS_KILOGRAMS = "kg" # type: str
MASS_OUNCES = "oz" # type: str
MASS_POUNDS = "lb" # type: str
# Contains the information that is discovered
ATTR_DISCOVERED = "discovered"
@ -243,3 +271,10 @@ CONTENT_TYPE_TEXT_PLAIN = 'text/plain'
# The exit code to send to request a restart
RESTART_EXIT_CODE = 100
UNIT_NOT_RECOGNIZED_TEMPLATE = '{} is not a recognized {} unit.' # type: str
LENGTH = 'length' # type: str
MASS = 'mass' # type: str
VOLUME = 'volume' # type: str
TEMPERATURE = 'temperature' # type: str

View File

@ -17,7 +17,6 @@ from types import MappingProxyType
from typing import Any, Callable
import voluptuous as vol
import homeassistant.helpers.temperature as temp_helper
import homeassistant.util as util
import homeassistant.util.dt as dt_util
import homeassistant.util.location as location
@ -28,11 +27,13 @@ from homeassistant.const import (
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP,
EVENT_SERVICE_EXECUTED, EVENT_SERVICE_REGISTERED, EVENT_STATE_CHANGED,
EVENT_TIME_CHANGED, MATCH_ALL, RESTART_EXIT_CODE,
SERVICE_HOMEASSISTANT_RESTART, SERVICE_HOMEASSISTANT_STOP, TEMP_CELSIUS,
TEMP_FAHRENHEIT, __version__)
SERVICE_HOMEASSISTANT_RESTART, SERVICE_HOMEASSISTANT_STOP, __version__)
from homeassistant.exceptions import (
HomeAssistantError, InvalidEntityFormatError)
from homeassistant.helpers.entity import split_entity_id, valid_entity_id
from homeassistant.helpers.unit_system import (
METRIC_SYSTEM,
)
DOMAIN = "homeassistant"
@ -95,7 +96,7 @@ class HomeAssistant(object):
self.bus = EventBus(pool)
self.services = ServiceRegistry(self.bus, self.add_job)
self.states = StateMachine(self.bus)
self.config = Config()
self.config = Config() # type: Config
self.state = CoreState.not_running
@property
@ -715,9 +716,9 @@ class Config(object):
self.latitude = None
self.longitude = None
self.elevation = None
self.temperature_unit = None
self.location_name = None
self.time_zone = None
self.units = METRIC_SYSTEM
# If True, pip install is skipped for requirements on startup
self.skip_pip = False
@ -731,29 +732,15 @@ class Config(object):
# Directory that holds the configuration
self.config_dir = get_default_config_dir()
def distance(self, lat, lon):
"""Calculate distance from Home Assistant in meters."""
return location.distance(self.latitude, self.longitude, lat, lon)
def distance(self: object, lat: float, lon: float) -> float:
"""Calculate distance from Home Assistant."""
return self.units.length(
location.distance(self.latitude, self.longitude, lat, lon), 'm')
def path(self, *path):
"""Generate path to the file within the config dir."""
return os.path.join(self.config_dir, *path)
def temperature(self, value, unit):
"""Convert temperature to user preferred unit if set."""
if not (unit in (TEMP_CELSIUS, TEMP_FAHRENHEIT) and
self.temperature_unit and unit != self.temperature_unit):
return value, unit
try:
temp = float(value)
except ValueError: # Could not convert value to float
return value, unit
return (
round(temp_helper.convert(temp, unit, self.temperature_unit), 1),
self.temperature_unit)
def as_dict(self):
"""Create a dict representation of this dict."""
time_zone = self.time_zone or dt_util.UTC
@ -761,7 +748,7 @@ class Config(object):
return {
'latitude': self.latitude,
'longitude': self.longitude,
'temperature_unit': self.temperature_unit,
'unit_system': self.units.as_dict(),
'location_name': self.location_name,
'time_zone': time_zone.zone,
'components': self.components,

View File

@ -9,7 +9,7 @@ from homeassistant.const import (
CONF_PLATFORM, CONF_SCAN_INTERVAL, TEMP_CELSIUS, TEMP_FAHRENHEIT,
CONF_ALIAS, CONF_ENTITY_ID, CONF_VALUE_TEMPLATE, WEEKDAYS,
CONF_CONDITION, CONF_BELOW, CONF_ABOVE, SUN_EVENT_SUNSET,
SUN_EVENT_SUNRISE)
SUN_EVENT_SUNRISE, CONF_UNIT_SYSTEM_IMPERIAL, CONF_UNIT_SYSTEM_METRIC)
from homeassistant.helpers.entity import valid_entity_id
import homeassistant.util.dt as dt_util
from homeassistant.util import slugify
@ -226,6 +226,10 @@ def temperature_unit(value):
raise vol.Invalid('invalid temperature unit (expected C or F)')
unit_system = vol.All(vol.Lower, vol.Any(CONF_UNIT_SYSTEM_METRIC,
CONF_UNIT_SYSTEM_IMPERIAL))
def template(value):
"""Validate a jinja2 template."""
if value is None:

View File

@ -199,13 +199,15 @@ class Entity(object):
attr.pop(ATTR_HIDDEN)
# Convert temperature if we detect one
if attr.get(ATTR_UNIT_OF_MEASUREMENT) in (TEMP_CELSIUS,
TEMP_FAHRENHEIT):
state, attr[ATTR_UNIT_OF_MEASUREMENT] = \
self.hass.config.temperature(
state, attr[ATTR_UNIT_OF_MEASUREMENT])
state = str(state)
try:
unit_of_measure = attr.get(ATTR_UNIT_OF_MEASUREMENT)
if unit_of_measure in (TEMP_CELSIUS, TEMP_FAHRENHEIT):
state = \
str(self.hass.config.units.temperature(float(state),
unit_of_measure))
except ValueError:
# Could not convert state to float
pass
return self.hass.states.set(
self.entity_id, state, attr, self.force_update)

View File

@ -1,13 +0,0 @@
"""Methods to help handle temperature in Home Assistant."""
import homeassistant.util.temperature as temp_util
from homeassistant.const import TEMP_CELSIUS
def convert(temperature, unit, to_unit):
"""Convert temperature to correct unit."""
if unit == to_unit or unit is None or to_unit is None:
return temperature
elif unit == TEMP_CELSIUS:
return temp_util.celsius_to_fahrenheit(temperature)
return temp_util.fahrenheit_to_celsius(temperature)

View File

@ -219,7 +219,8 @@ class LocationMethods(object):
if len(locations) == 1:
return self._hass.config.distance(*locations[0])
return loc_util.distance(*locations[0] + locations[1])
return self._hass.config.units.length(
loc_util.distance(*locations[0] + locations[1]), 'm')
def _resolve_state(self, entity_id_or_state):
"""Return state or entity_id if given."""

View File

@ -0,0 +1,126 @@
"""Unit system helper class and methods."""
import logging
from numbers import Number
from homeassistant.const import (
TEMP_CELSIUS, TEMP_FAHRENHEIT, LENGTH_CENTIMETERS, LENGTH_METERS,
LENGTH_KILOMETERS, LENGTH_INCHES, LENGTH_FEET, LENGTH_YARD, LENGTH_MILES,
VOLUME_LITERS, VOLUME_MILLILITERS, VOLUME_GALLONS, VOLUME_FLUID_OUNCE,
MASS_GRAMS, MASS_KILOGRAMS, MASS_OUNCES, MASS_POUNDS,
CONF_UNIT_SYSTEM_METRIC,
CONF_UNIT_SYSTEM_IMPERIAL, LENGTH, MASS, VOLUME, TEMPERATURE,
UNIT_NOT_RECOGNIZED_TEMPLATE)
from homeassistant.util import temperature as temperature_util
from homeassistant.util import distance as distance_util
_LOGGER = logging.getLogger(__name__)
LENGTH_UNITS = [
LENGTH_MILES,
LENGTH_YARD,
LENGTH_FEET,
LENGTH_INCHES,
LENGTH_KILOMETERS,
LENGTH_METERS,
LENGTH_CENTIMETERS,
]
MASS_UNITS = [
MASS_POUNDS,
MASS_OUNCES,
MASS_KILOGRAMS,
MASS_GRAMS,
]
VOLUME_UNITS = [
VOLUME_GALLONS,
VOLUME_FLUID_OUNCE,
VOLUME_LITERS,
VOLUME_MILLILITERS,
]
TEMPERATURE_UNITS = [
TEMP_FAHRENHEIT,
TEMP_CELSIUS,
]
def is_valid_unit(unit: str, unit_type: str) -> bool:
"""Check if the unit is valid for it's type."""
if unit_type == LENGTH:
units = LENGTH_UNITS
elif unit_type == TEMPERATURE:
units = TEMPERATURE_UNITS
elif unit_type == MASS:
units = MASS_UNITS
elif unit_type == VOLUME:
units = VOLUME_UNITS
else:
return False
return unit in units
class UnitSystem(object):
"""A container for units of measure."""
# pylint: disable=too-many-arguments
def __init__(self: object, name: str, temperature: str, length: str,
volume: str, mass: str) -> None:
"""Initialize the unit system object."""
errors = \
', '.join(UNIT_NOT_RECOGNIZED_TEMPLATE.format(unit, unit_type)
for unit, unit_type in [
(temperature, TEMPERATURE),
(length, LENGTH),
(volume, VOLUME),
(mass, MASS), ]
if not is_valid_unit(unit, unit_type)) # type: str
if errors:
raise ValueError(errors)
self.name = name
self.temperature_unit = temperature
self.length_unit = length
self.mass_unit = mass
self.volume_unit = volume
@property
def is_metric(self: object) -> bool:
"""Determine if this is the metric unit system."""
return self.name == CONF_UNIT_SYSTEM_METRIC
def temperature(self: object, temperature: float, from_unit: str) -> (
float, str):
"""Convert the given temperature to this unit system."""
if not isinstance(temperature, Number):
raise TypeError(
'{} is not a numeric value.'.format(str(temperature)))
return temperature_util.convert(temperature,
from_unit, self.temperature_unit)
def length(self: object, length: float, from_unit: str) -> float:
"""Convert the given length to this unit system."""
if not isinstance(length, Number):
raise TypeError('{} is not a numeric value.'.format(str(length)))
return distance_util.convert(length, from_unit,
self.length_unit) # type: float
def as_dict(self) -> dict:
"""Convert the unit system to a dictionary."""
return {
LENGTH: self.length_unit,
MASS: self.mass_unit,
TEMPERATURE: self.temperature_unit,
VOLUME: self.volume_unit
}
METRIC_SYSTEM = UnitSystem(CONF_UNIT_SYSTEM_METRIC, TEMP_CELSIUS,
LENGTH_KILOMETERS, VOLUME_LITERS, MASS_GRAMS)
IMPERIAL_SYSTEM = UnitSystem(CONF_UNIT_SYSTEM_IMPERIAL, TEMP_FAHRENHEIT,
LENGTH_MILES, VOLUME_GALLONS, MASS_POUNDS)

View File

@ -3,82 +3,86 @@
import logging
from numbers import Number
from homeassistant.const import (
LENGTH_KILOMETERS,
LENGTH_MILES,
LENGTH_FEET,
LENGTH_METERS,
UNIT_NOT_RECOGNIZED_TEMPLATE,
LENGTH,
)
_LOGGER = logging.getLogger(__name__)
KILOMETERS_SYMBOL = 'km'
METERS_SYMBOL = 'm'
FEET_SYMBOL = 'ft'
MILES_SYMBOL = 'mi'
VALID_UNITS = [
KILOMETERS_SYMBOL,
METERS_SYMBOL,
FEET_SYMBOL,
MILES_SYMBOL,
LENGTH_KILOMETERS,
LENGTH_MILES,
LENGTH_FEET,
LENGTH_METERS,
]
def convert(value, unit_1, unit_2):
def convert(value: float, unit_1: str, unit_2: str) -> float:
"""Convert one unit of measurement to another."""
if not isinstance(value, Number):
raise TypeError(str(value) + ' is not of numeric type')
if unit_1 == unit_2:
return value
if unit_1 not in VALID_UNITS:
_LOGGER.error('Unknown unit of measure: ' + str(unit_1))
raise ValueError('Unknown unit of measure: ' + str(unit_1))
elif unit_2 not in VALID_UNITS:
_LOGGER.error('Unknown unit of measure: ' + str(unit_2))
raise ValueError('Unknown unit of measure: ' + str(unit_2))
raise ValueError(
UNIT_NOT_RECOGNIZED_TEMPLATE.format(unit_1, LENGTH))
if unit_2 not in VALID_UNITS:
raise ValueError(
UNIT_NOT_RECOGNIZED_TEMPLATE.format(unit_2, LENGTH))
if not isinstance(value, Number):
raise TypeError('{} is not of numeric type'.format(value))
if unit_1 == unit_2 or unit_1 not in VALID_UNITS:
return value
meters = value
if unit_1 == MILES_SYMBOL:
if unit_1 == LENGTH_MILES:
meters = __miles_to_meters(value)
elif unit_1 == FEET_SYMBOL:
elif unit_1 == LENGTH_FEET:
meters = __feet_to_meters(value)
elif unit_1 == KILOMETERS_SYMBOL:
elif unit_1 == LENGTH_KILOMETERS:
meters = __kilometers_to_meters(value)
result = meters
if unit_2 == MILES_SYMBOL:
if unit_2 == LENGTH_MILES:
result = __meters_to_miles(meters)
elif unit_2 == FEET_SYMBOL:
elif unit_2 == LENGTH_FEET:
result = __meters_to_feet(meters)
elif unit_2 == KILOMETERS_SYMBOL:
elif unit_2 == LENGTH_KILOMETERS:
result = __meters_to_kilometers(meters)
return result
def __miles_to_meters(miles):
def __miles_to_meters(miles: float) -> float:
"""Convert miles to meters."""
return miles * 1609.344
def __feet_to_meters(feet):
def __feet_to_meters(feet: float) -> float:
"""Convert feet to meters."""
return feet * 0.3048
def __kilometers_to_meters(kilometers):
def __kilometers_to_meters(kilometers: float) -> float:
"""Convert kilometers to meters."""
return kilometers * 1000
def __meters_to_miles(meters):
def __meters_to_miles(meters: float) -> float:
"""Convert meters to miles."""
return meters * 0.000621371
def __meters_to_feet(meters):
def __meters_to_feet(meters: float) -> float:
"""Convert meters to feet."""
return meters * 3.28084
def __meters_to_kilometers(meters):
def __meters_to_kilometers(meters: float) -> float:
"""Convert meters to kilometers."""
return meters * 0.001

View File

@ -31,7 +31,7 @@ LocationInfo = collections.namedtuple(
"LocationInfo",
['ip', 'country_code', 'country_name', 'region_code', 'region_name',
'city', 'zip_code', 'time_zone', 'latitude', 'longitude',
'use_fahrenheit'])
'use_metric'])
def detect_location_info():
@ -44,11 +44,8 @@ def detect_location_info():
if data is None:
return None
# From Wikipedia: Fahrenheit is used in the Bahamas, Belize,
# the Cayman Islands, Palau, and the United States and associated
# territories of American Samoa and the U.S. Virgin Islands
data['use_fahrenheit'] = data['country_code'] in (
'BS', 'BZ', 'KY', 'PW', 'US', 'AS', 'VI')
data['use_metric'] = data['country_code'] not in (
'US', 'MM', 'LR')
return LocationInfo(**data)

View File

@ -1,4 +1,10 @@
"""Temperature util functions."""
from homeassistant.const import (
TEMP_CELSIUS,
TEMP_FAHRENHEIT,
UNIT_NOT_RECOGNIZED_TEMPLATE,
TEMPERATURE
)
def fahrenheit_to_celsius(fahrenheit: float) -> float:
@ -9,3 +15,20 @@ def fahrenheit_to_celsius(fahrenheit: float) -> float:
def celsius_to_fahrenheit(celsius: float) -> float:
"""Convert a Celsius temperature to Fahrenheit."""
return celsius * 1.8 + 32.0
def convert(temperature: float, from_unit: str, to_unit: str) -> float:
"""Convert a temperature from one unit to another."""
if from_unit not in (TEMP_CELSIUS, TEMP_FAHRENHEIT):
raise ValueError(UNIT_NOT_RECOGNIZED_TEMPLATE.format(from_unit,
TEMPERATURE))
if to_unit not in (TEMP_CELSIUS, TEMP_FAHRENHEIT):
raise ValueError(UNIT_NOT_RECOGNIZED_TEMPLATE.format(to_unit,
TEMPERATURE))
if from_unit == to_unit:
return temperature
elif from_unit == TEMP_CELSIUS:
return celsius_to_fahrenheit(temperature)
else:
return round(fahrenheit_to_celsius(temperature), 1)

View File

@ -6,11 +6,12 @@ from unittest import mock
from homeassistant import core as ha, loader
from homeassistant.bootstrap import _setup_component
from homeassistant.helpers.entity import ToggleEntity
from homeassistant.helpers.unit_system import METRIC_SYSTEM
import homeassistant.util.dt as date_util
from homeassistant.const import (
STATE_ON, STATE_OFF, DEVICE_DEFAULT_NAME, EVENT_TIME_CHANGED,
EVENT_STATE_CHANGED, EVENT_PLATFORM_DISCOVERED, ATTR_SERVICE,
ATTR_DISCOVERED, SERVER_PORT, TEMP_CELSIUS)
ATTR_DISCOVERED, SERVER_PORT)
from homeassistant.components import sun, mqtt
_TEST_INSTANCE_PORT = SERVER_PORT
@ -37,7 +38,7 @@ def get_test_home_assistant(num_threads=None):
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
hass.config.units = METRIC_SYSTEM
if 'custom_components.test' not in loader.AVAILABLE_COMPONENTS:
loader.prepare(hass)

View File

@ -1,8 +1,8 @@
"""The tests for the demo hvac."""
import unittest
from homeassistant.const import (
TEMP_CELSIUS,
from homeassistant.helpers.unit_system import (
METRIC_SYSTEM,
)
from homeassistant.components import hvac
@ -18,7 +18,7 @@ class TestDemoHvac(unittest.TestCase):
def setUp(self): # pylint: disable=invalid-name
"""Setup things to be run when tests are started."""
self.hass = get_test_home_assistant()
self.hass.config.temperature_unit = TEMP_CELSIUS
self.hass.config.units = METRIC_SYSTEM
self.assertTrue(hvac.setup(self.hass, {'hvac': {
'platform': 'demo',
}}))

View File

@ -1,8 +1,8 @@
"""The tests for the demo thermostat."""
import unittest
from homeassistant.const import (
TEMP_CELSIUS,
from homeassistant.helpers.unit_system import (
METRIC_SYSTEM,
)
from homeassistant.components import thermostat
@ -18,7 +18,7 @@ class TestDemoThermostat(unittest.TestCase):
def setUp(self): # pylint: disable=invalid-name
"""Setup things to be run when tests are started."""
self.hass = get_test_home_assistant()
self.hass.config.temperature_unit = TEMP_CELSIUS
self.hass.config.units = METRIC_SYSTEM
self.assertTrue(thermostat.setup(self.hass, {'thermostat': {
'platform': 'demo',
}}))
@ -43,10 +43,10 @@ class TestDemoThermostat(unittest.TestCase):
def test_set_target_temp_bad_attr(self):
"""Test setting the target temperature without required attribute."""
self.assertEqual('21', self.hass.states.get(ENTITY_NEST).state)
self.assertEqual('21.0', self.hass.states.get(ENTITY_NEST).state)
thermostat.set_temperature(self.hass, None, ENTITY_NEST)
self.hass.pool.block_till_done()
self.assertEqual('21', self.hass.states.get(ENTITY_NEST).state)
self.assertEqual('21.0', self.hass.states.get(ENTITY_NEST).state)
def test_set_target_temp(self):
"""Test the setting of the target temperature."""

View File

@ -10,6 +10,7 @@ from homeassistant.const import (
STATE_OFF,
TEMP_CELSIUS,
)
from homeassistant.helpers.unit_system import METRIC_SYSTEM
from homeassistant.components import thermostat
from tests.common import get_test_home_assistant
@ -75,7 +76,7 @@ class TestThermostatHeatControl(unittest.TestCase):
def setUp(self): # pylint: disable=invalid-name
"""Setup things to be run when tests are started."""
self.hass = get_test_home_assistant()
self.hass.config.temperature_unit = TEMP_CELSIUS
self.hass.config.units = METRIC_SYSTEM
thermostat.setup(self.hass, {'thermostat': {
'platform': 'heat_control',
'name': 'test',

View File

@ -6,6 +6,13 @@ from unittest.mock import patch
from homeassistant.components import group
from homeassistant.exceptions import TemplateError
from homeassistant.helpers import template
from homeassistant.helpers.unit_system import UnitSystem
from homeassistant.const import (
LENGTH_METERS,
TEMP_CELSIUS,
MASS_GRAMS,
VOLUME_LITERS,
)
import homeassistant.util.dt as dt_util
from tests.common import get_test_home_assistant
@ -17,6 +24,9 @@ class TestUtilTemplate(unittest.TestCase):
def setUp(self): # pylint: disable=invalid-name
"""Setup the tests."""
self.hass = get_test_home_assistant()
self.hass.config.units = UnitSystem('custom', TEMP_CELSIUS,
LENGTH_METERS, VOLUME_LITERS,
MASS_GRAMS)
def tearDown(self): # pylint: disable=invalid-name
"""Stop down stuff we started."""

View File

@ -0,0 +1,132 @@
"""Test the unit system helper."""
import unittest
from homeassistant.helpers.unit_system import (
UnitSystem,
METRIC_SYSTEM,
IMPERIAL_SYSTEM,
)
from homeassistant.const import (
LENGTH_METERS,
LENGTH_KILOMETERS,
MASS_GRAMS,
VOLUME_LITERS,
TEMP_CELSIUS,
LENGTH,
MASS,
TEMPERATURE,
VOLUME
)
SYSTEM_NAME = 'TEST'
INVALID_UNIT = 'INVALID'
class TestUnitSystem(unittest.TestCase):
"""Test the unit system helper."""
def test_invalid_units(self):
"""Test errors are raised when invalid units are passed in."""
with self.assertRaises(ValueError):
UnitSystem(SYSTEM_NAME, INVALID_UNIT, LENGTH_METERS, VOLUME_LITERS,
MASS_GRAMS)
with self.assertRaises(ValueError):
UnitSystem(SYSTEM_NAME, TEMP_CELSIUS, INVALID_UNIT, VOLUME_LITERS,
MASS_GRAMS)
with self.assertRaises(ValueError):
UnitSystem(SYSTEM_NAME, TEMP_CELSIUS, LENGTH_METERS, INVALID_UNIT,
MASS_GRAMS)
with self.assertRaises(ValueError):
UnitSystem(SYSTEM_NAME, TEMP_CELSIUS, LENGTH_METERS, VOLUME_LITERS,
INVALID_UNIT)
def test_invalid_value(self):
"""Test no conversion happens if value is non-numeric."""
with self.assertRaises(TypeError):
METRIC_SYSTEM.length('25a', LENGTH_KILOMETERS)
with self.assertRaises(TypeError):
METRIC_SYSTEM.temperature('50K', TEMP_CELSIUS)
def test_as_dict(self):
"""Test that the as_dict() method returns the expected dictionary."""
expected = {
LENGTH: LENGTH_KILOMETERS,
TEMPERATURE: TEMP_CELSIUS,
VOLUME: VOLUME_LITERS,
MASS: MASS_GRAMS
}
self.assertEqual(expected, METRIC_SYSTEM.as_dict())
def test_temperature_same_unit(self):
"""Test no conversion happens if to unit is same as from unit."""
self.assertEqual(
5,
METRIC_SYSTEM.temperature(5,
METRIC_SYSTEM.temperature_unit))
def test_temperature_unknown_unit(self):
"""Test no conversion happens if unknown unit."""
with self.assertRaises(ValueError):
METRIC_SYSTEM.temperature(5, 'K')
def test_temperature_to_metric(self):
"""Test temperature conversion to metric system."""
self.assertEqual(
25,
METRIC_SYSTEM.temperature(25, METRIC_SYSTEM.temperature_unit))
self.assertEqual(
26.7,
METRIC_SYSTEM.temperature(80, IMPERIAL_SYSTEM.temperature_unit))
def test_temperature_to_imperial(self):
"""Test temperature conversion to imperial system."""
self.assertEqual(
77,
IMPERIAL_SYSTEM.temperature(77, IMPERIAL_SYSTEM.temperature_unit))
self.assertEqual(
77,
IMPERIAL_SYSTEM.temperature(25, METRIC_SYSTEM.temperature_unit))
def test_length_unknown_unit(self):
"""Test length conversion with unknown from unit."""
with self.assertRaises(ValueError):
METRIC_SYSTEM.length(5, 'fr')
def test_length_to_metric(self):
"""Test length conversion to metric system."""
self.assertEqual(
100,
METRIC_SYSTEM.length(100, METRIC_SYSTEM.length_unit)
)
self.assertEqual(
8.04672,
METRIC_SYSTEM.length(5, IMPERIAL_SYSTEM.length_unit)
)
def test_length_to_imperial(self):
"""Test length conversion to imperial system."""
self.assertEqual(
100,
IMPERIAL_SYSTEM.length(100,
IMPERIAL_SYSTEM.length_unit)
)
self.assertEqual(
3.106855,
IMPERIAL_SYSTEM.length(5, METRIC_SYSTEM.length_unit)
)
def test_properties(self):
"""Test the unit properties are returned as expected."""
self.assertEqual(LENGTH_KILOMETERS, METRIC_SYSTEM.length_unit)
self.assertEqual(TEMP_CELSIUS, METRIC_SYSTEM.temperature_unit)
self.assertEqual(MASS_GRAMS, METRIC_SYSTEM.mass_unit)
self.assertEqual(VOLUME_LITERS, METRIC_SYSTEM.volume_unit)
def test_is_metric(self):
"""Test the is metric flag."""
self.assertTrue(METRIC_SYSTEM.is_metric)
self.assertFalse(IMPERIAL_SYSTEM.is_metric)

View File

@ -11,9 +11,9 @@ from voluptuous import MultipleInvalid
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_LATITUDE, CONF_LONGITUDE, CONF_UNIT_SYSTEM, CONF_NAME,
CONF_TIME_ZONE, CONF_ELEVATION, CONF_CUSTOMIZE, __version__,
TEMP_FAHRENHEIT)
CONF_UNIT_SYSTEM_METRIC, CONF_UNIT_SYSTEM_IMPERIAL, CONF_TEMPERATURE_UNIT)
from homeassistant.util import location as location_util, dt as dt_util
from homeassistant.helpers.entity import Entity
@ -145,7 +145,7 @@ class TestConfig(unittest.TestCase):
CONF_LATITUDE: 32.8594,
CONF_LONGITUDE: -117.2073,
CONF_ELEVATION: 101,
CONF_TEMPERATURE_UNIT: 'F',
CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_METRIC,
CONF_NAME: 'Home',
CONF_TIME_ZONE: 'America/Los_Angeles'
}
@ -167,7 +167,7 @@ class TestConfig(unittest.TestCase):
def test_core_config_schema(self):
for value in (
{'temperature_unit': 'K'},
{CONF_UNIT_SYSTEM: 'K'},
{'time_zone': 'non-exist'},
{'latitude': '91'},
{'longitude': -181},
@ -182,7 +182,7 @@ class TestConfig(unittest.TestCase):
'name': 'Test name',
'latitude': '-23.45',
'longitude': '123.45',
'temperature_unit': 'c',
CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_METRIC,
'customize': {
'sensor.temperature': {
'hidden': True,
@ -264,7 +264,7 @@ class TestConfig(unittest.TestCase):
'longitude': 50,
'elevation': 25,
'name': 'Huis',
'temperature_unit': 'F',
CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_IMPERIAL,
'time_zone': 'America/New_York',
})
@ -272,7 +272,28 @@ class TestConfig(unittest.TestCase):
assert config.longitude == 50
assert config.elevation == 25
assert config.location_name == 'Huis'
assert config.temperature_unit == TEMP_FAHRENHEIT
assert config.units.name == CONF_UNIT_SYSTEM_IMPERIAL
assert config.time_zone.zone == 'America/New_York'
def test_loading_configuration_temperature_unit(self):
"""Test backward compatibility when loading core config."""
config = Config()
hass = mock.Mock(config=config)
config_util.process_ha_core_config(hass, {
'latitude': 60,
'longitude': 50,
'elevation': 25,
'name': 'Huis',
CONF_TEMPERATURE_UNIT: 'C',
'time_zone': 'America/New_York',
})
assert config.latitude == 60
assert config.longitude == 50
assert config.elevation == 25
assert config.location_name == 'Huis'
assert config.units.name == CONF_UNIT_SYSTEM_METRIC
assert config.time_zone.zone == 'America/New_York'
@mock.patch('homeassistant.util.location.detect_location_info',
@ -292,7 +313,8 @@ class TestConfig(unittest.TestCase):
assert config.longitude == -117.2073
assert config.elevation == 101
assert config.location_name == 'San Diego'
assert config.temperature_unit == TEMP_FAHRENHEIT
assert config.units.name == CONF_UNIT_SYSTEM_METRIC
assert config.units.is_metric
assert config.time_zone.zone == 'America/Los_Angeles'
@mock.patch('homeassistant.util.location.detect_location_info',
@ -311,5 +333,5 @@ class TestConfig(unittest.TestCase):
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.units == blankConfig.units
assert config.time_zone == blankConfig.time_zone

View File

@ -15,10 +15,10 @@ import homeassistant.core as ha
from homeassistant.exceptions import (
HomeAssistantError, InvalidEntityFormatError)
import homeassistant.util.dt as dt_util
from homeassistant.helpers.unit_system import (METRIC_SYSTEM)
from homeassistant.const import (
__version__, EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP,
EVENT_STATE_CHANGED, ATTR_FRIENDLY_NAME, TEMP_CELSIUS,
TEMP_FAHRENHEIT)
EVENT_STATE_CHANGED, ATTR_FRIENDLY_NAME, CONF_UNIT_SYSTEM)
from tests.common import get_test_home_assistant
@ -465,56 +465,12 @@ class TestConfig(unittest.TestCase):
os.path.join(data_dir, ".homeassistant", "dir", "test.conf"),
self.config.path("dir", "test.conf"))
def test_temperature_not_convert_if_no_preference(self):
"""No unit conversion to happen if no preference."""
self.assertEqual(
(25, TEMP_CELSIUS),
self.config.temperature(25, TEMP_CELSIUS))
self.assertEqual(
(80, TEMP_FAHRENHEIT),
self.config.temperature(80, TEMP_FAHRENHEIT))
def test_temperature_not_convert_if_invalid_value(self):
"""No unit conversion to happen if no preference."""
self.config.temperature_unit = TEMP_FAHRENHEIT
self.assertEqual(
('25a', TEMP_CELSIUS),
self.config.temperature('25a', TEMP_CELSIUS))
def test_temperature_not_convert_if_invalid_unit(self):
"""No unit conversion to happen if no preference."""
self.assertEqual(
(25, 'Invalid unit'),
self.config.temperature(25, 'Invalid unit'))
def test_temperature_to_convert_to_celsius(self):
"""Test temperature conversion to celsius."""
self.config.temperature_unit = TEMP_CELSIUS
self.assertEqual(
(25, TEMP_CELSIUS),
self.config.temperature(25, TEMP_CELSIUS))
self.assertEqual(
(26.7, TEMP_CELSIUS),
self.config.temperature(80, TEMP_FAHRENHEIT))
def test_temperature_to_convert_to_fahrenheit(self):
"""Test temperature conversion to fahrenheit."""
self.config.temperature_unit = TEMP_FAHRENHEIT
self.assertEqual(
(77, TEMP_FAHRENHEIT),
self.config.temperature(25, TEMP_CELSIUS))
self.assertEqual(
(80, TEMP_FAHRENHEIT),
self.config.temperature(80, TEMP_FAHRENHEIT))
def test_as_dict(self):
"""Test as dict."""
expected = {
'latitude': None,
'longitude': None,
'temperature_unit': None,
CONF_UNIT_SYSTEM: METRIC_SYSTEM.as_dict(),
'location_name': None,
'time_zone': 'UTC',
'components': [],

View File

@ -2,14 +2,11 @@
import unittest
import homeassistant.util.distance as distance_util
KILOMETERS = distance_util.KILOMETERS_SYMBOL
METERS = distance_util.METERS_SYMBOL
FEET = distance_util.FEET_SYMBOL
MILES = distance_util.MILES_SYMBOL
from homeassistant.const import (LENGTH_KILOMETERS, LENGTH_METERS, LENGTH_FEET,
LENGTH_MILES)
INVALID_SYMBOL = 'bob'
VALID_SYMBOL = KILOMETERS
VALID_SYMBOL = LENGTH_KILOMETERS
class TestDistanceUtil(unittest.TestCase):
@ -17,52 +14,78 @@ class TestDistanceUtil(unittest.TestCase):
def test_convert_same_unit(self):
"""Test conversion from any unit to same unit."""
self.assertEqual(5, distance_util.convert(5, KILOMETERS, KILOMETERS))
self.assertEqual(2, distance_util.convert(2, METERS, METERS))
self.assertEqual(10, distance_util.convert(10, MILES, MILES))
self.assertEqual(9, distance_util.convert(9, FEET, FEET))
self.assertEqual(5,
distance_util.convert(5, LENGTH_KILOMETERS,
LENGTH_KILOMETERS))
self.assertEqual(2,
distance_util.convert(2, LENGTH_METERS,
LENGTH_METERS))
self.assertEqual(10,
distance_util.convert(10, LENGTH_MILES, LENGTH_MILES))
self.assertEqual(9,
distance_util.convert(9, LENGTH_FEET, LENGTH_FEET))
def test_convert_invalid_unit(self):
"""Test exception is thrown for invalid units."""
with self.assertRaises(ValueError):
distance_util.convert(5, INVALID_SYMBOL, VALID_SYMBOL)
distance_util.convert(5, INVALID_SYMBOL,
VALID_SYMBOL)
with self.assertRaises(ValueError):
distance_util.convert(5, VALID_SYMBOL, INVALID_SYMBOL)
distance_util.convert(5, VALID_SYMBOL,
INVALID_SYMBOL)
def test_convert_nonnumeric_value(self):
"""Test exception is thrown for nonnumeric type."""
with self.assertRaises(TypeError):
distance_util.convert('a', KILOMETERS, METERS)
distance_util.convert('a', LENGTH_KILOMETERS, LENGTH_METERS)
def test_convert_from_miles(self):
"""Test conversion from miles to other units."""
miles = 5
self.assertEqual(distance_util.convert(miles, MILES, KILOMETERS),
8.04672)
self.assertEqual(distance_util.convert(miles, MILES, METERS), 8046.72)
self.assertEqual(distance_util.convert(miles, MILES, FEET),
26400.0008448)
self.assertEqual(
distance_util.convert(miles, LENGTH_MILES, LENGTH_KILOMETERS),
8.04672)
self.assertEqual(
distance_util.convert(miles, LENGTH_MILES, LENGTH_METERS),
8046.72)
self.assertEqual(
distance_util.convert(miles, LENGTH_MILES, LENGTH_FEET),
26400.0008448)
def test_convert_from_feet(self):
"""Test conversion from feet to other units."""
feet = 5000
self.assertEqual(distance_util.convert(feet, FEET, KILOMETERS), 1.524)
self.assertEqual(distance_util.convert(feet, FEET, METERS), 1524)
self.assertEqual(distance_util.convert(feet, FEET, MILES),
0.9469694040000001)
self.assertEqual(
distance_util.convert(feet, LENGTH_FEET, LENGTH_KILOMETERS),
1.524)
self.assertEqual(
distance_util.convert(feet, LENGTH_FEET, LENGTH_METERS),
1524)
self.assertEqual(
distance_util.convert(feet, LENGTH_FEET, LENGTH_MILES),
0.9469694040000001)
def test_convert_from_kilometers(self):
"""Test conversion from kilometers to other units."""
km = 5
self.assertEqual(distance_util.convert(km, KILOMETERS, FEET), 16404.2)
self.assertEqual(distance_util.convert(km, KILOMETERS, METERS), 5000)
self.assertEqual(distance_util.convert(km, KILOMETERS, MILES),
3.106855)
self.assertEqual(
distance_util.convert(km, LENGTH_KILOMETERS, LENGTH_FEET),
16404.2)
self.assertEqual(
distance_util.convert(km, LENGTH_KILOMETERS, LENGTH_METERS),
5000)
self.assertEqual(
distance_util.convert(km, LENGTH_KILOMETERS, LENGTH_MILES),
3.106855)
def test_convert_from_meters(self):
"""Test conversion from meters to other units."""
m = 5000
self.assertEqual(distance_util.convert(m, METERS, FEET), 16404.2)
self.assertEqual(distance_util.convert(m, METERS, KILOMETERS), 5)
self.assertEqual(distance_util.convert(m, METERS, MILES), 3.106855)
self.assertEqual(distance_util.convert(m, LENGTH_METERS, LENGTH_FEET),
16404.2)
self.assertEqual(
distance_util.convert(m, LENGTH_METERS, LENGTH_KILOMETERS),
5)
self.assertEqual(distance_util.convert(m, LENGTH_METERS, LENGTH_MILES),
3.106855)

View File

@ -79,7 +79,7 @@ class TestLocationUtil(TestCase):
assert info.time_zone == 'America/Los_Angeles'
assert info.latitude == 32.8594
assert info.longitude == -117.2073
assert info.use_fahrenheit
assert not info.use_metric
@requests_mock.Mocker()
@patch('homeassistant.util.location._get_freegeoip', return_value=None)
@ -101,7 +101,7 @@ class TestLocationUtil(TestCase):
assert info.time_zone == 'America/Los_Angeles'
assert info.latitude == 32.8594
assert info.longitude == -117.2073
assert info.use_fahrenheit
assert not info.use_metric
@patch('homeassistant.util.location.elevation', return_value=0)
@patch('homeassistant.util.location._get_freegeoip', return_value=None)