Many changes to cleanup config directory and lib installations.
Cleaned up default config directory determination. Made bootstrap creators for HA always set config directory. Made bootstrap creators set the local library in the Python Path. Moved all exceptions to their own file to make imports easier. Moved default configuration directory be in the users’ profile. Moved pip installs to be done to a lib folder in the config directory. Reduced requirements.txt to only the barebones reqs.
This commit is contained in:
parent
18e32165a4
commit
6fdf9b8d7c
7 changed files with 53 additions and 150 deletions
|
@ -10,9 +10,6 @@ from homeassistant import bootstrap
|
||||||
import homeassistant.config as config_util
|
import homeassistant.config as config_util
|
||||||
from homeassistant.components import frontend, demo
|
from homeassistant.components import frontend, demo
|
||||||
|
|
||||||
USER_DATA_DIR = os.getenv('APPDATA') if os.name == "nt" \
|
|
||||||
else os.path.expanduser('~')
|
|
||||||
|
|
||||||
|
|
||||||
def validate_python():
|
def validate_python():
|
||||||
""" Validate we're running the right Python version. """
|
""" Validate we're running the right Python version. """
|
||||||
|
@ -83,7 +80,7 @@ def get_arguments():
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-c', '--config',
|
'-c', '--config',
|
||||||
metavar='path_to_config_dir',
|
metavar='path_to_config_dir',
|
||||||
default=os.path.join(USER_DATA_DIR, '.homeassistant'),
|
default=config_util.get_default_config_dir(),
|
||||||
help="Directory that contains the Home Assistant configuration")
|
help="Directory that contains the Home Assistant configuration")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--demo-mode',
|
'--demo-mode',
|
||||||
|
@ -112,7 +109,7 @@ def main():
|
||||||
hass = bootstrap.from_config_dict({
|
hass = bootstrap.from_config_dict({
|
||||||
frontend.DOMAIN: {},
|
frontend.DOMAIN: {},
|
||||||
demo.DOMAIN: {}
|
demo.DOMAIN: {}
|
||||||
})
|
}, config_dir=config_dir)
|
||||||
else:
|
else:
|
||||||
hass = bootstrap.from_config_file(config_path)
|
hass = bootstrap.from_config_file(config_path)
|
||||||
|
|
||||||
|
|
|
@ -61,13 +61,13 @@ def setup_component(hass, domain, config=None):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def _handle_requirements(component, name):
|
def _handle_requirements(hass, component, name):
|
||||||
""" Installs requirements for component. """
|
""" Installs requirements for component. """
|
||||||
if not hasattr(component, 'REQUIREMENTS'):
|
if not hasattr(component, 'REQUIREMENTS'):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
for req in component.REQUIREMENTS:
|
for req in component.REQUIREMENTS:
|
||||||
if not pkg_util.install_package(req):
|
if not pkg_util.install_package(req, target=hass.config.path('lib')):
|
||||||
_LOGGER.error('Not initializing %s because could not install '
|
_LOGGER.error('Not initializing %s because could not install '
|
||||||
'dependency %s', name, req)
|
'dependency %s', name, req)
|
||||||
return False
|
return False
|
||||||
|
@ -88,7 +88,7 @@ def _setup_component(hass, domain, config):
|
||||||
domain, ", ".join(missing_deps))
|
domain, ", ".join(missing_deps))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if not _handle_requirements(component, domain):
|
if not _handle_requirements(hass, component, domain):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -138,14 +138,14 @@ def prepare_setup_platform(hass, config, domain, platform_name):
|
||||||
component)
|
component)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if not _handle_requirements(platform, platform_path):
|
if not _handle_requirements(hass, platform, platform_path):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return platform
|
return platform
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=too-many-branches, too-many-statements
|
# pylint: disable=too-many-branches, too-many-statements
|
||||||
def from_config_dict(config, hass=None):
|
def from_config_dict(config, hass=None, config_dir=None):
|
||||||
"""
|
"""
|
||||||
Tries to configure Home Assistant from a config dict.
|
Tries to configure Home Assistant from a config dict.
|
||||||
|
|
||||||
|
@ -153,6 +153,9 @@ def from_config_dict(config, hass=None):
|
||||||
"""
|
"""
|
||||||
if hass is None:
|
if hass is None:
|
||||||
hass = core.HomeAssistant()
|
hass = core.HomeAssistant()
|
||||||
|
if config_dir is not None:
|
||||||
|
hass.config.config_dir = os.path.abspath(config_dir)
|
||||||
|
hass.config.mount_local_path()
|
||||||
|
|
||||||
process_ha_core_config(hass, config.get(core.DOMAIN, {}))
|
process_ha_core_config(hass, config.get(core.DOMAIN, {}))
|
||||||
|
|
||||||
|
@ -196,6 +199,7 @@ def from_config_file(config_path, hass=None):
|
||||||
|
|
||||||
# Set config dir to directory holding config file
|
# Set config dir to directory holding config file
|
||||||
hass.config.config_dir = os.path.abspath(os.path.dirname(config_path))
|
hass.config.config_dir = os.path.abspath(os.path.dirname(config_path))
|
||||||
|
hass.config.mount_local_path()
|
||||||
|
|
||||||
config_dict = config_util.load_config_file(config_path)
|
config_dict = config_util.load_config_file(config_path)
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ Module to help with parsing and generating configuration files.
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from homeassistant.core import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_LATITUDE, CONF_LONGITUDE, CONF_TEMPERATURE_UNIT, CONF_NAME,
|
CONF_LATITUDE, CONF_LONGITUDE, CONF_TEMPERATURE_UNIT, CONF_NAME,
|
||||||
CONF_TIME_ZONE)
|
CONF_TIME_ZONE)
|
||||||
|
@ -16,6 +16,7 @@ import homeassistant.util.location as loc_util
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
YAML_CONFIG_FILE = 'configuration.yaml'
|
YAML_CONFIG_FILE = 'configuration.yaml'
|
||||||
|
CONFIG_DIR_NAME = '.homeassistant'
|
||||||
|
|
||||||
DEFAULT_CONFIG = (
|
DEFAULT_CONFIG = (
|
||||||
# Tuples (attribute, default, auto detect property, description)
|
# Tuples (attribute, default, auto detect property, description)
|
||||||
|
@ -39,6 +40,13 @@ DEFAULT_COMPONENTS = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def get_default_config_dir():
|
||||||
|
""" Put together the default configuration directory based on OS. """
|
||||||
|
data_dir = os.getenv('APPDATA') if os.name == "nt" \
|
||||||
|
else os.path.expanduser('~')
|
||||||
|
return os.path.join(data_dir, CONFIG_DIR_NAME)
|
||||||
|
|
||||||
|
|
||||||
def ensure_config_exists(config_dir, detect_location=True):
|
def ensure_config_exists(config_dir, detect_location=True):
|
||||||
""" Ensures a config file exists in given config dir.
|
""" Ensures a config file exists in given config dir.
|
||||||
Creating a default one if needed.
|
Creating a default one if needed.
|
||||||
|
|
|
@ -7,6 +7,7 @@ of entities and react to changes.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
import time
|
import time
|
||||||
import logging
|
import logging
|
||||||
import threading
|
import threading
|
||||||
|
@ -21,9 +22,12 @@ from homeassistant.const import (
|
||||||
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, ATTR_FRIENDLY_NAME)
|
TEMP_CELCIUS, TEMP_FAHRENHEIT, ATTR_FRIENDLY_NAME)
|
||||||
|
from homeassistant.exceptions import (
|
||||||
|
HomeAssistantError, InvalidEntityFormatError, NoEntitySpecifiedError)
|
||||||
import homeassistant.util as util
|
import homeassistant.util as util
|
||||||
import homeassistant.util.dt as date_util
|
import homeassistant.util.dt as date_util
|
||||||
import homeassistant.helpers.temperature as temp_helper
|
import homeassistant.helpers.temperature as temp_helper
|
||||||
|
from homeassistant.config import get_default_config_dir
|
||||||
|
|
||||||
DOMAIN = "homeassistant"
|
DOMAIN = "homeassistant"
|
||||||
|
|
||||||
|
@ -660,7 +664,11 @@ class Config(object):
|
||||||
self.api = None
|
self.api = None
|
||||||
|
|
||||||
# Directory that holds the configuration
|
# Directory that holds the configuration
|
||||||
self.config_dir = os.path.join(os.getcwd(), 'config')
|
self.config_dir = get_default_config_dir()
|
||||||
|
|
||||||
|
def mount_local_path(self):
|
||||||
|
""" Add local library to Python Path """
|
||||||
|
sys.path.insert(0, self.path('lib'))
|
||||||
|
|
||||||
def path(self, *path):
|
def path(self, *path):
|
||||||
""" Returns path to the file within the config dir. """
|
""" Returns path to the file within the config dir. """
|
||||||
|
@ -695,21 +703,6 @@ class Config(object):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class HomeAssistantError(Exception):
|
|
||||||
""" General Home Assistant exception occured. """
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class InvalidEntityFormatError(HomeAssistantError):
|
|
||||||
""" When an invalid formatted entity is encountered. """
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class NoEntitySpecifiedError(HomeAssistantError):
|
|
||||||
""" When no entity is specified. """
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def create_timer(hass, interval=TIMER_INTERVAL):
|
def create_timer(hass, interval=TIMER_INTERVAL):
|
||||||
""" Creates a timer. Timer will start on HOMEASSISTANT_START. """
|
""" Creates a timer. Timer will start on HOMEASSISTANT_START. """
|
||||||
# We want to be able to fire every time a minute starts (seconds=0).
|
# We want to be able to fire every time a minute starts (seconds=0).
|
||||||
|
|
15
homeassistant/exceptions.py
Normal file
15
homeassistant/exceptions.py
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
""" Exceptions used by Home Assistant """
|
||||||
|
|
||||||
|
class HomeAssistantError(Exception):
|
||||||
|
""" General Home Assistant exception occured. """
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidEntityFormatError(HomeAssistantError):
|
||||||
|
""" When an invalid formatted entity is encountered. """
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class NoEntitySpecifiedError(HomeAssistantError):
|
||||||
|
""" When no entity is specified. """
|
||||||
|
pass
|
|
@ -1,4 +1,5 @@
|
||||||
"""Helpers to install PyPi packages."""
|
"""Helpers to install PyPi packages."""
|
||||||
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
@ -8,15 +9,16 @@ from . import environment as env
|
||||||
INSTALL_USER = not env.is_virtual()
|
INSTALL_USER = not env.is_virtual()
|
||||||
|
|
||||||
|
|
||||||
def install_package(package, upgrade=False, user=INSTALL_USER):
|
def install_package(package, upgrade=False, target=None):
|
||||||
"""Install a package on PyPi. Accepts pip compatible package strings.
|
"""Install a package on PyPi. Accepts pip compatible package strings.
|
||||||
Return boolean if install successfull."""
|
Return boolean if install successfull."""
|
||||||
# Not using 'import pip; pip.main([])' because it breaks the logger
|
# Not using 'import pip; pip.main([])' because it breaks the logger
|
||||||
args = [sys.executable, '-m', 'pip', 'install', '--quiet', package]
|
args = [sys.executable, '-m', 'pip', 'install', '--quiet',
|
||||||
|
'--isolated', '-I', package]
|
||||||
if upgrade:
|
if upgrade:
|
||||||
args.append('--upgrade')
|
args.append('--upgrade')
|
||||||
if user:
|
if target:
|
||||||
args.append('--user')
|
args += ['--target', os.path.abspath(target)]
|
||||||
try:
|
try:
|
||||||
return 0 == subprocess.call(args)
|
return 0 == subprocess.call(args)
|
||||||
except subprocess.SubprocessError:
|
except subprocess.SubprocessError:
|
||||||
|
|
122
requirements.txt
122
requirements.txt
|
@ -1,119 +1,3 @@
|
||||||
# Required for Home Assistant core
|
requests==2.7.0
|
||||||
requests>=2.0
|
pyyaml==3.11
|
||||||
pyyaml>=3.11
|
pytz==2015.4
|
||||||
pytz>=2015.2
|
|
||||||
|
|
||||||
# Optional, needed for specific components
|
|
||||||
|
|
||||||
# Sun (sun)
|
|
||||||
astral>=0.8.1
|
|
||||||
|
|
||||||
# Philips Hue library (lights.hue)
|
|
||||||
phue>=0.8
|
|
||||||
|
|
||||||
# Limitlessled/Easybulb/Milight library (lights.limitlessled)
|
|
||||||
ledcontroller>=1.0.7
|
|
||||||
|
|
||||||
# Chromecast bindings (media_player.cast)
|
|
||||||
pychromecast>=0.6.10
|
|
||||||
|
|
||||||
# Keyboard (keyboard)
|
|
||||||
pyuserinput>=0.1.9
|
|
||||||
|
|
||||||
# Tellstick bindings (*.tellstick)
|
|
||||||
tellcore-py>=1.0.4
|
|
||||||
|
|
||||||
# Nmap bindings (device_tracker.nmap)
|
|
||||||
python-libnmap>=0.6.3
|
|
||||||
|
|
||||||
# PushBullet bindings (notify.pushbullet)
|
|
||||||
pushbullet.py>=0.7.1
|
|
||||||
|
|
||||||
# Nest Thermostat bindings (thermostat.nest)
|
|
||||||
python-nest>=2.4.0
|
|
||||||
|
|
||||||
# Z-Wave (*.zwave)
|
|
||||||
pydispatcher>=2.0.5
|
|
||||||
|
|
||||||
# ISY994 bindings (*.isy994)
|
|
||||||
PyISY>=1.0.5
|
|
||||||
|
|
||||||
# PSutil (sensor.systemmonitor)
|
|
||||||
psutil>=3.0.0
|
|
||||||
|
|
||||||
# Pushover bindings (notify.pushover)
|
|
||||||
python-pushover>=0.2
|
|
||||||
|
|
||||||
# Transmission Torrent Client (*.transmission)
|
|
||||||
transmissionrpc>=0.11
|
|
||||||
|
|
||||||
# OpenWeatherMap Web API (sensor.openweathermap)
|
|
||||||
pyowm>=2.2.1
|
|
||||||
|
|
||||||
# XMPP Bindings (notify.xmpp)
|
|
||||||
sleekxmpp>=1.3.1
|
|
||||||
dnspython3>=1.12.0
|
|
||||||
|
|
||||||
# Blockchain (sensor.bitcoin)
|
|
||||||
blockchain>=1.1.2
|
|
||||||
|
|
||||||
# MPD Bindings (media_player.mpd)
|
|
||||||
python-mpd2>=0.5.4
|
|
||||||
|
|
||||||
# Hikvision (switch.hikvisioncam)
|
|
||||||
hikvision>=0.4
|
|
||||||
|
|
||||||
# console log coloring
|
|
||||||
colorlog>=2.6.0
|
|
||||||
|
|
||||||
# JSON-RPC interface (media_player.kodi)
|
|
||||||
jsonrpc-requests>=0.1
|
|
||||||
|
|
||||||
# Forecast.io Bindings (sensor.forecast)
|
|
||||||
python-forecastio>=1.3.3
|
|
||||||
|
|
||||||
# Firmata Bindings (*.arduino)
|
|
||||||
PyMata==2.07a
|
|
||||||
|
|
||||||
# Rfxtrx sensor (sensor.rfxtrx)
|
|
||||||
https://github.com/Danielhiversen/pyRFXtrx/archive/master.zip
|
|
||||||
|
|
||||||
# Mysensors
|
|
||||||
https://github.com/theolind/pymysensors/archive/master.zip#egg=pymysensors-0.1
|
|
||||||
|
|
||||||
# Netgear (device_tracker.netgear)
|
|
||||||
pynetgear>=0.3
|
|
||||||
|
|
||||||
# Netdisco (discovery)
|
|
||||||
netdisco>=0.3
|
|
||||||
|
|
||||||
# Wemo (switch.wemo)
|
|
||||||
pywemo>=0.2
|
|
||||||
|
|
||||||
# Wink (*.wink)
|
|
||||||
https://github.com/balloob/python-wink/archive/master.zip#pywink>=0.1
|
|
||||||
|
|
||||||
# Slack notifier (notify.slack)
|
|
||||||
slacker>=0.6.8
|
|
||||||
|
|
||||||
# Temper sensors (sensor.temper)
|
|
||||||
https://github.com/rkabadi/temper-python/archive/master.zip
|
|
||||||
|
|
||||||
# PyEdimax
|
|
||||||
https://github.com/rkabadi/pyedimax/archive/master.zip
|
|
||||||
|
|
||||||
# RPI-GPIO platform (*.rpi_gpio)
|
|
||||||
RPi.GPIO >=0.5.11
|
|
||||||
|
|
||||||
# Adafruit temperature/humidity sensor
|
|
||||||
# uncomment on a Raspberry Pi / Beaglebone
|
|
||||||
#git+git://github.com/mala-zaba/Adafruit_Python_DHT
|
|
||||||
|
|
||||||
# PAHO MQTT Binding (mqtt)
|
|
||||||
paho-mqtt>=1.1
|
|
||||||
|
|
||||||
# PyModbus (modbus)
|
|
||||||
https://github.com/bashwork/pymodbus/archive/python3.zip#pymodbus>=1.2.0
|
|
||||||
|
|
||||||
# Verisure (verisure)
|
|
||||||
https://github.com/persandstrom/python-verisure/archive/master.zip
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue