mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 21:27:38 +00:00
Merge branch 'dev' of https://github.com/balloob/home-assistant into automation_confg_list
This commit is contained in:
commit
e1a7b8f988
@ -3,11 +3,6 @@ language: python
|
||||
python:
|
||||
- "3.4"
|
||||
install:
|
||||
- pip install -r requirements_all.txt
|
||||
- pip install flake8 pylint coveralls
|
||||
- script/bootstrap_server
|
||||
script:
|
||||
- flake8 homeassistant
|
||||
- pylint homeassistant
|
||||
- coverage run -m unittest discover tests
|
||||
after_success:
|
||||
- coveralls
|
||||
- script/cibuild
|
||||
|
@ -103,6 +103,10 @@ def get_arguments():
|
||||
'--uninstall-osx',
|
||||
action='store_true',
|
||||
help='Uninstalls from OS X.')
|
||||
parser.add_argument(
|
||||
'--restart-osx',
|
||||
action='store_true',
|
||||
help='Restarts on OS X.')
|
||||
if os.name != "nt":
|
||||
parser.add_argument(
|
||||
'--daemon',
|
||||
@ -216,6 +220,10 @@ def main():
|
||||
if args.uninstall_osx:
|
||||
uninstall_osx()
|
||||
return
|
||||
if args.restart_osx:
|
||||
uninstall_osx()
|
||||
install_osx()
|
||||
return
|
||||
|
||||
# daemon functions
|
||||
if args.pid_file:
|
||||
|
@ -27,8 +27,7 @@ def trigger(hass, config, action):
|
||||
if CONF_AFTER in config:
|
||||
after = dt_util.parse_time_str(config[CONF_AFTER])
|
||||
if after is None:
|
||||
_LOGGER.error(
|
||||
'Received invalid after value: %s', config[CONF_AFTER])
|
||||
_error_time(config[CONF_AFTER], CONF_AFTER)
|
||||
return False
|
||||
hours, minutes, seconds = after.hour, after.minute, after.second
|
||||
elif (CONF_HOURS in config or CONF_MINUTES in config
|
||||
@ -63,27 +62,27 @@ def if_action(hass, config):
|
||||
CONF_BEFORE, CONF_AFTER, CONF_WEEKDAY)
|
||||
return None
|
||||
|
||||
if before is not None:
|
||||
before = dt_util.parse_time_str(before)
|
||||
if before is None:
|
||||
_error_time(before, CONF_BEFORE)
|
||||
return None
|
||||
|
||||
if after is not None:
|
||||
after = dt_util.parse_time_str(after)
|
||||
if after is None:
|
||||
_error_time(after, CONF_AFTER)
|
||||
return None
|
||||
|
||||
def time_if():
|
||||
""" Validate time based if-condition """
|
||||
now = dt_util.now()
|
||||
if before is not None:
|
||||
time = dt_util.parse_time_str(before)
|
||||
if time is None:
|
||||
if before is not None and now > now.replace(hour=before.hour,
|
||||
minute=before.minute):
|
||||
return False
|
||||
|
||||
before_point = now.replace(hour=time.hour, minute=time.minute)
|
||||
|
||||
if now > before_point:
|
||||
return False
|
||||
|
||||
if after is not None:
|
||||
time = dt_util.parse_time_str(after)
|
||||
if time is None:
|
||||
return False
|
||||
|
||||
after_point = now.replace(hour=time.hour, minute=time.minute)
|
||||
|
||||
if now < after_point:
|
||||
if after is not None and now < now.replace(hour=after.hour,
|
||||
minute=after.minute):
|
||||
return False
|
||||
|
||||
if weekday is not None:
|
||||
@ -96,3 +95,11 @@ def if_action(hass, config):
|
||||
return True
|
||||
|
||||
return time_if
|
||||
|
||||
|
||||
def _error_time(value, key):
|
||||
""" Helper method to print error. """
|
||||
_LOGGER.error(
|
||||
"Received invalid value for '%s': %s", key, value)
|
||||
if isinstance(value, int):
|
||||
_LOGGER.error('Make sure you wrap time values in quotes')
|
||||
|
@ -1,41 +1,31 @@
|
||||
"""
|
||||
Support for Foscam IP Cameras.
|
||||
|
||||
homeassistant.components.camera.foscam
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
This component provides basic support for Foscam IP cameras.
|
||||
|
||||
As part of the basic support the following features will be provided:
|
||||
-MJPEG video streaming
|
||||
|
||||
To use this component, add the following to your config/configuration.yaml:
|
||||
To use this component, add the following to your configuration.yaml file.
|
||||
|
||||
camera:
|
||||
platform: foscam
|
||||
name: Door Camera
|
||||
ip: 192.168.0.123
|
||||
port: 88
|
||||
username: visitor
|
||||
password: password
|
||||
platform: foscam
|
||||
name: Door Camera
|
||||
ip: 192.168.0.123
|
||||
port: 88
|
||||
username: YOUR_USERNAME
|
||||
password: YOUR_PASSWORD
|
||||
|
||||
camera 2:
|
||||
name: 'Second Camera'
|
||||
...
|
||||
camera 3:
|
||||
name: 'Camera Three'
|
||||
...
|
||||
|
||||
|
||||
VARIABLES:
|
||||
|
||||
These are the variables for the device_data array:
|
||||
Variables:
|
||||
|
||||
ip
|
||||
*Required
|
||||
The IP address of your foscam device
|
||||
The IP address of your Foscam device.
|
||||
|
||||
username
|
||||
*Required
|
||||
The username of a visitor or operator of your camera.
|
||||
Oddly admin accounts don't seem to have access to take snapshots.
|
||||
The username of a visitor or operator of your camera. Oddly admin accounts
|
||||
don't seem to have access to take snapshots.
|
||||
|
||||
password
|
||||
*Required
|
||||
@ -49,6 +39,8 @@ port
|
||||
*Optional
|
||||
The port that the camera is running on. The default is 88.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/camera.foscam.html
|
||||
"""
|
||||
import logging
|
||||
from homeassistant.helpers import validate_config
|
||||
@ -72,9 +64,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
||||
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
class FoscamCamera(Camera):
|
||||
"""
|
||||
An implementation of a Foscam IP camera.
|
||||
"""
|
||||
""" An implementation of a Foscam IP camera. """
|
||||
|
||||
def __init__(self, device_info):
|
||||
super(FoscamCamera, self).__init__()
|
||||
@ -94,7 +84,7 @@ class FoscamCamera(Camera):
|
||||
self._name, self._snap_picture_url)
|
||||
|
||||
def camera_image(self):
|
||||
""" Return a still image reponse from the camera """
|
||||
""" Return a still image reponse from the camera. """
|
||||
|
||||
# send the request to snap a picture
|
||||
response = requests.get(self._snap_picture_url)
|
||||
@ -111,5 +101,5 @@ class FoscamCamera(Camera):
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
""" Return the name of this device """
|
||||
""" Return the name of this device. """
|
||||
return self._name
|
||||
|
@ -28,6 +28,14 @@ REQUIREMENTS = ['SoCo==0.11.1']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
# The soco library is excessively chatty when it comes to logging and
|
||||
# causes a LOT of spam in the logs due to making a http connection to each
|
||||
# speaker every 10 seconds. Quiet it down a bit to just actual problems.
|
||||
_SOCO_LOGGER = logging.getLogger('soco')
|
||||
_SOCO_LOGGER.setLevel(logging.ERROR)
|
||||
_REQUESTS_LOGGER = logging.getLogger('requests')
|
||||
_REQUESTS_LOGGER.setLevel(logging.ERROR)
|
||||
|
||||
SUPPORT_SONOS = SUPPORT_PAUSE | SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE |\
|
||||
SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK | SUPPORT_SEEK
|
||||
|
||||
|
@ -256,7 +256,7 @@ class Recorder(threading.Thread):
|
||||
""" Query the database. """
|
||||
try:
|
||||
with self.conn, self.lock:
|
||||
_LOGGER.info("Running query %s", sql_query)
|
||||
_LOGGER.debug("Running query %s", sql_query)
|
||||
|
||||
cur = self.conn.cursor()
|
||||
|
||||
|
@ -70,7 +70,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
||||
data,
|
||||
config.get('name', DEFAULT_NAME),
|
||||
config.get('unit_of_measurement'),
|
||||
config.get('correction_factor', None),
|
||||
config.get('correction_factor', 1.0),
|
||||
config.get('decimal_places', 0)
|
||||
)])
|
||||
|
||||
@ -108,12 +108,15 @@ class CommandSensor(Entity):
|
||||
self.data.update()
|
||||
value = self.data.value
|
||||
|
||||
if value is not None:
|
||||
if self._corr_factor is not None:
|
||||
self._state = round((int(value) * self._corr_factor),
|
||||
self._decimal_places)
|
||||
else:
|
||||
self._state = value
|
||||
try:
|
||||
if value is not None:
|
||||
if self._corr_factor is not None:
|
||||
self._state = round((float(value) * self._corr_factor),
|
||||
self._decimal_places)
|
||||
else:
|
||||
self._state = value
|
||||
except ValueError:
|
||||
self._state = value
|
||||
|
||||
|
||||
# pylint: disable=too-few-public-methods
|
||||
|
@ -3,7 +3,6 @@ homeassistant.components.sensor.glances
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Gathers system information of hosts which running glances.
|
||||
|
||||
|
||||
Configuration:
|
||||
|
||||
To use the glances sensor you will need to add something like the following
|
||||
|
@ -91,7 +91,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
_LOGGER.error(
|
||||
"Connection error "
|
||||
"Please check your settings for OpenWeatherMap.")
|
||||
return None
|
||||
return False
|
||||
|
||||
data = WeatherData(owm, forecast, hass.config.latitude,
|
||||
hass.config.longitude)
|
||||
|
@ -31,7 +31,7 @@ Details for the API : http://transport.opendata.ch
|
||||
"""
|
||||
import logging
|
||||
from datetime import timedelta
|
||||
from requests import get
|
||||
import requests
|
||||
|
||||
from homeassistant.util import Throttle
|
||||
import homeassistant.util.dt as dt_util
|
||||
@ -53,8 +53,8 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
try:
|
||||
for location in [config.get('from', None), config.get('to', None)]:
|
||||
# transport.opendata.ch doesn't play nice with requests.Session
|
||||
result = get(_RESOURCE + 'locations?query=%s' % location,
|
||||
timeout=10)
|
||||
result = requests.get(_RESOURCE + 'locations?query=%s' % location,
|
||||
timeout=10)
|
||||
journey.append(result.json()['stations'][0]['name'])
|
||||
except KeyError:
|
||||
_LOGGER.exception(
|
||||
@ -110,7 +110,7 @@ class PublicTransportData(object):
|
||||
def update(self):
|
||||
""" Gets the latest data from opendata.ch. """
|
||||
|
||||
response = get(
|
||||
response = requests.get(
|
||||
_RESOURCE +
|
||||
'connections?' +
|
||||
'from=' + self.start + '&' +
|
||||
|
@ -1,6 +1,6 @@
|
||||
"""Helpers to install PyPi packages."""
|
||||
import os
|
||||
import logging
|
||||
import os
|
||||
import pkg_resources
|
||||
import subprocess
|
||||
import sys
|
||||
@ -15,25 +15,24 @@ def install_package(package, upgrade=True, target=None):
|
||||
"""Install a package on PyPi. Accepts pip compatible package strings.
|
||||
Return boolean if install successfull."""
|
||||
# Not using 'import pip; pip.main([])' because it breaks the logger
|
||||
args = [sys.executable, '-m', 'pip', 'install', '--quiet', package]
|
||||
|
||||
if upgrade:
|
||||
args.append('--upgrade')
|
||||
if target:
|
||||
args += ['--target', os.path.abspath(target)]
|
||||
|
||||
with INSTALL_LOCK:
|
||||
if check_package_exists(package, target):
|
||||
return True
|
||||
|
||||
_LOGGER.info('Attempting install of %s', package)
|
||||
args = [sys.executable, '-m', 'pip', 'install', '--quiet', package]
|
||||
if upgrade:
|
||||
args.append('--upgrade')
|
||||
if target:
|
||||
args += ['--target', os.path.abspath(target)]
|
||||
|
||||
try:
|
||||
return 0 == subprocess.call(args)
|
||||
except subprocess.SubprocessError:
|
||||
return False
|
||||
|
||||
|
||||
def check_package_exists(package, target=None):
|
||||
def check_package_exists(package, target):
|
||||
"""Check if a package exists.
|
||||
Returns True when the requirement is met.
|
||||
Returns False when the package is not installed or doesn't meet req."""
|
||||
@ -43,16 +42,5 @@ def check_package_exists(package, target=None):
|
||||
# This is a zip file
|
||||
req = pkg_resources.Requirement.parse(urlparse(package).fragment)
|
||||
|
||||
if target:
|
||||
work_set = pkg_resources.WorkingSet([target])
|
||||
search_fun = work_set.find
|
||||
|
||||
else:
|
||||
search_fun = pkg_resources.get_distribution
|
||||
|
||||
try:
|
||||
result = search_fun(req)
|
||||
except (pkg_resources.DistributionNotFound, pkg_resources.VersionConflict):
|
||||
return False
|
||||
|
||||
return bool(result)
|
||||
return any(dist in req for dist in
|
||||
pkg_resources.find_distributions(target))
|
||||
|
9
script/bootstrap
Executable file
9
script/bootstrap
Executable file
@ -0,0 +1,9 @@
|
||||
#!/bin/sh
|
||||
|
||||
# script/bootstrap: Resolve all dependencies that the application requires to
|
||||
# run.
|
||||
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
script/bootstrap_server
|
||||
script/bootstrap_frontend
|
5
script/bootstrap_frontend
Executable file
5
script/bootstrap_frontend
Executable file
@ -0,0 +1,5 @@
|
||||
echo "Bootstrapping frontend..."
|
||||
cd homeassistant/components/frontend/www_static/home-assistant-polymer
|
||||
npm install
|
||||
npm run setup_js_dev
|
||||
cd ../../../../..
|
10
script/bootstrap_server
Executable file
10
script/bootstrap_server
Executable file
@ -0,0 +1,10 @@
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
echo "Update the submodule to latest version..."
|
||||
git submodule update
|
||||
|
||||
echo "Installing dependencies..."
|
||||
python3 -m pip install --upgrade -r requirements_all.txt
|
||||
|
||||
echo "Installing development dependencies.."
|
||||
python3 -m pip install --upgrade flake8 pylint coveralls pytest pytest-cov
|
@ -1,12 +1,8 @@
|
||||
# Builds the frontend for production
|
||||
|
||||
# If current pwd is scripts, go 1 up.
|
||||
if [ ${PWD##*/} == "scripts" ]; then
|
||||
cd ..
|
||||
fi
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
cd homeassistant/components/frontend/www_static/home-assistant-polymer
|
||||
npm install
|
||||
npm run frontend_prod
|
||||
|
||||
cp bower_components/webcomponentsjs/webcomponents-lite.min.js ..
|
@ -3,10 +3,7 @@
|
||||
# apt-get install cython3 libudev-dev python-sphinx python3-setuptools
|
||||
# pip3 install cython
|
||||
|
||||
# If current pwd is scripts, go 1 up.
|
||||
if [ ${PWD##*/} == "scripts" ]; then
|
||||
cd ..
|
||||
fi
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
if [ ! -d build ]; then
|
||||
mkdir build
|
7
script/cibuild
Executable file
7
script/cibuild
Executable file
@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
# script/cibuild: Setup environment for CI to run tests. This is primarily
|
||||
# designed to run on the continuous integration server.
|
||||
|
||||
script/test coverage
|
||||
coveralls
|
@ -3,10 +3,7 @@
|
||||
# Optional: pass in a timezone as first argument
|
||||
# If not given will attempt to mount /etc/localtime
|
||||
|
||||
# If current pwd is scripts, go 1 up.
|
||||
if [ ${PWD##*/} == "scripts" ]; then
|
||||
cd ..
|
||||
fi
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
docker build -t home-assistant-dev .
|
||||
|
@ -1,10 +1,7 @@
|
||||
# Open a docker that can be used to debug/dev python-openzwave
|
||||
# Pass in a command line argument to build
|
||||
|
||||
# If current pwd is scripts, go 1 up.
|
||||
if [ ${PWD##*/} == "scripts" ]; then
|
||||
cd ..
|
||||
fi
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
if [ $# -gt 0 ]
|
||||
then
|
3
scripts/hass-daemon → script/hass-daemon
Normal file → Executable file
3
scripts/hass-daemon → script/hass-daemon
Normal file → Executable file
@ -34,6 +34,7 @@ RUN_AS="USER"
|
||||
PID_FILE="/var/run/hass.pid"
|
||||
CONFIG_DIR="/var/opt/homeassistant"
|
||||
FLAGS="-v --config $CONFIG_DIR --pid-file $PID_FILE --daemon"
|
||||
REDIRECT="> $CONFIG_DIR/home-assistant.log 2>&1"
|
||||
|
||||
start() {
|
||||
if [ -f $PID_FILE ] && kill -0 $(cat $PID_FILE); then
|
||||
@ -41,7 +42,7 @@ start() {
|
||||
return 1
|
||||
fi
|
||||
echo 'Starting service…' >&2
|
||||
local CMD="$PRE_EXEC hass $FLAGS;"
|
||||
local CMD="$PRE_EXEC hass $FLAGS $REDIRECT;"
|
||||
su -c "$CMD" $RUN_AS
|
||||
echo 'Service started' >&2
|
||||
}
|
9
script/lint
Executable file
9
script/lint
Executable file
@ -0,0 +1,9 @@
|
||||
# Run style checks
|
||||
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
echo "Checking style with flake8..."
|
||||
flake8 homeassistant
|
||||
|
||||
echo "Checking style with pylint..."
|
||||
pylint homeassistant
|
8
script/server
Executable file
8
script/server
Executable file
@ -0,0 +1,8 @@
|
||||
#!/bin/sh
|
||||
|
||||
# script/server: Launch the application and any extra required processes
|
||||
# locally.
|
||||
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
python3 -m homeassistant -c config
|
5
script/setup
Executable file
5
script/setup
Executable file
@ -0,0 +1,5 @@
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
git submodule init
|
||||
script/bootstrap
|
||||
python3 setup.py develop
|
16
script/test
Executable file
16
script/test
Executable file
@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
|
||||
# script/test: Run test suite for application. Optionallly pass in a path to an
|
||||
# individual test file to run a single test.
|
||||
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
script/lint
|
||||
|
||||
echo "Running tests..."
|
||||
|
||||
if [ "$1" = "coverage" ]; then
|
||||
py.test --cov homeassistant tests
|
||||
else
|
||||
py.test tests
|
||||
fi
|
8
script/update
Executable file
8
script/update
Executable file
@ -0,0 +1,8 @@
|
||||
#!/bin/sh
|
||||
|
||||
# script/update: Update application to run for its current checkout.
|
||||
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
git pull
|
||||
git submodule update
|
@ -1,9 +0,0 @@
|
||||
# Run style checks
|
||||
|
||||
# If current pwd is scripts, go 1 up.
|
||||
if [ ${PWD##*/} == "scripts" ]; then
|
||||
cd ..
|
||||
fi
|
||||
|
||||
flake8 homeassistant
|
||||
pylint homeassistant
|
@ -1,10 +0,0 @@
|
||||
# If current pwd is scripts, go 1 up.
|
||||
if [ ${PWD##*/} == "scripts" ]; then
|
||||
cd ..
|
||||
fi
|
||||
|
||||
if [ "$1" = "coverage" ]; then
|
||||
coverage run -m unittest discover tests
|
||||
else
|
||||
python3 -m unittest discover tests
|
||||
fi
|
@ -1,6 +0,0 @@
|
||||
echo "The update script has been deprecated since Home Assistant v0.7"
|
||||
echo
|
||||
echo "Home Assistant is now distributed via PyPi and can be installed and"
|
||||
echo "upgraded by running: pip3 install --upgrade homeassistant"
|
||||
echo
|
||||
echo "If you are developing a new feature for Home Assistant, run: git pull"
|
@ -285,9 +285,9 @@ class TestAutomationTime(unittest.TestCase):
|
||||
automation.DOMAIN: {
|
||||
'trigger': {
|
||||
'platform': 'time',
|
||||
'hours': 0,
|
||||
'minutes': 0,
|
||||
'seconds': 0,
|
||||
'hours': 1,
|
||||
'minutes': 2,
|
||||
'seconds': 3,
|
||||
},
|
||||
'action': {
|
||||
'execute_service': 'test.automation'
|
||||
@ -296,7 +296,7 @@ class TestAutomationTime(unittest.TestCase):
|
||||
}))
|
||||
|
||||
fire_time_changed(self.hass, dt_util.utcnow().replace(
|
||||
hour=0, minute=0, second=0))
|
||||
hour=1, minute=2, second=3))
|
||||
|
||||
self.hass.pool.block_till_done()
|
||||
self.assertEqual(1, len(self.calls))
|
||||
@ -320,6 +320,30 @@ class TestAutomationTime(unittest.TestCase):
|
||||
self.hass.pool.block_till_done()
|
||||
self.assertEqual(1, len(self.calls))
|
||||
|
||||
@patch('homeassistant.components.automation.time._LOGGER.error')
|
||||
def test_if_not_fires_using_wrong_after(self, mock_error):
|
||||
""" YAML translates time values to total seconds. This should break the
|
||||
before rule. """
|
||||
self.assertTrue(automation.setup(self.hass, {
|
||||
automation.DOMAIN: {
|
||||
'trigger': {
|
||||
'platform': 'time',
|
||||
'after': 3605,
|
||||
# Total seconds. Hour = 3600 second
|
||||
},
|
||||
'action': {
|
||||
'execute_service': 'test.automation'
|
||||
}
|
||||
}
|
||||
}))
|
||||
|
||||
fire_time_changed(self.hass, dt_util.utcnow().replace(
|
||||
hour=1, minute=0, second=5))
|
||||
|
||||
self.hass.pool.block_till_done()
|
||||
self.assertEqual(0, len(self.calls))
|
||||
self.assertEqual(2, mock_error.call_count)
|
||||
|
||||
def test_if_action_before(self):
|
||||
automation.setup(self.hass, {
|
||||
automation.DOMAIN: {
|
||||
|
Loading…
x
Reference in New Issue
Block a user