fixed merge conflict

This commit is contained in:
happyleavesaoc 2015-10-10 16:53:55 -04:00
commit 7ca21f577d
20 changed files with 257 additions and 263 deletions

View File

@ -66,6 +66,7 @@ omit =
homeassistant/components/notify/slack.py
homeassistant/components/notify/smtp.py
homeassistant/components/notify/syslog.py
homeassistant/components/notify/telegram.py
homeassistant/components/notify/xmpp.py
homeassistant/components/sensor/arest.py
homeassistant/components/sensor/bitcoin.py

View File

@ -1,2 +1,5 @@
recursive-exclude tests *
recursive-include homeassistant services.yaml
include README.md
include LICENSE
graft homeassistant
prune homeassistant/components/frontend/www_static/home-assistant-polymer
recursive-exclude * *.py[co]

View File

@ -16,10 +16,12 @@ Check out [the website](https://home-assistant.io) for [a demo][demo], installat
Examples of devices it can interface it:
* Monitoring connected devices to a wireless router: [OpenWrt](https://openwrt.org/), [Tomato](http://www.polarcloud.com/tomato), [Netgear](http://netgear.com), [DD-WRT](http://www.dd-wrt.com/site/index), [TPLink](http://www.tp-link.us/), [ASUSWRT](http://event.asus.com/2013/nw/ASUSWRT/) and any SNMP capable WAP/WRT
* Monitoring connected devices to a wireless router: [OpenWrt](https://openwrt.org/), [Tomato](http://www.polarcloud.com/tomato), [Netgear](http://netgear.com), [DD-WRT](http://www.dd-wrt.com/site/index), [TPLink](http://www.tp-link.us/), [ASUSWRT](http://event.asus.com/2013/nw/ASUSWRT/) and any SNMP capable Linksys WAP/WRT
*
* [Philips Hue](http://meethue.com) lights, [WeMo](http://www.belkin.com/us/Products/home-automation/c/wemo-home-automation/) switches, [Edimax](http://www.edimax.com/) switches, [Efergy](https://efergy.com) energy monitoring, RFXtrx sensors, and [Tellstick](http://www.telldus.se/products/tellstick) devices and sensors
* [Google Chromecasts](http://www.google.com/intl/en/chrome/devices/chromecast), [Music Player Daemon](http://www.musicpd.org/), [Logitech Squeezebox](https://en.wikipedia.org/wiki/Squeezebox_%28network_music_player%29), [Kodi (XBMC)](http://kodi.tv/), iTunes (by way of [itunes-api](https://github.com/maddox/itunes-api)), and Amazon Fire TV (by way of [python-firetv](https://github.com/happyleavesaoc/python-firetv))
* Support for [ISY994](https://www.universal-devices.com/residential/isy994i-series/) (Insteon and X10 devices), [Z-Wave](http://www.z-wave.com/), [Nest Thermostats](https://nest.com/), [Arduino](https://www.arduino.cc/), [Raspberry Pi](https://www.raspberrypi.org/), and [Modbus](http://www.modbus.org/)
* [Google Chromecasts](http://www.google.com/intl/en/chrome/devices/chromecast), [Music Player Daemon](http://www.musicpd.org/), [Logitech Squeezebox](https://en.wikipedia.org/wiki/Squeezebox_%28network_music_player%29), [Plex](https://plex.tv/), [Kodi (XBMC)](http://kodi.tv/), iTunes (by way of [itunes-api](https://github.com/maddox/itunes-api)), and Amazon Fire TV (by way of [python-firetv](https://github.com/happyleavesaoc/python-firetv))
* Support for [ISY994](https://www.universal-devices.com/residential/isy994i-series/) (Insteon and X10 devices), [Z-Wave](http://www.z-wave.com/), [Nest Thermostats](https://nest.com/), [RFXtrx](http://www.rfxcom.com/), [Arduino](https://www.arduino.cc/), [Raspberry Pi](https://www.raspberrypi.org/), and [Modbus](http://www.modbus.org/)
* Interaction with [IFTTT](https://ifttt.com/)
* Integrate data from the [Bitcoin](https://bitcoin.org) network, meteorological data from [OpenWeatherMap](http://openweathermap.org/) and [Forecast.io](https://forecast.io/), [Transmission](http://www.transmissionbt.com/), or [SABnzbd](http://sabnzbd.org).
* [See full list of supported devices](https://home-assistant.io/components/)
@ -29,8 +31,8 @@ Built home automation on top of your devices:
* Turn on the lights when people get home after sun set
* Turn on lights slowly during sun set to compensate for less light
* Turn off all lights and devices when everybody leaves the house
* Offers a [REST API](https://home-assistant.io/developers/api.html) and can interface with MQTT for easy integration with other projects
* Allow sending notifications using [Instapush](https://instapush.im), [Notify My Android (NMA)](http://www.notifymyandroid.com/), [PushBullet](https://www.pushbullet.com/), [PushOver](https://pushover.net/), [Slack](https://slack.com/), and [Jabber (XMPP)](http://xmpp.org)
* Offers a [REST API](https://home-assistant.io/developers/api.html) and can interface with MQTT for easy integration with other projects like [OwnTracks](http://owntracks.org/)
* Allow sending notifications using [Instapush](https://instapush.im), [Notify My Android (NMA)](http://www.notifymyandroid.com/), [PushBullet](https://www.pushbullet.com/), [PushOver](https://pushover.net/), [Slack](https://slack.com/), [Telegram](https://telegram.org/), and [Jabber (XMPP)](http://xmpp.org)
The system is built modular so support for other devices or actions can be implemented easily. See also the [section on architecture](https://home-assistant.io/developers/architecture.html) and the [section on creating your own components](https://home-assistant.io/developers/creating_components.html).

View File

@ -114,6 +114,8 @@ def setup(hass, config):
os.remove(csv_path)
conf = config.get(DOMAIN, {})
if isinstance(conf, list):
conf = conf[0]
consider_home = timedelta(
seconds=util.convert(conf.get(CONF_CONSIDER_HOME), int,
DEFAULT_CONSIDER_HOME))

View File

@ -46,6 +46,7 @@ MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)
_LOGGER = logging.getLogger(__name__)
_DDWRT_DATA_REGEX = re.compile(r'\{(\w+)::([^\}]*)\}')
_MAC_REGEX = re.compile(r'(([0-9A-Fa-f]{1,2}\:){5}[0-9A-Fa-f]{1,2})')
# pylint: disable=unused-argument
@ -77,7 +78,7 @@ class DdWrtDeviceScanner(object):
self.last_results = {}
self.mac2name = None
self.mac2name = {}
# Test the router is accessible
url = 'http://{}/Status_Wireless.live.asp'.format(self.host)
@ -98,30 +99,33 @@ class DdWrtDeviceScanner(object):
with self.lock:
# if not initialised and not already scanned and not found
if self.mac2name is None or device not in self.mac2name:
if device not in self.mac2name:
url = 'http://{}/Status_Lan.live.asp'.format(self.host)
data = self.get_ddwrt_data(url)
if not data:
return
return None
dhcp_leases = data.get('dhcp_leases', None)
if dhcp_leases:
# remove leading and trailing single quotes
cleaned_str = dhcp_leases.strip().strip('"')
elements = cleaned_str.split('","')
num_clients = int(len(elements)/5)
self.mac2name = {}
for idx in range(0, num_clients):
# this is stupid but the data is a single array
# every 5 elements represents one hosts, the MAC
# is the third element and the name is the first
mac_index = (idx * 5) + 2
if mac_index < len(elements):
mac = elements[mac_index]
self.mac2name[mac] = elements[idx * 5]
return self.mac2name.get(device, None)
if not dhcp_leases:
return None
# remove leading and trailing single quotes
cleaned_str = dhcp_leases.strip().strip('"')
elements = cleaned_str.split('","')
num_clients = int(len(elements)/5)
self.mac2name = {}
for idx in range(0, num_clients):
# this is stupid but the data is a single array
# every 5 elements represents one hosts, the MAC
# is the third element and the name is the first
mac_index = (idx * 5) + 2
if mac_index < len(elements):
mac = elements[mac_index]
self.mac2name[mac] = elements[idx * 5]
return self.mac2name.get(device)
@Throttle(MIN_TIME_BETWEEN_SCANS)
def _update_info(self):
@ -141,29 +145,25 @@ class DdWrtDeviceScanner(object):
if not data:
return False
if data:
self.last_results = []
active_clients = data.get('active_wireless', None)
if active_clients:
# This is really lame, instead of using JSON the DD-WRT UI
# uses its own data format for some reason and then
# regex's out values so I guess I have to do the same,
# LAME!!!
self.last_results = []
# remove leading and trailing single quotes
clean_str = active_clients.strip().strip("'")
elements = clean_str.split("','")
active_clients = data.get('active_wireless', None)
if not active_clients:
return False
num_clients = int(len(elements)/9)
for idx in range(0, num_clients):
# get every 9th element which is the MAC address
index = idx * 9
if index < len(elements):
self.last_results.append(elements[index])
# This is really lame, instead of using JSON the DD-WRT UI
# uses its own data format for some reason and then
# regex's out values so I guess I have to do the same,
# LAME!!!
return True
# remove leading and trailing single quotes
clean_str = active_clients.strip().strip("'")
elements = clean_str.split("','")
return False
self.last_results.extend(item for item in elements
if _MAC_REGEX.match(item))
return True
def get_ddwrt_data(self, url):
""" Retrieve data from DD-WRT and return parsed result. """

View File

@ -117,15 +117,18 @@ class NmapDeviceScanner(object):
scanner = PortScanner()
options = "-F --host-timeout 5"
exclude_targets = set()
if self.home_interval:
now = dt_util.now()
for host in self.last_results:
if host.last_update + self.home_interval > now:
exclude_targets.add(host)
if len(exclude_targets) > 0:
target_list = [t.ip for t in exclude_targets]
options += " --exclude {}".format(",".join(target_list))
boundary = dt_util.now() - self.home_interval
last_results = [device for device in self.last_results
if device.last_update > boundary]
if last_results:
# Pylint is confused here.
# pylint: disable=no-member
options += " --exclude {}".format(",".join(device.ip for device
in last_results))
else:
last_results = []
try:
result = scanner.scan(hosts=self.hosts, arguments=options)
@ -133,18 +136,17 @@ class NmapDeviceScanner(object):
return False
now = dt_util.now()
self.last_results = []
for ipv4, info in result['scan'].items():
if info['status']['state'] != 'up':
continue
name = info['hostnames'][0] if info['hostnames'] else ipv4
name = info['hostnames'][0]['name'] if info['hostnames'] else ipv4
# Mac address only returned if nmap ran as root
mac = info['addresses'].get('mac') or _arp(ipv4)
if mac is None:
continue
device = Device(mac.upper(), name, ipv4, now)
self.last_results.append(device)
self.last_results.extend(exclude_targets)
last_results.append(Device(mac.upper(), name, ipv4, now))
self.last_results = last_results
_LOGGER.info("nmap scan successful")
return True

View File

@ -1,34 +1,11 @@
"""
homeassistant.components.media_player.plex
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Provides an interface to the Plex API.
Provides an interface to the Plex API
Configuration:
To use Plex add something like this to your configuration:
media_player:
platform: plex
name: plex_server
user: plex
password: my_secure_password
Variables:
name
*Required
The name of the backend device (Under Plex Media Server > settings > server).
user
*Required
The Plex username
password
*Required
The Plex password
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/media_player.plex.html
"""
import logging
from datetime import timedelta
@ -49,10 +26,8 @@ _LOGGER = logging.getLogger(__name__)
SUPPORT_PLEX = SUPPORT_PAUSE | SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK
# pylint: disable=abstract-method
# pylint: disable=unused-argument
# pylint: disable=abstract-method, unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Sets up the plex platform. """
from plexapi.myplex import MyPlexUser
@ -68,7 +43,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
@util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS)
def update_devices():
""" Updates the devices objects """
""" Updates the devices objects. """
try:
devices = plexuser.devices()
except BadRequest:
@ -94,7 +69,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
@util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS)
def update_sessions():
""" Updates the sessions objects """
""" Updates the sessions objects. """
try:
sessions = plexserver.sessions()
except BadRequest:
@ -113,7 +88,6 @@ class PlexClient(MediaPlayerDevice):
""" Represents a Plex device. """
# pylint: disable=too-many-public-methods
def __init__(self, device, plex_sessions, update_devices, update_sessions):
self.plex_sessions = plex_sessions
self.update_devices = update_devices
@ -121,12 +95,12 @@ class PlexClient(MediaPlayerDevice):
self.set_device(device)
def set_device(self, device):
""" Sets the device property """
""" Sets the device property. """
self.device = device
@property
def session(self):
""" Returns the session, if any """
""" Returns the session, if any. """
if self.device.clientIdentifier not in self.plex_sessions:
return None
@ -196,21 +170,21 @@ class PlexClient(MediaPlayerDevice):
@property
def media_season(self):
""" Season of curent playing media. (TV Show only) """
""" Season of curent playing media (TV Show only). """
from plexapi.video import Show
if isinstance(self.session, Show):
return self.session.seasons()[0].index
@property
def media_series_title(self):
""" Series title of current playing media. (TV Show only)"""
""" Series title of current playing media (TV Show only). """
from plexapi.video import Show
if isinstance(self.session, Show):
return self.session.grandparentTitle
@property
def media_episode(self):
""" Episode of current playing media. (TV Show only) """
""" Episode of current playing media (TV Show only). """
from plexapi.video import Show
if isinstance(self.session, Show):
return self.session.index

View File

@ -0,0 +1,66 @@
"""
homeassistant.components.notify.telegram
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Telegram platform for notify component.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/notify.telegram.html
"""
import logging
import urllib
from homeassistant.helpers import validate_config
from homeassistant.components.notify import (
DOMAIN, ATTR_TITLE, BaseNotificationService)
from homeassistant.const import CONF_API_KEY
_LOGGER = logging.getLogger(__name__)
REQUIREMENTS = ['python-telegram-bot==2.8.7']
def get_service(hass, config):
""" Get the Telegram notification service. """
if not validate_config(config,
{DOMAIN: [CONF_API_KEY, 'chat_id']},
_LOGGER):
return None
try:
import telegram
except ImportError:
_LOGGER.exception(
"Unable to import python-telegram-bot. "
"Did you maybe not install the 'python-telegram-bot' package?")
return None
try:
bot = telegram.Bot(token=config[DOMAIN][CONF_API_KEY])
username = bot.getMe()['username']
_LOGGER.info("Telegram bot is' %s'", username)
except urllib.error.HTTPError:
_LOGGER.error("Please check your access token.")
return None
return TelegramNotificationService(
config[DOMAIN][CONF_API_KEY],
config[DOMAIN]['chat_id'])
# pylint: disable=too-few-public-methods
class TelegramNotificationService(BaseNotificationService):
""" Implements notification service for Telegram. """
def __init__(self, api_key, chat_id):
import telegram
self._api_key = api_key
self._chat_id = chat_id
self.bot = telegram.Bot(token=self._api_key)
def send_message(self, message="", **kwargs):
""" Send a message to a user. """
title = kwargs.get(ATTR_TITLE)
self.bot.sendMessage(chat_id=self._chat_id,
text=title + " " + message)

View File

@ -1,20 +1,10 @@
"""
homeassistant.components.rfxtrx
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Connects Home Assistant to a RFXtrx device.
Configuration:
To use Rfxtrx device you will need to add the following to your
configuration.yaml file.
rfxtrx:
device: /dev/serial/by-id/usb-RFXCOM_RFXtrx433_A1YVC1P0-if00-port0
*Optional*
debug: True
Provides support for RFXtrx components.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/rfxtrx.html
"""
import logging
from homeassistant.util import slugify

View File

@ -3,51 +3,11 @@ homeassistant.components.sensor.arest
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The arest sensor will consume an exposed aREST API of a device.
Configuration:
To use the arest sensor you will need to add something like the following
to your configuration.yaml file.
sensor:
platform: arest
resource: http://IP_ADDRESS
monitored_variables:
- name: temperature
unit: '°C'
- name: humidity
unit: '%'
Variables:
resource:
*Required
IP address of the device that is exposing an aREST API.
These are the variables for the monitored_variables array:
name
*Required
The name of the variable you wish to monitor.
unit
*Optional
Defines the units of measurement of the sensor, if any.
Details for the API: http://arest.io
Format of a default JSON response by aREST:
{
"variables":{
"temperature":21,
"humidity":89
},
"id":"device008",
"name":"Bedroom",
"connected":true
}
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/sensor.arest.html
"""
import logging
from requests import get, exceptions
import requests
from datetime import timedelta
from homeassistant.util import Throttle
@ -58,36 +18,42 @@ _LOGGER = logging.getLogger(__name__)
# Return cached results if last scan was less then this time ago
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=60)
CONF_RESOURCE = 'resource'
CONF_MONITORED_VARIABLES = 'monitored_variables'
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Get the aREST sensor. """
resource = config.get('resource', None)
resource = config.get(CONF_RESOURCE)
var_conf = config.get(CONF_MONITORED_VARIABLES)
if None in (resource, var_conf):
_LOGGER.error('Not all required config keys present: %s',
', '.join((CONF_RESOURCE, CONF_MONITORED_VARIABLES)))
return False
try:
response = get(resource, timeout=10)
except exceptions.MissingSchema:
response = requests.get(resource, timeout=10).json()
except requests.exceptions.MissingSchema:
_LOGGER.error("Missing resource or schema in configuration. "
"Add http:// to your URL.")
return False
except exceptions.ConnectionError:
except requests.exceptions.ConnectionError:
_LOGGER.error("No route to device. "
"Please check the IP address in the configuration file.")
return False
rest = ArestData(resource)
arest = ArestData(resource)
dev = []
for variable in config['monitored_variables']:
if 'unit' not in variable:
variable['unit'] = ' '
if variable['name'] not in response.json()['variables']:
if variable['name'] not in response['variables']:
_LOGGER.error('Variable: "%s" does not exist', variable['name'])
else:
dev.append(ArestSensor(rest,
response.json()['name'],
variable['name'],
variable['unit']))
continue
dev.append(ArestSensor(arest, response['name'], variable['name'],
variable.get('unit')))
add_devices(dev)
@ -95,8 +61,8 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
class ArestSensor(Entity):
""" Implements an aREST sensor. """
def __init__(self, rest, location, variable, unit_of_measurement):
self.rest = rest
def __init__(self, arest, location, variable, unit_of_measurement):
self.arest = arest
self._name = '{} {}'.format(location.title(), variable.title())
self._variable = variable
self._state = 'n/a'
@ -116,17 +82,16 @@ class ArestSensor(Entity):
@property
def state(self):
""" Returns the state of the device. """
return self._state
def update(self):
""" Gets the latest data from aREST API and updates the state. """
self.rest.update()
values = self.rest.data
values = self.arest.data
if 'error' in values:
self._state = values['error']
return values['error']
else:
self._state = values[self._variable]
return values.get(self._variable, 'n/a')
def update(self):
""" Gets the latest data from aREST API. """
self.arest.update()
# pylint: disable=too-few-public-methods
@ -135,16 +100,14 @@ class ArestData(object):
def __init__(self, resource):
self.resource = resource
self.data = dict()
self.data = {}
@Throttle(MIN_TIME_BETWEEN_UPDATES)
def update(self):
""" Gets the latest data from aREST device. """
try:
response = get(self.resource, timeout=10)
if 'error' in self.data:
del self.data['error']
response = requests.get(self.resource, timeout=10)
self.data = response.json()['variables']
except exceptions.ConnectionError:
except requests.exceptions.ConnectionError:
_LOGGER.error("No route to device. Is device offline?")
self.data['error'] = 'n/a'
self.data = {'error': 'error fetching'}

View File

@ -3,38 +3,9 @@ homeassistant.components.switch.arest
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The arest switch can control the digital pins of a device running with the
aREST RESTful framework for Arduino, the ESP8266, and the Raspberry Pi.
Only tested with Arduino boards so far.
Configuration:
To use the arest switch you will need to add something like the following
to your configuration.yaml file.
sensor:
platform: arest
resource: http://IP_ADDRESS
pins:
11:
name: Fan Office
12:
name: Light Desk
Variables:
resource:
*Required
IP address of the device that is exposing an aREST API.
pins:
The number of the digital pin to switch.
These are the variables for the pins array:
name
*Required
The name for the pin that will be used in the frontend.
Details for the API: http://arest.io
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/switch.arest.html
"""
import logging
from requests import get, exceptions

View File

@ -1,7 +1,7 @@
# coding: utf-8
""" Constants used by Home Assistant components. """
__version__ = "0.7.5dev0"
__version__ = "0.7.6.dev0"
# Can be used to specify a catch all when registering state or event listeners.
MATCH_ALL = '*'

View File

@ -59,14 +59,14 @@ def reproduce_state(hass, states, blocking=False):
state.entity_id)
continue
if state.domain == 'media_player' and state.state == STATE_PAUSED:
service = SERVICE_MEDIA_PAUSE
elif state.domain == 'media_player' and state.state == STATE_PLAYING:
service = SERVICE_MEDIA_PLAY
elif state.domain == 'media_player' and state.attributes and \
if state.domain == 'media_player' and state.attributes and \
'media_type' in state.attributes and \
'media_id' in state.attributes:
service = SERVICE_PLAY_MEDIA
elif state.domain == 'media_player' and state.state == STATE_PAUSED:
service = SERVICE_MEDIA_PAUSE
elif state.domain == 'media_player' and state.state == STATE_PLAYING:
service = SERVICE_MEDIA_PLAY
elif state.state == STATE_ON:
service = SERVICE_TURN_ON
elif state.state == STATE_OFF:

View File

@ -233,35 +233,42 @@ class Throttle(object):
self.limit_no_throttle = limit_no_throttle
def __call__(self, method):
lock = threading.Lock()
if self.limit_no_throttle is not None:
method = Throttle(self.limit_no_throttle)(method)
# We want to be able to differentiate between function and method calls
# All methods have the classname in their qualname seperated by a '.'
# Functions have a '.' in their qualname if defined inline, but will
# be prefixed by '.<locals>.' so we strip that out.
is_func = '.' not in method.__qualname__.split('.<locals>.')[-1]
@wraps(method)
def wrapper(*args, **kwargs):
"""
Wrapper that allows wrapped to be called only once per min_time.
If we cannot acquire the lock, it is running so return None.
"""
if not lock.acquire(False):
# pylint: disable=protected-access
host = wrapper if is_func else args[0]
if not hasattr(host, '_throttle_lock'):
host._throttle_lock = threading.Lock()
if not host._throttle_lock.acquire(False):
return None
last_call = getattr(host, '_throttle_last_call', None)
# Check if method is never called or no_throttle is given
force = not last_call or kwargs.pop('no_throttle', False)
try:
last_call = wrapper.last_call
# Check if method is never called or no_throttle is given
force = not last_call or kwargs.pop('no_throttle', False)
if force or utcnow() - last_call > self.min_time:
result = method(*args, **kwargs)
wrapper.last_call = utcnow()
host._throttle_last_call = utcnow()
return result
else:
return None
finally:
lock.release()
wrapper.last_call = None
host._throttle_lock.release()
return wrapper

View File

@ -10,77 +10,77 @@ vincenty==0.1.2
# Sun (sun)
astral==0.8.1
# Philips Hue library (lights.hue)
# Philips Hue (lights.hue)
phue==0.8
# Limitlessled/Easybulb/Milight library (lights.limitlessled)
# Limitlessled/Easybulb/Milight (lights.limitlessled)
ledcontroller==1.1.0
# Chromecast bindings (media_player.cast)
# Chromecast (media_player.cast)
pychromecast==0.6.12
# Keyboard (keyboard)
pyuserinput==0.1.9
# Tellstick bindings (*.tellstick)
# Tellstick (*.tellstick)
tellcore-py==1.1.2
# Nmap bindings (device_tracker.nmap)
# Nmap (device_tracker.nmap)
python-nmap==0.4.3
# PushBullet bindings (notify.pushbullet)
# PushBullet (notify.pushbullet)
pushbullet.py==0.7.1
# Nest Thermostat bindings (thermostat.nest)
# Nest Thermostat (thermostat.nest)
python-nest==2.6.0
# Z-Wave (*.zwave)
pydispatcher==2.0.5
# ISY994 bindings (*.isy994)
# ISY994 (isy994)
PyISY==1.0.5
# PSutil (sensor.systemmonitor)
psutil==3.0.0
# Pushover bindings (notify.pushover)
# Pushover (notify.pushover)
python-pushover==0.2
# Transmission Torrent Client (*.transmission)
transmissionrpc==0.11
# OpenWeatherMap Web API (sensor.openweathermap)
# OpenWeatherMap (sensor.openweathermap)
pyowm==2.2.1
# XMPP Bindings (notify.xmpp)
# XMPP (notify.xmpp)
sleekxmpp==1.3.1
dnspython3==1.12.0
# Blockchain (sensor.bitcoin)
blockchain==1.1.2
# MPD Bindings (media_player.mpd)
# Music Player Daemon (media_player.mpd)
python-mpd2==0.5.4
# Hikvision (switch.hikvisioncam)
hikvision==0.4
# console log coloring
# Console log coloring
colorlog==2.6.0
# JSON-RPC interface (media_player.kodi)
jsonrpc-requests==0.1
# Forecast.io Bindings (sensor.forecast)
# Forecast.io (sensor.forecast)
python-forecastio==1.3.3
# Firmata Bindings (*.arduino)
# Firmata (*.arduino)
PyMata==2.07a
# Rfxtrx sensor (sensor.rfxtrx)
# Rfxtrx (rfxtrx)
https://github.com/Danielhiversen/pyRFXtrx/archive/ec7a1aaddf8270db6e5da1c13d58c1547effd7cf.zip#RFXtrx==0.15
# Mysensors
# Mysensors (sensor.mysensors)
https://github.com/theolind/pymysensors/archive/35b87d880147a34107da0d40cb815d75e6cb4af7.zip#pymysensors==0.2
# Netgear (device_tracker.netgear)
@ -101,18 +101,18 @@ slacker==0.6.8
# Temper sensors (sensor.temper)
https://github.com/rkabadi/temper-python/archive/3dbdaf2d87b8db9a3cd6e5585fc704537dd2d09b.zip#temperusb==1.2.3
# PyEdimax
# PyEdimax (switch.edimax)
https://github.com/rkabadi/pyedimax/archive/365301ce3ff26129a7910c501ead09ea625f3700.zip#pyedimax==0.1
# RPI-GPIO platform (*.rpi_gpio)
# Uncomment for Raspberry Pi
# RPi.GPIO==0.5.11
# Adafruit temperature/humidity sensor
# uncomment on a Raspberry Pi / Beaglebone
# Adafruit temperature/humidity sensor (sensor.dht)
# Uncomment on a Raspberry Pi / Beaglebone
# http://github.com/mala-zaba/Adafruit_Python_DHT/archive/4101340de8d2457dd194bca1e8d11cbfc237e919.zip#Adafruit_DHT==1.1.0
# PAHO MQTT Binding (mqtt)
# PAHO MQTT (mqtt)
paho-mqtt==1.1
# PyModbus (modbus)
@ -121,25 +121,26 @@ https://github.com/bashwork/pymodbus/archive/d7fc4f1cc975631e0a9011390e8017f64b6
# Verisure (verisure)
https://github.com/persandstrom/python-verisure/archive/9873c4527f01b1ba1f72ae60f7f35854390d59be.zip#python-verisure==0.2.6
# Python tools for interacting with IFTTT Maker Channel (ifttt)
# IFTTT Maker Channel (ifttt)
pyfttt==0.3
# sensor.sabnzbd
# SABnzbd (sensor.sabnzbd)
https://github.com/balloob/home-assistant-nzb-clients/archive/616cad59154092599278661af17e2a9f2cf5e2a9.zip#python-sabnzbd==0.1
# switch.vera
# sensor.vera
# light.vera
# Vera (*.vera)
https://github.com/balloob/home-assistant-vera-api/archive/a8f823066ead6c7da6fb5e7abaf16fef62e63364.zip#python-vera==0.1
# Sonos bindings (media_player.sonos)
# Sonos (media_player.sonos)
SoCo==0.11.1
# PlexAPI (media_player.plex)
https://github.com/adrienbrault/python-plexapi/archive/df2d0847e801d6d5cda920326d693cf75f304f1a.zip#python-plexapi==1.0.2
# python-pysnmp (device_tracker.snmp)
# SNMP (device_tracker.snmp)
pysnmp==4.2.5
# Blinkstick
# Blinkstick (light.blinksticklight)
blinkstick==1.1.7
# Telegram (notify.telegram)
python-telegram-bot==2.8.7

View File

@ -9,11 +9,12 @@ DOWNLOAD_URL = ('https://github.com/balloob/home-assistant/archive/'
PACKAGES = find_packages(exclude=['tests', 'tests.*'])
PACKAGE_DATA = \
{'homeassistant.components.frontend': ['index.html.template'],
'homeassistant.components.frontend.www_static': ['*.*'],
'homeassistant.components.frontend.www_static.images': ['*.*'],
'homeassistant.startup': ['*.*']}
# PACKAGE_DATA = \
# {'homeassistant.components.frontend': ['index.html.template'],
# 'homeassistant.components.frontend.www_static': ['*.*'],
# 'homeassistant.components.frontend.www_static.images': ['*.*'],
# 'homeassistant.components.mqtt': ['*.crt'],
# 'homeassistant.startup': ['*.*']}
REQUIRES = [
'requests>=2,<3',
@ -23,6 +24,7 @@ REQUIRES = [
'vincenty==0.1.2'
]
# package_data=PACKAGE_DATA,
setup(
name=PACKAGE_NAME,
version=__version__,
@ -34,7 +36,6 @@ setup(
description='Open-source home automation platform running on Python 3.',
packages=PACKAGES,
include_package_data=True,
package_data=PACKAGE_DATA,
zip_safe=False,
platforms='any',
install_requires=REQUIRES,

View File

@ -218,3 +218,14 @@ class TestUtil(unittest.TestCase):
self.assertEqual(3, len(calls1))
self.assertEqual(2, len(calls2))
def test_throttle_per_instance(self):
""" Test that the throttle method is done per instance of a class. """
class Tester(object):
@util.Throttle(timedelta(seconds=1))
def hello(self):
return True
self.assertTrue(Tester().hello())
self.assertTrue(Tester().hello())