Merge pull request #67 from balloob/ha-config

Add a global config object to Home Assistant
This commit is contained in:
Paulus Schoutsen 2015-03-21 00:53:30 -07:00
commit c8401a3c4d
27 changed files with 234 additions and 180 deletions

View File

@ -1,8 +1,20 @@
homeassistant: homeassistant:
# Omitted values in this section will be auto detected using freegeoip.net
# Location required to calculate the time the sun rises and sets # Location required to calculate the time the sun rises and sets
latitude: 32.87336 latitude: 32.87336
longitude: 117.22743 longitude: 117.22743
# C for Celcius, F for Fahrenheit
temperature_unit: C
# Pick yours from here:
# http://en.wikipedia.org/wiki/List_of_tz_database_time_zones
time_zone: America/Los_Angeles
# Name of the location where Home Assistant is running
name: Home
http: http:
api_password: mypass api_password: mypass
# Set to 1 to enable development mode # Set to 1 to enable development mode

View File

@ -15,11 +15,14 @@ import re
import datetime as dt import datetime as dt
import functools as ft import functools as ft
import requests
from homeassistant.const import ( from homeassistant.const import (
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP,
SERVICE_HOMEASSISTANT_STOP, EVENT_TIME_CHANGED, EVENT_STATE_CHANGED, SERVICE_HOMEASSISTANT_STOP, EVENT_TIME_CHANGED, EVENT_STATE_CHANGED,
EVENT_CALL_SERVICE, ATTR_NOW, ATTR_DOMAIN, ATTR_SERVICE, MATCH_ALL, EVENT_CALL_SERVICE, ATTR_NOW, ATTR_DOMAIN, ATTR_SERVICE, MATCH_ALL,
EVENT_SERVICE_EXECUTED, ATTR_SERVICE_CALL_ID, EVENT_SERVICE_REGISTERED) EVENT_SERVICE_EXECUTED, ATTR_SERVICE_CALL_ID, EVENT_SERVICE_REGISTERED,
TEMP_CELCIUS, TEMP_FAHRENHEIT)
import homeassistant.util as util import homeassistant.util as util
DOMAIN = "homeassistant" DOMAIN = "homeassistant"
@ -49,6 +52,7 @@ class HomeAssistant(object):
self.bus = EventBus(pool) self.bus = EventBus(pool)
self.services = ServiceRegistry(self.bus, pool) self.services = ServiceRegistry(self.bus, pool)
self.states = StateMachine(self.bus) self.states = StateMachine(self.bus)
self.config = Config()
# List of loaded components # List of loaded components
self.components = [] self.components = []
@ -56,12 +60,18 @@ class HomeAssistant(object):
# Remote.API object pointing at local API # Remote.API object pointing at local API
self.local_api = None self.local_api = None
# Directory that holds the configuration @property
self.config_dir = os.path.join(os.getcwd(), 'config') def config_dir(self):
""" DEPRECATED 3/18/2015. Use hass.config.config_dir """
_LOGGER.warning(
'hass.config_dir is deprecated. Use hass.config.config_dir')
return self.config.config_dir
def get_config_path(self, path): def get_config_path(self, path):
""" Returns path to the file within the config dir. """ """ DEPRECATED 3/18/2015. Use hass.config.path """
return os.path.join(self.config_dir, path) _LOGGER.warning(
'hass.get_config_path is deprecated. Use hass.config.path')
return self.config.path(path)
def start(self): def start(self):
""" Start home assistant. """ """ Start home assistant. """
@ -836,6 +846,75 @@ class Timer(threading.Thread):
self.hass.bus.fire(EVENT_TIME_CHANGED, {ATTR_NOW: now}) self.hass.bus.fire(EVENT_TIME_CHANGED, {ATTR_NOW: now})
class Config(object):
""" Configuration settings for Home Assistant. """
def __init__(self):
self.latitude = None
self.longitude = None
self.temperature_unit = None
self.location_name = None
self.time_zone = None
# Directory that holds the configuration
self.config_dir = os.path.join(os.getcwd(), 'config')
def auto_detect(self):
""" Will attempt to detect config of Home Assistant. """
# Only detect if location or temp unit missing
if None not in (self.latitude, self.longitude, self.temperature_unit):
return
_LOGGER.info('Auto detecting location and temperature unit')
try:
info = requests.get('https://freegeoip.net/json/').json()
except requests.RequestException:
return
if self.latitude is None and self.longitude is None:
self.latitude = info['latitude']
self.longitude = info['longitude']
if self.temperature_unit is 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
if info['country_code'] in ('BS', 'BZ', 'KY', 'PW',
'US', 'AS', 'VI'):
self.temperature_unit = TEMP_FAHRENHEIT
else:
self.temperature_unit = TEMP_CELCIUS
if self.location_name is None:
self.location_name = info['city']
if self.time_zone is None:
self.time_zone = info['time_zone']
def path(self, path):
""" Returns path to the file within the config dir. """
return os.path.join(self.config_dir, path)
def temperature(self, value, unit):
""" Converts temperature to user preferred unit if set. """
if not (unit and self.temperature_unit and
unit != self.temperature_unit):
return value, unit
try:
if unit == TEMP_CELCIUS:
# Convert C to F
return round(float(value) * 1.8 + 32.0, 1), TEMP_FAHRENHEIT
# Convert F to C
return round((float(value)-32.0)/1.8, 1), TEMP_CELCIUS
except ValueError:
# Could not convert value to float
return value, unit
class HomeAssistantError(Exception): class HomeAssistantError(Exception):
""" General Home Assistant exception occured. """ """ General Home Assistant exception occured. """
pass pass

View File

@ -20,7 +20,10 @@ import homeassistant
import homeassistant.loader as loader import homeassistant.loader as loader
import homeassistant.components as core_components import homeassistant.components as core_components
import homeassistant.components.group as group import homeassistant.components.group as group
from homeassistant.const import EVENT_COMPONENT_LOADED from homeassistant.const import (
EVENT_COMPONENT_LOADED, CONF_LATITUDE, CONF_LONGITUDE,
CONF_TEMPERATURE_UNIT, CONF_NAME, CONF_TIME_ZONE, TEMP_CELCIUS,
TEMP_FAHRENHEIT)
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -73,6 +76,8 @@ def from_config_dict(config, hass=None):
if hass is None: if hass is None:
hass = homeassistant.HomeAssistant() hass = homeassistant.HomeAssistant()
process_ha_core_config(hass, config.get(homeassistant.DOMAIN, {}))
enable_logging(hass) enable_logging(hass)
_ensure_loader_prepared(hass) _ensure_loader_prepared(hass)
@ -111,8 +116,8 @@ def from_config_file(config_path, hass=None):
if hass is None: if hass is None:
hass = homeassistant.HomeAssistant() hass = homeassistant.HomeAssistant()
# Set config dir to directory holding config file # Set config dir to directory holding config file
hass.config_dir = os.path.abspath(os.path.dirname(config_path)) hass.config.config_dir = os.path.abspath(os.path.dirname(config_path))
config_dict = {} config_dict = {}
# check config file type # check config file type
@ -143,13 +148,13 @@ def enable_logging(hass):
logging.basicConfig(level=logging.INFO) logging.basicConfig(level=logging.INFO)
# Log errors to a file if we have write access to file or config dir # Log errors to a file if we have write access to file or config dir
err_log_path = hass.get_config_path("home-assistant.log") err_log_path = hass.config.path("home-assistant.log")
err_path_exists = os.path.isfile(err_log_path) err_path_exists = os.path.isfile(err_log_path)
# Check if we can write to the error log if it exists or that # Check if we can write to the error log if it exists or that
# we can create files in the containing directory if not. # we can create files in the containing directory if not.
if (err_path_exists and os.access(err_log_path, os.W_OK)) or \ if (err_path_exists and os.access(err_log_path, os.W_OK)) or \
(not err_path_exists and os.access(hass.config_dir, os.W_OK)): (not err_path_exists and os.access(hass.config.config_dir, os.W_OK)):
err_handler = logging.FileHandler( err_handler = logging.FileHandler(
err_log_path, mode='w', delay=True) err_log_path, mode='w', delay=True)
@ -165,6 +170,26 @@ def enable_logging(hass):
"Unable to setup error log %s (access denied)", err_log_path) "Unable to setup error log %s (access denied)", err_log_path)
def process_ha_core_config(hass, config):
""" Processes the [homeassistant] section from the config. """
for key, attr in ((CONF_LATITUDE, 'latitude'),
(CONF_LONGITUDE, 'longitude'),
(CONF_NAME, 'location_name'),
(CONF_TIME_ZONE, 'time_zone')):
if key in config:
setattr(hass.config, attr, config[key])
if CONF_TEMPERATURE_UNIT in config:
unit = config[CONF_TEMPERATURE_UNIT]
if unit == 'C':
hass.config.temperature_unit = TEMP_CELCIUS
elif unit == 'F':
hass.config.temperature_unit = TEMP_FAHRENHEIT
hass.config.auto_detect()
def _ensure_loader_prepared(hass): def _ensure_loader_prepared(hass):
""" Ensure Home Assistant loader is prepared. """ """ Ensure Home Assistant loader is prepared. """
if not loader.PREPARED: if not loader.PREPARED:

View File

@ -10,8 +10,7 @@ import homeassistant as ha
import homeassistant.bootstrap as bootstrap import homeassistant.bootstrap as bootstrap
import homeassistant.loader as loader import homeassistant.loader as loader
from homeassistant.const import ( from homeassistant.const import (
CONF_PLATFORM, ATTR_ENTITY_PICTURE, ATTR_ENTITY_ID, CONF_PLATFORM, ATTR_ENTITY_PICTURE, ATTR_ENTITY_ID)
CONF_LATITUDE, CONF_LONGITUDE)
DOMAIN = "demo" DOMAIN = "demo"
@ -33,8 +32,6 @@ def setup(hass, config):
hass.states.set('a.Demo_Mode', 'Enabled') hass.states.set('a.Demo_Mode', 'Enabled')
# Setup sun # Setup sun
config[ha.DOMAIN].setdefault(CONF_LATITUDE, '32.87336')
config[ha.DOMAIN].setdefault(CONF_LONGITUDE, '-117.22743')
loader.get_component('sun').setup(hass, config) loader.get_component('sun').setup(hass, config)
# Setup demo platforms # Setup demo platforms

View File

@ -179,7 +179,7 @@ class DeviceTracker(object):
# Write new devices to known devices file # Write new devices to known devices file
if not self.invalid_known_devices_file: if not self.invalid_known_devices_file:
known_dev_path = self.hass.get_config_path(KNOWN_DEVICES_FILE) known_dev_path = self.hass.config.path(KNOWN_DEVICES_FILE)
try: try:
# If file does not exist we will write the header too # If file does not exist we will write the header too
@ -214,7 +214,7 @@ class DeviceTracker(object):
# pylint: disable=too-many-branches # pylint: disable=too-many-branches
def _read_known_devices_file(self): def _read_known_devices_file(self):
""" Parse and process the known devices file. """ """ Parse and process the known devices file. """
known_dev_path = self.hass.get_config_path(KNOWN_DEVICES_FILE) known_dev_path = self.hass.config.path(KNOWN_DEVICES_FILE)
# Return if no known devices file exists # Return if no known devices file exists
if not os.path.isfile(known_dev_path): if not os.path.isfile(known_dev_path):

View File

@ -148,7 +148,7 @@ def setup(hass, config):
# Load built-in profiles and custom profiles # Load built-in profiles and custom profiles
profile_paths = [os.path.join(os.path.dirname(__file__), profile_paths = [os.path.join(os.path.dirname(__file__),
LIGHT_PROFILES_FILE), LIGHT_PROFILES_FILE),
hass.get_config_path(LIGHT_PROFILES_FILE)] hass.config.path(LIGHT_PROFILES_FILE)]
profiles = {} profiles = {}
for profile_path in profile_paths: for profile_path in profile_paths:

View File

@ -51,7 +51,8 @@ def setup_bridge(host, hass, add_devices_callback):
try: try:
bridge = phue.Bridge( bridge = phue.Bridge(
host, config_file_path=hass.get_config_path(PHUE_CONFIG_FILE)) host,
config_file_path=hass.config.path(PHUE_CONFIG_FILE))
except ConnectionRefusedError: # Wrong host was given except ConnectionRefusedError: # Wrong host was given
_LOGGER.exception("Error connecting to the Hue bridge at %s", host) _LOGGER.exception("Error connecting to the Hue bridge at %s", host)

View File

@ -262,7 +262,7 @@ class Recorder(threading.Thread):
def _setup_connection(self): def _setup_connection(self):
""" Ensure database is ready to fly. """ """ Ensure database is ready to fly. """
db_path = self.hass.get_config_path(DB_FILE) db_path = self.hass.config.path(DB_FILE)
self.conn = sqlite3.connect(db_path, check_same_thread=False) self.conn = sqlite3.connect(db_path, check_same_thread=False)
self.conn.row_factory = sqlite3.Row self.conn.row_factory = sqlite3.Row

View File

@ -74,7 +74,7 @@ def setup(hass, config):
schedule.schedule(hass) schedule.schedule(hass)
return True return True
with open(hass.get_config_path(_SCHEDULE_FILE)) as schedule_file: with open(hass.config.path(_SCHEDULE_FILE)) as schedule_file:
schedule_descriptions = json.load(schedule_file) schedule_descriptions = json.load(schedule_file)
for schedule_description in schedule_descriptions: for schedule_description in schedule_descriptions:

View File

@ -1,25 +1,25 @@
""" Support for Wink sensors. """ """ Support for Wink sensors. """
from homeassistant.helpers.device import Device from homeassistant.helpers.device import Device
from homeassistant.const import ( from homeassistant.const import TEMP_CELCIUS, ATTR_BATTERY_LEVEL
TEMP_CELCIUS, ATTR_UNIT_OF_MEASUREMENT, ATTR_FRIENDLY_NAME)
# pylint: disable=unused-argument # pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None): def setup_platform(hass, config, add_devices, discovery_info=None):
""" Sets up the Demo sensors. """ """ Sets up the Demo sensors. """
add_devices([ add_devices([
DemoSensor('Outside Temperature', 15.6, TEMP_CELCIUS), DemoSensor('Outside Temperature', 15.6, TEMP_CELCIUS, 12),
DemoSensor('Outside Humidity', 54, '%'), DemoSensor('Outside Humidity', 54, '%', None),
]) ])
class DemoSensor(Device): class DemoSensor(Device):
""" A Demo sensor. """ """ A Demo sensor. """
def __init__(self, name, state, unit_of_measurement): def __init__(self, name, state, unit_of_measurement, battery):
self._name = name self._name = name
self._state = state self._state = state
self._unit_of_measurement = unit_of_measurement self._unit_of_measurement = unit_of_measurement
self._battery = battery
@property @property
def should_poll(self): def should_poll(self):
@ -36,10 +36,15 @@ class DemoSensor(Device):
""" Returns the state of the device. """ """ Returns the state of the device. """
return self._state return self._state
@property
def unit_of_measurement(self):
""" Unit this state is expressed in. """
return self._unit_of_measurement
@property @property
def state_attributes(self): def state_attributes(self):
""" Returns the state attributes. """ """ Returns the state attributes. """
return { if self._battery:
ATTR_FRIENDLY_NAME: self._name, return {
ATTR_UNIT_OF_MEASUREMENT: self._unit_of_measurement, ATTR_BATTERY_LEVEL: self._battery,
} }

View File

@ -7,8 +7,7 @@ Shows system monitor values such as: disk, memory and processor use
""" """
from homeassistant.helpers.device import Device from homeassistant.helpers.device import Device
from homeassistant.const import ( from homeassistant.const import STATE_ON, STATE_OFF
ATTR_UNIT_OF_MEASUREMENT, ATTR_FRIENDLY_NAME, STATE_ON, STATE_OFF)
import psutil import psutil
import logging import logging
@ -63,14 +62,6 @@ class SystemMonitorSensor(Device):
""" Returns the state of the device. """ """ Returns the state of the device. """
return self._state return self._state
@property
def state_attributes(self):
""" Returns the state attributes. """
return {
ATTR_FRIENDLY_NAME: self.name,
ATTR_UNIT_OF_MEASUREMENT: self.unit_of_measurement,
}
def update(self): def update(self):
if self.type == 'disk_use_percent': if self.type == 'disk_use_percent':
self._state = psutil.disk_usage(self.argument).percent self._state = psutil.disk_usage(self.argument).percent

View File

@ -29,8 +29,7 @@ from collections import namedtuple
import tellcore.telldus as telldus import tellcore.telldus as telldus
import tellcore.constants as tellcore_constants import tellcore.constants as tellcore_constants
from homeassistant.const import ( from homeassistant.const import TEMP_CELCIUS
ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT, TEMP_CELCIUS)
from homeassistant.helpers.device import Device from homeassistant.helpers.device import Device
import homeassistant.util as util import homeassistant.util as util
@ -100,7 +99,7 @@ class TellstickSensor(Device):
def __init__(self, name, sensor, datatype, sensor_info): def __init__(self, name, sensor, datatype, sensor_info):
self.datatype = datatype self.datatype = datatype
self.sensor = sensor self.sensor = sensor
self.unit = sensor_info.unit or None self.unit_of_measurement = sensor_info.unit or None
self._name = "{} {}".format(name, sensor_info.name) self._name = "{} {}".format(name, sensor_info.name)
@ -113,15 +112,3 @@ class TellstickSensor(Device):
def state(self): def state(self):
""" Returns the state of the device. """ """ Returns the state of the device. """
return self.sensor.value(self.datatype).value return self.sensor.value(self.datatype).value
@property
def state_attributes(self):
""" Returns the state attributes. """
attrs = {
ATTR_FRIENDLY_NAME: self._name,
}
if self.unit:
attrs[ATTR_UNIT_OF_MEASUREMENT] = self.unit
return attrs

View File

@ -4,8 +4,8 @@ import logging
# pylint: disable=no-name-in-module, import-error # pylint: disable=no-name-in-module, import-error
import homeassistant.external.wink.pywink as pywink import homeassistant.external.wink.pywink as pywink
from homeassistant.components.wink import WinkSensorDevice from homeassistant.helpers.device import Device
from homeassistant.const import CONF_ACCESS_TOKEN from homeassistant.const import CONF_ACCESS_TOKEN, STATE_OPEN, STATE_CLOSED
def setup_platform(hass, config, add_devices, discovery_info=None): def setup_platform(hass, config, add_devices, discovery_info=None):
@ -22,3 +22,34 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
pywink.set_bearer_token(token) pywink.set_bearer_token(token)
add_devices(WinkSensorDevice(sensor) for sensor in pywink.get_sensors()) add_devices(WinkSensorDevice(sensor) for sensor in pywink.get_sensors())
class WinkSensorDevice(Device):
""" represents a wink sensor within home assistant. """
def __init__(self, wink):
self.wink = wink
@property
def state(self):
""" Returns the state. """
return STATE_OPEN if self.is_open else STATE_CLOSED
@property
def unique_id(self):
""" Returns the id of this wink sensor """
return "{}.{}".format(self.__class__, self.wink.deviceId())
@property
def name(self):
""" Returns the name of the sensor if any. """
return self.wink.name()
def update(self):
""" Update state of the sensor. """
self.wink.updateState()
@property
def is_open(self):
""" True if door is open. """
return self.wink.state()

View File

@ -11,7 +11,7 @@ from pydispatch import dispatcher
import homeassistant.components.zwave as zwave import homeassistant.components.zwave as zwave
from homeassistant.helpers.device import Device from homeassistant.helpers.device import Device
from homeassistant.const import ( from homeassistant.const import (
ATTR_BATTERY_LEVEL, ATTR_UNIT_OF_MEASUREMENT, STATE_ON, STATE_OFF, ATTR_BATTERY_LEVEL, STATE_ON, STATE_OFF,
TEMP_CELCIUS, TEMP_FAHRENHEIT, ATTR_LOCATION) TEMP_CELCIUS, TEMP_FAHRENHEIT, ATTR_LOCATION)
@ -77,11 +77,6 @@ class ZWaveSensor(Device):
if battery_level is not None: if battery_level is not None:
attrs[ATTR_BATTERY_LEVEL] = battery_level attrs[ATTR_BATTERY_LEVEL] = battery_level
unit = self.unit
if unit:
attrs[ATTR_UNIT_OF_MEASUREMENT] = unit
location = self._node.location location = self._node.location
if location: if location:
@ -90,8 +85,7 @@ class ZWaveSensor(Device):
return attrs return attrs
@property @property
def unit(self): def unit_of_measurement(self):
""" Unit if sensor has one. """
return self._value.units return self._value.units
def _value_changed(self, value): def _value_changed(self, value):
@ -126,8 +120,7 @@ class ZWaveMultilevelSensor(ZWaveSensor):
return value return value
@property @property
def unit(self): def unit_of_measurement(self):
""" Unit of this sensor. """
unit = self._value.units unit = self._value.units
if unit == 'C': if unit == 'C':

View File

@ -24,9 +24,6 @@ which event (sunset or sunrise) and the offset.
import logging import logging
from datetime import datetime, timedelta from datetime import datetime, timedelta
import homeassistant as ha
from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE
from homeassistant.helpers import validate_config
from homeassistant.util import str_to_datetime, datetime_to_str from homeassistant.util import str_to_datetime, datetime_to_str
from homeassistant.components.scheduler import ServiceEventListener from homeassistant.components.scheduler import ServiceEventListener
@ -83,11 +80,6 @@ def setup(hass, config):
""" Tracks the state of the sun. """ """ Tracks the state of the sun. """
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
if not validate_config(config,
{ha.DOMAIN: [CONF_LATITUDE, CONF_LONGITUDE]},
logger):
return False
try: try:
import ephem import ephem
except ImportError: except ImportError:
@ -96,8 +88,8 @@ def setup(hass, config):
sun = ephem.Sun() # pylint: disable=no-member sun = ephem.Sun() # pylint: disable=no-member
latitude = str(config[ha.DOMAIN][CONF_LATITUDE]) latitude = str(hass.config.latitude)
longitude = str(config[ha.DOMAIN][CONF_LONGITUDE]) longitude = str(hass.config.longitude)
# Validate latitude and longitude # Validate latitude and longitude
observer = ephem.Observer() observer = ephem.Observer()

View File

@ -11,8 +11,7 @@ from homeassistant.helpers.device_component import DeviceComponent
import homeassistant.util as util import homeassistant.util as util
from homeassistant.helpers.device import Device from homeassistant.helpers.device import Device
from homeassistant.const import ( from homeassistant.const import (
ATTR_ENTITY_ID, ATTR_TEMPERATURE, ATTR_UNIT_OF_MEASUREMENT, ATTR_ENTITY_ID, ATTR_TEMPERATURE, STATE_ON, STATE_OFF)
STATE_ON, STATE_OFF)
DOMAIN = "thermostat" DOMAIN = "thermostat"
DEPENDENCIES = [] DEPENDENCIES = []
@ -109,11 +108,6 @@ class ThermostatDevice(Device):
""" Returns the current state. """ """ Returns the current state. """
return self.target_temperature return self.target_temperature
@property
def unit_of_measurement(self):
""" Returns the unit of measurement. """
return ""
@property @property
def device_state_attributes(self): def device_state_attributes(self):
""" Returns device specific state attributes. """ """ Returns device specific state attributes. """
@ -123,8 +117,8 @@ class ThermostatDevice(Device):
def state_attributes(self): def state_attributes(self):
""" Returns optional state attributes. """ """ Returns optional state attributes. """
data = { data = {
ATTR_UNIT_OF_MEASUREMENT: self.unit_of_measurement, ATTR_CURRENT_TEMPERATURE: self.hass.config.temperature(
ATTR_CURRENT_TEMPERATURE: self.current_temperature self.current_temperature, self.unit_of_measurement)[0]
} }
is_away = self.is_away_mode_on is_away = self.is_away_mode_on

View File

@ -8,10 +8,9 @@ import homeassistant.external.wink.pywink as pywink
from homeassistant import bootstrap from homeassistant import bootstrap
from homeassistant.loader import get_component from homeassistant.loader import get_component
from homeassistant.helpers import validate_config, ToggleDevice, Device from homeassistant.helpers import validate_config, ToggleDevice
from homeassistant.const import ( from homeassistant.const import (
EVENT_PLATFORM_DISCOVERED, CONF_ACCESS_TOKEN, EVENT_PLATFORM_DISCOVERED, CONF_ACCESS_TOKEN,
STATE_OPEN, STATE_CLOSED,
ATTR_SERVICE, ATTR_DISCOVERED, ATTR_FRIENDLY_NAME) ATTR_SERVICE, ATTR_DISCOVERED, ATTR_FRIENDLY_NAME)
DOMAIN = "wink" DOMAIN = "wink"
@ -53,44 +52,6 @@ def setup(hass, config):
return True return True
class WinkSensorDevice(Device):
""" represents a wink sensor within home assistant. """
def __init__(self, wink):
self.wink = wink
@property
def state(self):
""" Returns the state. """
return STATE_OPEN if self.is_open else STATE_CLOSED
@property
def unique_id(self):
""" Returns the id of this wink switch """
return "{}.{}".format(self.__class__, self.wink.deviceId())
@property
def name(self):
""" Returns the name of the sensor if any. """
return self.wink.name()
@property
def state_attributes(self):
""" Returns optional state attributes. """
return {
ATTR_FRIENDLY_NAME: self.wink.name()
}
def update(self):
""" Update state of the sensor. """
self.wink.updateState()
@property
def is_open(self):
""" True if door is open. """
return self.wink.state()
class WinkToggleDevice(ToggleDevice): class WinkToggleDevice(ToggleDevice):
""" represents a Wink switch within home assistant. """ """ represents a Wink switch within home assistant. """

View File

@ -72,7 +72,7 @@ def setup(hass, config):
# Setup options # Setup options
options = ZWaveOption( options = ZWaveOption(
config[DOMAIN].get(CONF_USB_STICK_PATH, DEFAULT_CONF_USB_STICK_PATH), config[DOMAIN].get(CONF_USB_STICK_PATH, DEFAULT_CONF_USB_STICK_PATH),
user_path=hass.config_dir) user_path=hass.config.config_dir)
options.set_console_output(use_debug) options.set_console_output(use_debug)
options.lock() options.lock()

View File

@ -8,6 +8,9 @@ DEVICE_DEFAULT_NAME = "Unnamed Device"
# #### CONFIG #### # #### CONFIG ####
CONF_LATITUDE = "latitude" CONF_LATITUDE = "latitude"
CONF_LONGITUDE = "longitude" CONF_LONGITUDE = "longitude"
CONF_TEMPERATURE_UNIT = "temperature_unit"
CONF_NAME = "name"
CONF_TIME_ZONE = "time_zone"
CONF_PLATFORM = "platform" CONF_PLATFORM = "platform"
CONF_HOST = "host" CONF_HOST = "host"

View File

@ -8,7 +8,8 @@ Provides ABC for devices in HA.
from homeassistant import NoEntitySpecifiedError from homeassistant import NoEntitySpecifiedError
from homeassistant.const import ( from homeassistant.const import (
ATTR_FRIENDLY_NAME, STATE_ON, STATE_OFF, DEVICE_DEFAULT_NAME) ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT, STATE_ON, STATE_OFF,
DEVICE_DEFAULT_NAME, TEMP_CELCIUS, TEMP_FAHRENHEIT)
class Device(object): class Device(object):
@ -46,6 +47,11 @@ class Device(object):
""" Returns the state attributes. """ """ Returns the state attributes. """
return {} return {}
@property
def unit_of_measurement(self):
""" Unit of measurement of this entity if any. """
return None
# DEPRECATION NOTICE: # DEPRECATION NOTICE:
# Device is moving from getters to properties. # Device is moving from getters to properties.
# For now the new properties will call the old functions # For now the new properties will call the old functions
@ -82,12 +88,25 @@ class Device(object):
if force_refresh: if force_refresh:
self.update() self.update()
state = str(self.state)
attr = self.state_attributes or {} attr = self.state_attributes or {}
if ATTR_FRIENDLY_NAME not in attr and self.name: if ATTR_FRIENDLY_NAME not in attr and self.name:
attr[ATTR_FRIENDLY_NAME] = self.name attr[ATTR_FRIENDLY_NAME] = self.name
return self.hass.states.set(self.entity_id, self.state, attr) if ATTR_UNIT_OF_MEASUREMENT not in attr and self.unit_of_measurement:
attr[ATTR_UNIT_OF_MEASUREMENT] = self.unit_of_measurement
# Convert temperature if we detect one
if attr.get(ATTR_UNIT_OF_MEASUREMENT) in (TEMP_CELCIUS,
TEMP_FAHRENHEIT):
state, attr[ATTR_UNIT_OF_MEASUREMENT] = \
self.hass.config.temperature(
state, attr[ATTR_UNIT_OF_MEASUREMENT])
state = str(state)
return self.hass.states.set(self.entity_id, state, attr)
def __eq__(self, other): def __eq__(self, other):
return (isinstance(other, Device) and return (isinstance(other, Device) and

View File

@ -46,11 +46,11 @@ def prepare(hass):
pkgutil.iter_modules(components.__path__, 'homeassistant.components.')) pkgutil.iter_modules(components.__path__, 'homeassistant.components.'))
# Look for available custom components # Look for available custom components
custom_path = hass.get_config_path("custom_components") custom_path = hass.config.path("custom_components")
if os.path.isdir(custom_path): if os.path.isdir(custom_path):
# Ensure we can load custom components using Pythons import # Ensure we can load custom components using Pythons import
sys.path.insert(0, hass.config_dir) sys.path.insert(0, hass.config.config_dir)
# We cannot use the same approach as for built-in components because # We cannot use the same approach as for built-in components because
# custom components might only contain a platform for a component. # custom components might only contain a platform for a component.

View File

@ -14,7 +14,6 @@ import logging
import json import json
import enum import enum
import urllib.parse import urllib.parse
import os
import requests import requests
@ -115,10 +114,9 @@ class HomeAssistant(ha.HomeAssistant):
self.bus = EventBus(remote_api, pool) self.bus = EventBus(remote_api, pool)
self.services = ha.ServiceRegistry(self.bus, pool) self.services = ha.ServiceRegistry(self.bus, pool)
self.states = StateMachine(self.bus, self.remote_api) self.states = StateMachine(self.bus, self.remote_api)
self.config = ha.Config()
self.components = [] self.components = []
self.config_dir = os.path.join(os.getcwd(), 'config')
def start(self): def start(self):
# Ensure a local API exists to connect with remote # Ensure a local API exists to connect with remote
if self.local_api is None: if self.local_api is None:

View File

@ -14,7 +14,7 @@ from homeassistant.const import STATE_ON, STATE_OFF, DEVICE_DEFAULT_NAME
def get_test_home_assistant(): def get_test_home_assistant():
""" Returns a Home Assistant object pointing at test config dir. """ """ Returns a Home Assistant object pointing at test config dir. """
hass = ha.HomeAssistant() hass = ha.HomeAssistant()
hass.config_dir = os.path.join(os.path.dirname(__file__), "config") hass.config.config_dir = os.path.join(os.path.dirname(__file__), "config")
return hass return hass

View File

@ -32,7 +32,7 @@ class TestComponentsDeviceTracker(unittest.TestCase):
self.hass = get_test_home_assistant() self.hass = get_test_home_assistant()
loader.prepare(self.hass) loader.prepare(self.hass)
self.known_dev_path = self.hass.get_config_path( self.known_dev_path = self.hass.config.path(
device_tracker.KNOWN_DEVICES_FILE) device_tracker.KNOWN_DEVICES_FILE)
def tearDown(self): # pylint: disable=invalid-name def tearDown(self): # pylint: disable=invalid-name

View File

@ -29,7 +29,7 @@ class TestLight(unittest.TestCase):
""" Stop down stuff we started. """ """ Stop down stuff we started. """
self.hass.stop() self.hass.stop()
user_light_file = self.hass.get_config_path(light.LIGHT_PROFILES_FILE) user_light_file = self.hass.config.path(light.LIGHT_PROFILES_FILE)
if os.path.isfile(user_light_file): if os.path.isfile(user_light_file):
os.remove(user_light_file) os.remove(user_light_file)
@ -218,7 +218,7 @@ class TestLight(unittest.TestCase):
platform = loader.get_component('light.test') platform = loader.get_component('light.test')
platform.init() platform.init()
user_light_file = self.hass.get_config_path(light.LIGHT_PROFILES_FILE) user_light_file = self.hass.config.path(light.LIGHT_PROFILES_FILE)
# Setup a wrong light file # Setup a wrong light file
with open(user_light_file, 'w') as user_file: with open(user_light_file, 'w') as user_file:
@ -234,7 +234,7 @@ class TestLight(unittest.TestCase):
platform = loader.get_component('light.test') platform = loader.get_component('light.test')
platform.init() platform.init()
user_light_file = self.hass.get_config_path(light.LIGHT_PROFILES_FILE) user_light_file = self.hass.config.path(light.LIGHT_PROFILES_FILE)
with open(user_light_file, 'w') as user_file: with open(user_light_file, 'w') as user_file:
user_file.write('id,x,y,brightness\n') user_file.write('id,x,y,brightness\n')

View File

@ -11,7 +11,6 @@ import datetime as dt
import ephem import ephem
import homeassistant as ha import homeassistant as ha
from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE
import homeassistant.components.sun as sun import homeassistant.components.sun as sun
@ -35,12 +34,9 @@ class TestSun(unittest.TestCase):
def test_setting_rising(self): def test_setting_rising(self):
""" Test retrieving sun setting and rising. """ """ Test retrieving sun setting and rising. """
# Compare it with the real data # Compare it with the real data
self.assertTrue(sun.setup( self.hass.config.latitude = '32.87336'
self.hass, self.hass.config.longitude = '117.22743'
{ha.DOMAIN: { sun.setup(self.hass, None)
CONF_LATITUDE: '32.87336',
CONF_LONGITUDE: '117.22743'
}}))
observer = ephem.Observer() observer = ephem.Observer()
observer.lat = '32.87336' # pylint: disable=assigning-non-slot observer.lat = '32.87336' # pylint: disable=assigning-non-slot
@ -74,12 +70,9 @@ class TestSun(unittest.TestCase):
def test_state_change(self): def test_state_change(self):
""" Test if the state changes at next setting/rising. """ """ Test if the state changes at next setting/rising. """
self.assertTrue(sun.setup( self.hass.config.latitude = '32.87336'
self.hass, self.hass.config.longitude = '117.22743'
{ha.DOMAIN: { sun.setup(self.hass, None)
CONF_LATITUDE: '32.87336',
CONF_LONGITUDE: '117.22743'
}}))
if sun.is_on(self.hass): if sun.is_on(self.hass):
test_state = sun.STATE_BELOW_HORIZON test_state = sun.STATE_BELOW_HORIZON
@ -96,30 +89,3 @@ class TestSun(unittest.TestCase):
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
self.assertEqual(test_state, self.hass.states.get(sun.ENTITY_ID).state) self.assertEqual(test_state, self.hass.states.get(sun.ENTITY_ID).state)
def test_setup(self):
""" Test Sun setup with empty and wrong configs. """
self.assertFalse(sun.setup(self.hass, {}))
self.assertFalse(sun.setup(self.hass, {sun.DOMAIN: {}}))
self.assertFalse(sun.setup(
self.hass, {ha.DOMAIN: {CONF_LATITUDE: '32.87336'}}))
self.assertFalse(sun.setup(
self.hass, {ha.DOMAIN: {CONF_LONGITUDE: '117.22743'}}))
self.assertFalse(sun.setup(
self.hass, {ha.DOMAIN: {CONF_LATITUDE: 'hello'}}))
self.assertFalse(sun.setup(
self.hass, {ha.DOMAIN: {CONF_LONGITUDE: 'how are you'}}))
self.assertFalse(sun.setup(
self.hass, {ha.DOMAIN: {
CONF_LATITUDE: 'wrong', CONF_LONGITUDE: '117.22743'
}}))
self.assertFalse(sun.setup(
self.hass, {ha.DOMAIN: {
CONF_LATITUDE: '32.87336', CONF_LONGITUDE: 'wrong'
}}))
# Test with correct config
self.assertTrue(sun.setup(
self.hass, {ha.DOMAIN: {
CONF_LATITUDE: '32.87336', CONF_LONGITUDE: '117.22743'
}}))

View File

@ -35,10 +35,10 @@ class TestHomeAssistant(unittest.TestCase):
def test_get_config_path(self): def test_get_config_path(self):
""" Test get_config_path method. """ """ Test get_config_path method. """
self.assertEqual(os.path.join(os.getcwd(), "config"), self.assertEqual(os.path.join(os.getcwd(), "config"),
self.hass.config_dir) self.hass.config.config_dir)
self.assertEqual(os.path.join(os.getcwd(), "config", "test.conf"), self.assertEqual(os.path.join(os.getcwd(), "config", "test.conf"),
self.hass.get_config_path("test.conf")) self.hass.config.path("test.conf"))
def test_block_till_stoped(self): def test_block_till_stoped(self):
""" Test if we can block till stop service is called. """ """ Test if we can block till stop service is called. """