mirror of
https://github.com/home-assistant/core.git
synced 2025-07-16 17:57:11 +00:00
removed old config example to fix merge conflict
This commit is contained in:
commit
ba58c080e3
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,6 +1,7 @@
|
|||||||
config/*
|
config/*
|
||||||
!config/home-assistant.conf.default
|
!config/home-assistant.conf.default
|
||||||
homeassistant/components/frontend/www_static/polymer/bower_components/*
|
homeassistant/components/frontend/www_static/polymer/bower_components/*
|
||||||
|
__pycache__
|
||||||
|
|
||||||
# There is not a better solution afaik..
|
# There is not a better solution afaik..
|
||||||
!config/custom_components
|
!config/custom_components
|
||||||
@ -62,4 +63,4 @@ nosetests.xml
|
|||||||
# Mr Developer
|
# Mr Developer
|
||||||
.mr.developer.cfg
|
.mr.developer.cfg
|
||||||
.project
|
.project
|
||||||
.pydevproject
|
.pydevproject
|
||||||
|
@ -3,4 +3,10 @@ MAINTAINER Paulus Schoutsen <Paulus@PaulusSchoutsen.nl>
|
|||||||
|
|
||||||
VOLUME /config
|
VOLUME /config
|
||||||
|
|
||||||
|
RUN apt-get update && \
|
||||||
|
apt-get install -y cython3 libudev-dev python-sphinx python3-setuptools mercurial && \
|
||||||
|
apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && \
|
||||||
|
pip3 install cython && \
|
||||||
|
scripts/build_python_openzwave
|
||||||
|
|
||||||
CMD [ "python", "-m", "homeassistant", "--config", "/config" ]
|
CMD [ "python", "-m", "homeassistant", "--config", "/config" ]
|
||||||
|
104
config/configuration.yaml.example
Normal file
104
config/configuration.yaml.example
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
homeassistant:
|
||||||
|
# Location required to calculate the time the sun rises and sets
|
||||||
|
latitude: 32.87336
|
||||||
|
longitude: 117.22743
|
||||||
|
|
||||||
|
http:
|
||||||
|
api_password: mypass
|
||||||
|
# Set to 1 to enable development mode
|
||||||
|
# development: 1
|
||||||
|
|
||||||
|
light:
|
||||||
|
# platform: hue
|
||||||
|
|
||||||
|
wink:
|
||||||
|
# Get your token at https://winkbearertoken.appspot.com
|
||||||
|
access_token: 'YOUR_TOKEN'
|
||||||
|
|
||||||
|
device_tracker:
|
||||||
|
# The following types are available: netgear, tomato, luci, nmap_tracker
|
||||||
|
platform: netgear
|
||||||
|
host: 192.168.1.1
|
||||||
|
username: admin
|
||||||
|
password: PASSWORD
|
||||||
|
# http_id is needed for Tomato routers only
|
||||||
|
# http_id: ABCDEFGHH
|
||||||
|
# For nmap_tracker, only the IP addresses to scan are needed:
|
||||||
|
# hosts: 192.168.1.1/24 # netmask prefix notation or
|
||||||
|
# hosts: 192.168.1.1-255 # address range
|
||||||
|
|
||||||
|
chromecast:
|
||||||
|
|
||||||
|
switch:
|
||||||
|
platform: wemo
|
||||||
|
|
||||||
|
thermostat:
|
||||||
|
platform: nest
|
||||||
|
# Required: username and password that are used to login to the Nest thermostat.
|
||||||
|
username: myemail@mydomain.com
|
||||||
|
password: mypassword
|
||||||
|
|
||||||
|
downloader:
|
||||||
|
download_dir: downloads
|
||||||
|
|
||||||
|
notify:
|
||||||
|
platform: pushbullet
|
||||||
|
api_key: ABCDEFGHJKLMNOPQRSTUVXYZ
|
||||||
|
|
||||||
|
device_sun_light_trigger:
|
||||||
|
# Optional: specify a specific light/group of lights that has to be turned on
|
||||||
|
light_group: group.living_room
|
||||||
|
# Optional: specify which light profile to use when turning lights on
|
||||||
|
light_profile: relax
|
||||||
|
# Optional: disable lights being turned off when everybody leaves the house
|
||||||
|
# disable_turn_off: 1
|
||||||
|
|
||||||
|
# A comma seperated list of states that have to be tracked as a single group
|
||||||
|
# Grouped states should share the same type of states (ON/OFF or HOME/NOT_HOME)
|
||||||
|
group:
|
||||||
|
living_room:
|
||||||
|
- light.Bowl
|
||||||
|
- light.Ceiling
|
||||||
|
- light.TV_back_light
|
||||||
|
children:
|
||||||
|
- device_tracker.child_1
|
||||||
|
- device_tracker.child_2
|
||||||
|
|
||||||
|
process:
|
||||||
|
# items are which processes to look for: <entity_id>: <search string within ps>
|
||||||
|
xbmc: XBMC.App
|
||||||
|
|
||||||
|
example:
|
||||||
|
|
||||||
|
simple_alarm:
|
||||||
|
# Which light/light group has to flash when a known device comes home
|
||||||
|
known_light: light.Bowl
|
||||||
|
# Which light/light group has to flash red when light turns on while no one home
|
||||||
|
unknown_light: group.living_room
|
||||||
|
|
||||||
|
browser:
|
||||||
|
|
||||||
|
keyboard:
|
||||||
|
|
||||||
|
automation:
|
||||||
|
platform: state
|
||||||
|
alias: Sun starts shining
|
||||||
|
|
||||||
|
state_entity_id: sun.sun
|
||||||
|
# Next two are optional, omit to match all
|
||||||
|
state_from: below_horizon
|
||||||
|
state_to: above_horizon
|
||||||
|
|
||||||
|
execute_service: light.turn_off
|
||||||
|
service_entity_id: group.living_room
|
||||||
|
|
||||||
|
automation 2:
|
||||||
|
platform: time
|
||||||
|
alias: Beer o Clock
|
||||||
|
|
||||||
|
time_hours: 16
|
||||||
|
time_minutes: 0
|
||||||
|
time_seconds: 0
|
||||||
|
|
||||||
|
execute_service: notify.notify
|
||||||
|
service_data: {"message":"It's 4, time for beer!"}
|
@ -1,124 +0,0 @@
|
|||||||
[homeassistant]
|
|
||||||
# Location required to calculate the time the sun rises and sets
|
|
||||||
latitude=32.87336
|
|
||||||
longitude=-117.22743
|
|
||||||
|
|
||||||
[http]
|
|
||||||
api_password=mypass
|
|
||||||
# Set to 1 to load each Polymer component separately
|
|
||||||
# development=1
|
|
||||||
|
|
||||||
[light]
|
|
||||||
platform=hue
|
|
||||||
|
|
||||||
[wink]
|
|
||||||
# Get your token at https://winkbearertoken.appspot.com
|
|
||||||
access_token=YOUR_TOKEN
|
|
||||||
|
|
||||||
[device_tracker]
|
|
||||||
# The following types are available: netgear, tomato, luci, nmap_tracker
|
|
||||||
platform=netgear
|
|
||||||
host=192.168.1.1
|
|
||||||
username=admin
|
|
||||||
password=PASSWORD
|
|
||||||
# http_id is needed for Tomato routers only
|
|
||||||
# http_id=ABCDEFGHH
|
|
||||||
# For nmap_tracker, only the IP addresses to scan are needed:
|
|
||||||
# hosts=192.168.1.1/24 # netmask prefix notation or
|
|
||||||
# hosts=192.168.1.1-255 # address range
|
|
||||||
|
|
||||||
|
|
||||||
[chromecast]
|
|
||||||
|
|
||||||
[switch]
|
|
||||||
platform=wemo
|
|
||||||
|
|
||||||
[thermostat]
|
|
||||||
platform=nest
|
|
||||||
# Required: username and password that are used to login to the Nest thermostat.
|
|
||||||
username=myemail@mydomain.com
|
|
||||||
password=mypassword
|
|
||||||
|
|
||||||
[downloader]
|
|
||||||
download_dir=downloads
|
|
||||||
|
|
||||||
[notify]
|
|
||||||
platform=pushbullet
|
|
||||||
api_key=ABCDEFGHJKLMNOPQRSTUVXYZ
|
|
||||||
|
|
||||||
[device_sun_light_trigger]
|
|
||||||
# Optional: specify a specific light/group of lights that has to be turned on
|
|
||||||
light_group=group.living_room
|
|
||||||
# Optional: specify which light profile to use when turning lights on
|
|
||||||
light_profile=relax
|
|
||||||
# Optional: disable lights being turned off when everybody leaves the house
|
|
||||||
# disable_turn_off=1
|
|
||||||
|
|
||||||
# A comma seperated list of states that have to be tracked as a single group
|
|
||||||
# Grouped states should share the same type of states (ON/OFF or HOME/NOT_HOME)
|
|
||||||
[group]
|
|
||||||
living_room=light.Bowl,light.Ceiling,light.TV_back_light
|
|
||||||
children=device_tracker.child_1,device_tracker.child_2
|
|
||||||
|
|
||||||
[process]
|
|
||||||
# items are which processes to look for: <entity_id>=<search string within ps>
|
|
||||||
xbmc=XBMC.App
|
|
||||||
|
|
||||||
[example]
|
|
||||||
|
|
||||||
[simple_alarm]
|
|
||||||
# Which light/light group has to flash when a known device comes home
|
|
||||||
known_light=light.Bowl
|
|
||||||
# Which light/light group has to flash red when light turns on while no one home
|
|
||||||
unknown_light=group.living_room
|
|
||||||
|
|
||||||
[browser]
|
|
||||||
|
|
||||||
[keyboard]
|
|
||||||
|
|
||||||
[automation]
|
|
||||||
platform=state
|
|
||||||
alias=Sun starts shining
|
|
||||||
|
|
||||||
state_entity_id=sun.sun
|
|
||||||
# Next two are optional, omit to match all
|
|
||||||
state_from=below_horizon
|
|
||||||
state_to=above_horizon
|
|
||||||
|
|
||||||
execute_service=light.turn_off
|
|
||||||
service_entity_id=group.living_room
|
|
||||||
|
|
||||||
[automation 2]
|
|
||||||
platform=time
|
|
||||||
alias=Beer o Clock
|
|
||||||
|
|
||||||
time_hours=16
|
|
||||||
time_minutes=0
|
|
||||||
time_seconds=0
|
|
||||||
|
|
||||||
execute_service=notify.notify
|
|
||||||
service_data={"message":"It's 4, time for beer!"}
|
|
||||||
|
|
||||||
|
|
||||||
[light]
|
|
||||||
platform=vera
|
|
||||||
# The "device_data" field is not required but if you want a switch to show up as a light
|
|
||||||
# then add it in to here. If you leave out the "name" field the name configured in your
|
|
||||||
# Vera controller will be used instead. The "id" field should be the Vera id for the device.
|
|
||||||
device_data=[{"id" : 12, "name": "Lounge Light"}]
|
|
||||||
vera_controller_url=http://192.168.1.254:3480/
|
|
||||||
|
|
||||||
[switch]
|
|
||||||
platform=vera
|
|
||||||
# The "device_data" field is not required and if not specified all switches will still be added.
|
|
||||||
# You can use it to override the name specified in your Vera controller using the "name" variable.
|
|
||||||
# The "id" field should be the Vera id for the device.
|
|
||||||
device_data=[{"id" : 12, "name": "Lounge Light"}, {"id" : 3, "name": "Lounge Motion"}]
|
|
||||||
vera_controller_url=http://192.168.1.254:3480/
|
|
||||||
|
|
||||||
[sensor]
|
|
||||||
platform=vera
|
|
||||||
# The "device_data" field is not required and if not specified all sensors will still be added.
|
|
||||||
# You can use it to override the name specified in your Vera controller using the "name" variable.
|
|
||||||
device_data=[{"id" : 4, "name": "Lounge Temp"}, {"id" : 3, "name": "Lounge Motion"}, {"id" : 5, "name": "Light Level"}]
|
|
||||||
vera_controller_url=http://192.168.1.254:3480/
|
|
@ -72,15 +72,19 @@ def ensure_config_path(config_dir):
|
|||||||
'directory {} ').format(config_dir))
|
'directory {} ').format(config_dir))
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
config_path = os.path.join(config_dir, 'home-assistant.conf')
|
# Try to use yaml configuration first
|
||||||
|
config_path = os.path.join(config_dir, 'configuration.yaml')
|
||||||
|
if not os.path.isfile(config_path):
|
||||||
|
config_path = os.path.join(config_dir, 'home-assistant.conf')
|
||||||
|
|
||||||
# Ensure a config file exists to make first time usage easier
|
# Ensure a config file exists to make first time usage easier
|
||||||
if not os.path.isfile(config_path):
|
if not os.path.isfile(config_path):
|
||||||
|
config_path = os.path.join(config_dir, 'configuration.yaml')
|
||||||
try:
|
try:
|
||||||
with open(config_path, 'w') as conf:
|
with open(config_path, 'w') as conf:
|
||||||
conf.write("[frontend]\n\n")
|
conf.write("frontend:\n\n")
|
||||||
conf.write("[discovery]\n\n")
|
conf.write("discovery:\n\n")
|
||||||
conf.write("[history]\n\n")
|
conf.write("history:\n\n")
|
||||||
except IOError:
|
except IOError:
|
||||||
print(('Fatal Error: No configuration file found and unable '
|
print(('Fatal Error: No configuration file found and unable '
|
||||||
'to write a default one to {}').format(config_path))
|
'to write a default one to {}').format(config_path))
|
||||||
|
@ -11,6 +11,8 @@ start by calling homeassistant.start_home_assistant(bus)
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import configparser
|
import configparser
|
||||||
|
import yaml
|
||||||
|
import io
|
||||||
import logging
|
import logging
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
@ -77,7 +79,9 @@ def from_config_dict(config, hass=None):
|
|||||||
|
|
||||||
# Make a copy because we are mutating it.
|
# Make a copy because we are mutating it.
|
||||||
# Convert it to defaultdict so components can always have config dict
|
# Convert it to defaultdict so components can always have config dict
|
||||||
config = defaultdict(dict, config)
|
# Convert values to dictionaries if they are None
|
||||||
|
config = defaultdict(
|
||||||
|
dict, {key: value or {} for key, value in config.items()})
|
||||||
|
|
||||||
# Filter out the repeating and common config section [homeassistant]
|
# Filter out the repeating and common config section [homeassistant]
|
||||||
components = (key for key in config.keys()
|
components = (key for key in config.keys()
|
||||||
@ -110,17 +114,21 @@ 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_dir = os.path.abspath(os.path.dirname(config_path))
|
hass.config_dir = os.path.abspath(os.path.dirname(config_path))
|
||||||
|
|
||||||
# Read config
|
|
||||||
config = configparser.ConfigParser()
|
|
||||||
config.read(config_path)
|
|
||||||
|
|
||||||
config_dict = {}
|
config_dict = {}
|
||||||
|
# check config file type
|
||||||
|
if os.path.splitext(config_path)[1] == '.yaml':
|
||||||
|
# Read yaml
|
||||||
|
config_dict = yaml.load(io.open(config_path, 'r'))
|
||||||
|
else:
|
||||||
|
# Read config
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
config.read(config_path)
|
||||||
|
|
||||||
for section in config.sections():
|
for section in config.sections():
|
||||||
config_dict[section] = {}
|
config_dict[section] = {}
|
||||||
|
|
||||||
for key, val in config.items(section):
|
for key, val in config.items(section):
|
||||||
config_dict[section][key] = val
|
config_dict[section][key] = val
|
||||||
|
|
||||||
return from_config_dict(config_dict, hass)
|
return from_config_dict(config_dict, hass)
|
||||||
|
|
||||||
|
@ -4,27 +4,21 @@ homeassistant.components.demo
|
|||||||
|
|
||||||
Sets up a demo environment that mimics interaction with devices
|
Sets up a demo environment that mimics interaction with devices
|
||||||
"""
|
"""
|
||||||
import random
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import homeassistant as ha
|
import homeassistant as ha
|
||||||
|
import homeassistant.bootstrap as bootstrap
|
||||||
import homeassistant.loader as loader
|
import homeassistant.loader as loader
|
||||||
from homeassistant.helpers import extract_entity_ids
|
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
SERVICE_TURN_ON, SERVICE_TURN_OFF,
|
CONF_PLATFORM, ATTR_ENTITY_PICTURE, STATE_ON,
|
||||||
STATE_ON, STATE_OFF, TEMP_CELCIUS,
|
|
||||||
ATTR_ENTITY_PICTURE, ATTR_ENTITY_ID, ATTR_UNIT_OF_MEASUREMENT,
|
|
||||||
CONF_LATITUDE, CONF_LONGITUDE)
|
CONF_LATITUDE, CONF_LONGITUDE)
|
||||||
from homeassistant.components.light import (
|
|
||||||
ATTR_XY_COLOR, ATTR_RGB_COLOR, ATTR_BRIGHTNESS, GROUP_NAME_ALL_LIGHTS)
|
|
||||||
from homeassistant.components.thermostat import (
|
|
||||||
ATTR_CURRENT_TEMPERATURE, ATTR_AWAY_MODE)
|
|
||||||
from homeassistant.util import split_entity_id, color_RGB_to_xy
|
|
||||||
|
|
||||||
DOMAIN = "demo"
|
DOMAIN = "demo"
|
||||||
|
|
||||||
DEPENDENCIES = []
|
DEPENDENCIES = []
|
||||||
|
|
||||||
|
COMPONENTS_WITH_DEMO_PLATFORM = ['switch', 'light', 'thermostat', 'sensor']
|
||||||
|
|
||||||
|
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
""" Setup a demo environment. """
|
""" Setup a demo environment. """
|
||||||
@ -37,57 +31,6 @@ def setup(hass, config):
|
|||||||
if config[DOMAIN].get('hide_demo_state') != '1':
|
if config[DOMAIN].get('hide_demo_state') != '1':
|
||||||
hass.states.set('a.Demo_Mode', 'Enabled')
|
hass.states.set('a.Demo_Mode', 'Enabled')
|
||||||
|
|
||||||
light_colors = [
|
|
||||||
[0.861, 0.3259],
|
|
||||||
[0.6389, 0.3028],
|
|
||||||
[0.1684, 0.0416]
|
|
||||||
]
|
|
||||||
|
|
||||||
def mock_turn_on(service):
|
|
||||||
""" Will fake the component has been turned on. """
|
|
||||||
if service.data and ATTR_ENTITY_ID in service.data:
|
|
||||||
entity_ids = extract_entity_ids(hass, service)
|
|
||||||
else:
|
|
||||||
entity_ids = hass.states.entity_ids(service.domain)
|
|
||||||
|
|
||||||
for entity_id in entity_ids:
|
|
||||||
domain, _ = split_entity_id(entity_id)
|
|
||||||
|
|
||||||
if domain == "light":
|
|
||||||
rgb_color = service.data.get(ATTR_RGB_COLOR)
|
|
||||||
|
|
||||||
if rgb_color:
|
|
||||||
color = color_RGB_to_xy(
|
|
||||||
rgb_color[0], rgb_color[1], rgb_color[2])
|
|
||||||
|
|
||||||
else:
|
|
||||||
cur_state = hass.states.get(entity_id)
|
|
||||||
|
|
||||||
# Use current color if available
|
|
||||||
if cur_state and cur_state.attributes.get(ATTR_XY_COLOR):
|
|
||||||
color = cur_state.attributes.get(ATTR_XY_COLOR)
|
|
||||||
else:
|
|
||||||
color = random.choice(light_colors)
|
|
||||||
|
|
||||||
data = {
|
|
||||||
ATTR_BRIGHTNESS: service.data.get(ATTR_BRIGHTNESS, 200),
|
|
||||||
ATTR_XY_COLOR: color
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
data = None
|
|
||||||
|
|
||||||
hass.states.set(entity_id, STATE_ON, data)
|
|
||||||
|
|
||||||
def mock_turn_off(service):
|
|
||||||
""" Will fake the component has been turned off. """
|
|
||||||
if service.data and ATTR_ENTITY_ID in service.data:
|
|
||||||
entity_ids = extract_entity_ids(hass, service)
|
|
||||||
else:
|
|
||||||
entity_ids = hass.states.entity_ids(service.domain)
|
|
||||||
|
|
||||||
for entity_id in entity_ids:
|
|
||||||
hass.states.set(entity_id, STATE_OFF)
|
|
||||||
|
|
||||||
# Setup sun
|
# Setup sun
|
||||||
if CONF_LATITUDE not in config[ha.DOMAIN]:
|
if CONF_LATITUDE not in config[ha.DOMAIN]:
|
||||||
config[ha.DOMAIN][CONF_LATITUDE] = '32.87336'
|
config[ha.DOMAIN][CONF_LATITUDE] = '32.87336'
|
||||||
@ -97,34 +40,16 @@ def setup(hass, config):
|
|||||||
|
|
||||||
loader.get_component('sun').setup(hass, config)
|
loader.get_component('sun').setup(hass, config)
|
||||||
|
|
||||||
# Setup fake lights
|
# Setup demo platforms
|
||||||
lights = ['light.Bowl', 'light.Ceiling', 'light.TV_Back_light',
|
for component in COMPONENTS_WITH_DEMO_PLATFORM:
|
||||||
'light.Bed_light']
|
bootstrap.setup_component(
|
||||||
|
hass, component, {component: {CONF_PLATFORM: 'demo'}})
|
||||||
hass.services.register('light', SERVICE_TURN_ON, mock_turn_on)
|
|
||||||
hass.services.register('light', SERVICE_TURN_OFF, mock_turn_off)
|
|
||||||
|
|
||||||
mock_turn_on(ha.ServiceCall('light', SERVICE_TURN_ON,
|
|
||||||
{'entity_id': lights[0:2]}))
|
|
||||||
mock_turn_off(ha.ServiceCall('light', SERVICE_TURN_OFF,
|
|
||||||
{'entity_id': lights[2:]}))
|
|
||||||
|
|
||||||
group.setup_group(hass, GROUP_NAME_ALL_LIGHTS, lights, False)
|
|
||||||
|
|
||||||
# Setup switch
|
|
||||||
switches = ['switch.AC', 'switch.Christmas_Lights']
|
|
||||||
|
|
||||||
hass.services.register('switch', SERVICE_TURN_ON, mock_turn_on)
|
|
||||||
hass.services.register('switch', SERVICE_TURN_OFF, mock_turn_off)
|
|
||||||
|
|
||||||
mock_turn_on(ha.ServiceCall('switch', SERVICE_TURN_ON,
|
|
||||||
{'entity_id': switches[0:1]}))
|
|
||||||
mock_turn_off(ha.ServiceCall('switch', SERVICE_TURN_OFF,
|
|
||||||
{'entity_id': switches[1:]}))
|
|
||||||
|
|
||||||
# Setup room groups
|
# Setup room groups
|
||||||
group.setup_group(hass, 'living room', lights[0:3] + switches[0:1])
|
lights = hass.states.entity_ids('light')
|
||||||
group.setup_group(hass, 'bedroom', [lights[3]] + switches[1:])
|
switches = hass.states.entity_ids('switch')
|
||||||
|
group.setup_group(hass, 'living room', [lights[0], lights[1], switches[0]])
|
||||||
|
group.setup_group(hass, 'bedroom', [lights[2], switches[1]])
|
||||||
|
|
||||||
# Setup process
|
# Setup process
|
||||||
hass.states.set("process.XBMC", STATE_ON)
|
hass.states.set("process.XBMC", STATE_ON)
|
||||||
@ -152,26 +77,7 @@ def setup(hass, config):
|
|||||||
ATTR_ENTITY_PICTURE:
|
ATTR_ENTITY_PICTURE:
|
||||||
'http://graph.facebook.com/KillBillMovie/picture'})
|
'http://graph.facebook.com/KillBillMovie/picture'})
|
||||||
|
|
||||||
# Setup tellstick sensors
|
# Setup configurator
|
||||||
hass.states.set("tellstick_sensor.Outside_temperature", "15.6",
|
|
||||||
{
|
|
||||||
'friendly_name': 'Outside temperature',
|
|
||||||
'unit_of_measurement': '°C'
|
|
||||||
})
|
|
||||||
hass.states.set("tellstick_sensor.Outside_humidity", "54",
|
|
||||||
{
|
|
||||||
'friendly_name': 'Outside humidity',
|
|
||||||
'unit_of_measurement': '%'
|
|
||||||
})
|
|
||||||
|
|
||||||
# Nest demo
|
|
||||||
hass.states.set("thermostat.Nest", "23",
|
|
||||||
{
|
|
||||||
ATTR_UNIT_OF_MEASUREMENT: TEMP_CELCIUS,
|
|
||||||
ATTR_CURRENT_TEMPERATURE: '18',
|
|
||||||
ATTR_AWAY_MODE: STATE_OFF
|
|
||||||
})
|
|
||||||
|
|
||||||
configurator_ids = []
|
configurator_ids = []
|
||||||
|
|
||||||
def hue_configuration_callback(data):
|
def hue_configuration_callback(data):
|
||||||
|
@ -11,7 +11,6 @@ import logging
|
|||||||
import threading
|
import threading
|
||||||
|
|
||||||
# pylint: disable=no-name-in-module, import-error
|
# pylint: disable=no-name-in-module, import-error
|
||||||
from homeassistant.external.netdisco.netdisco import DiscoveryService
|
|
||||||
import homeassistant.external.netdisco.netdisco.const as services
|
import homeassistant.external.netdisco.netdisco.const as services
|
||||||
|
|
||||||
from homeassistant import bootstrap
|
from homeassistant import bootstrap
|
||||||
@ -52,12 +51,20 @@ def listen(hass, service, callback):
|
|||||||
|
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
""" Starts a discovery service. """
|
""" Starts a discovery service. """
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
try:
|
||||||
|
from homeassistant.external.netdisco.netdisco.service import \
|
||||||
|
DiscoveryService
|
||||||
|
except ImportError:
|
||||||
|
logger.exception(
|
||||||
|
"Unable to import netdisco. "
|
||||||
|
"Did you install all the zeroconf dependency?")
|
||||||
|
return False
|
||||||
|
|
||||||
# Disable zeroconf logging, it spams
|
# Disable zeroconf logging, it spams
|
||||||
logging.getLogger('zeroconf').setLevel(logging.CRITICAL)
|
logging.getLogger('zeroconf').setLevel(logging.CRITICAL)
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
lock = threading.Lock()
|
lock = threading.Lock()
|
||||||
|
|
||||||
def new_service_listener(service, info):
|
def new_service_listener(service, info):
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
""" DO NOT MODIFY. Auto-generated by build_frontend script """
|
""" DO NOT MODIFY. Auto-generated by build_frontend script """
|
||||||
VERSION = "10b554c3f2db1c5441d5e74c0f6d8469"
|
VERSION = "30729f23c19a66d9364d78b2379867cc"
|
||||||
|
File diff suppressed because one or more lines are too long
@ -11,32 +11,32 @@
|
|||||||
"bower_components"
|
"bower_components"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"webcomponentsjs": "Polymer/webcomponentsjs#~0.5.4",
|
"webcomponentsjs": "Polymer/webcomponentsjs#~0.5.5",
|
||||||
"font-roboto": "Polymer/font-roboto#~0.5.4",
|
"font-roboto": "Polymer/font-roboto#~0.5.5",
|
||||||
"core-header-panel": "polymer/core-header-panel#~0.5.4",
|
"core-header-panel": "polymer/core-header-panel#~0.5.5",
|
||||||
"core-toolbar": "polymer/core-toolbar#~0.5.4",
|
"core-toolbar": "polymer/core-toolbar#~0.5.5",
|
||||||
"core-tooltip": "Polymer/core-tooltip#~0.5.4",
|
"core-tooltip": "Polymer/core-tooltip#~0.5.5",
|
||||||
"core-menu": "polymer/core-menu#~0.5.4",
|
"core-menu": "polymer/core-menu#~0.5.5",
|
||||||
"core-item": "Polymer/core-item#~0.5.4",
|
"core-item": "Polymer/core-item#~0.5.5",
|
||||||
"core-input": "Polymer/core-input#~0.5.4",
|
"core-input": "Polymer/core-input#~0.5.5",
|
||||||
"core-icons": "polymer/core-icons#~0.5.4",
|
"core-icons": "polymer/core-icons#~0.5.5",
|
||||||
"core-image": "polymer/core-image#~0.5.4",
|
"core-image": "polymer/core-image#~0.5.5",
|
||||||
"core-style": "polymer/core-style#~0.5.4",
|
"core-style": "polymer/core-style#~0.5.5",
|
||||||
"paper-toast": "Polymer/paper-toast#~0.5.4",
|
"paper-toast": "Polymer/paper-toast#~0.5.5",
|
||||||
"paper-dialog": "Polymer/paper-dialog#~0.5.4",
|
"paper-dialog": "Polymer/paper-dialog#~0.5.5",
|
||||||
"paper-spinner": "Polymer/paper-spinner#~0.5.4",
|
"paper-spinner": "Polymer/paper-spinner#~0.5.5",
|
||||||
"paper-button": "Polymer/paper-button#~0.5.4",
|
"paper-button": "Polymer/paper-button#~0.5.5",
|
||||||
"paper-input": "Polymer/paper-input#~0.5.4",
|
"paper-input": "Polymer/paper-input#~0.5.5",
|
||||||
"paper-toggle-button": "polymer/paper-toggle-button#~0.5.4",
|
"paper-toggle-button": "polymer/paper-toggle-button#~0.5.5",
|
||||||
"paper-icon-button": "polymer/paper-icon-button#~0.5.4",
|
"paper-icon-button": "polymer/paper-icon-button#~0.5.5",
|
||||||
"paper-menu-button": "polymer/paper-menu-button#~0.5.4",
|
"paper-menu-button": "polymer/paper-menu-button#~0.5.5",
|
||||||
"paper-dropdown": "polymer/paper-dropdown#~0.5.4",
|
"paper-dropdown": "polymer/paper-dropdown#~0.5.5",
|
||||||
"paper-item": "polymer/paper-item#~0.5.4",
|
"paper-item": "polymer/paper-item#~0.5.5",
|
||||||
"paper-slider": "polymer/paper-slider#~0.5.4",
|
"paper-slider": "polymer/paper-slider#~0.5.5",
|
||||||
"color-picker-element": "~0.0.2",
|
"color-picker-element": "~0.0.2",
|
||||||
"google-apis": "GoogleWebComponents/google-apis#~0.4.2",
|
"google-apis": "GoogleWebComponents/google-apis#~0.4.2",
|
||||||
"core-drawer-panel": "polymer/core-drawer-panel#~0.5.4",
|
"core-drawer-panel": "polymer/core-drawer-panel#~0.5.5",
|
||||||
"core-scroll-header-panel": "polymer/core-scroll-header-panel#~0.5.4",
|
"core-scroll-header-panel": "polymer/core-scroll-header-panel#~0.5.5",
|
||||||
"moment": "~2.9.0"
|
"moment": "~2.9.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit 9b5c89e05277c276c941166c582d2cb5159e9425
|
Subproject commit 2f96b21d42c6b1c01a235b39fbbd2e0cf1b8d651
|
@ -5,6 +5,7 @@
|
|||||||
<link rel="import" href="more-info-group.html">
|
<link rel="import" href="more-info-group.html">
|
||||||
<link rel="import" href="more-info-sun.html">
|
<link rel="import" href="more-info-sun.html">
|
||||||
<link rel="import" href="more-info-configurator.html">
|
<link rel="import" href="more-info-configurator.html">
|
||||||
|
<link rel="import" href="more-info-thermostat.html">
|
||||||
|
|
||||||
<polymer-element name="more-info-content" attributes="stateObj">
|
<polymer-element name="more-info-content" attributes="stateObj">
|
||||||
<template>
|
<template>
|
||||||
|
@ -0,0 +1,114 @@
|
|||||||
|
<link rel="import" href="../bower_components/polymer/polymer.html">
|
||||||
|
<link rel="import" href="../bower_components/paper-slider/paper-slider.html">
|
||||||
|
<link rel="import" href="../bower_components/paper-toggle-button/paper-toggle-button.html">
|
||||||
|
|
||||||
|
<polymer-element name="more-info-thermostat" attributes="stateObj">
|
||||||
|
<template>
|
||||||
|
<style>
|
||||||
|
paper-slider {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
paper-slider::shadow #sliderKnobInner,
|
||||||
|
paper-slider::shadow #sliderBar::shadow #activeProgress {
|
||||||
|
background-color: #039be5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.away-mode-toggle {
|
||||||
|
display: none;
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host-context(.has-away_mode) .away-mode-toggle {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<div>Target Temperature</div>
|
||||||
|
<paper-slider
|
||||||
|
min="{{tempMin}}" max="{{tempMax}}"
|
||||||
|
value='{{targetTemperatureSliderValue}}' pin
|
||||||
|
on-change="{{targetTemperatureSliderChanged}}">
|
||||||
|
</paper-slider>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class='away-mode-toggle'>
|
||||||
|
<div center horizontal layout>
|
||||||
|
<div flex>Away Mode</div>
|
||||||
|
<paper-toggle-button
|
||||||
|
checked="{{awayToggleChecked}}"
|
||||||
|
on-change="{{toggleChanged}}">
|
||||||
|
</paper-toggle-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
var constants = window.hass.constants;
|
||||||
|
|
||||||
|
Polymer({
|
||||||
|
tempMin: 10,
|
||||||
|
tempMax: 40,
|
||||||
|
targetTemperatureSliderValue: 0,
|
||||||
|
|
||||||
|
awayToggleChecked: false,
|
||||||
|
|
||||||
|
observe: {
|
||||||
|
'stateObj.attributes.away_mode': 'awayChanged'
|
||||||
|
},
|
||||||
|
|
||||||
|
stateObjChanged: function(oldVal, newVal) {
|
||||||
|
this.targetTemperatureSliderValue = this.stateObj.state;
|
||||||
|
|
||||||
|
if (this.stateObj.attributes.unit_of_measurement === constants.UNIT_TEMP_F) {
|
||||||
|
this.tempMin = 45;
|
||||||
|
this.tempMax = 95;
|
||||||
|
} else {
|
||||||
|
this.tempMin = 7;
|
||||||
|
this.tempMax = 35;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
targetTemperatureSliderChanged: function(ev, details, target) {
|
||||||
|
var temp = parseInt(target.value);
|
||||||
|
|
||||||
|
if(isNaN(temp)) return;
|
||||||
|
|
||||||
|
serviceActions.callService("thermostat", "set_temperature", {
|
||||||
|
entity_id: this.stateObj.entityId,
|
||||||
|
temperature: temp
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
toggleChanged: function(ev) {
|
||||||
|
var newVal = ev.target.checked;
|
||||||
|
|
||||||
|
if(newVal && this.stateObj.attributes.away_mode === 'off') {
|
||||||
|
this.service_set_away(true);
|
||||||
|
} else if(!newVal && this.stateObj.attributes.away_mode === 'on') {
|
||||||
|
this.service_set_away(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
awayChanged: function(oldVal, newVal) {
|
||||||
|
this.awayToggleChecked = newVal == 'on';
|
||||||
|
},
|
||||||
|
|
||||||
|
service_set_away: function(away_mode) {
|
||||||
|
// We call stateChanged after a successful call to re-sync the toggle
|
||||||
|
// with the state. It will be out of sync if our service call did not
|
||||||
|
// result in the entity to be turned on. Since the state is not changing,
|
||||||
|
// the resync is not called automatic.
|
||||||
|
serviceActions.callService(
|
||||||
|
'thermostat', 'set_away_mode',
|
||||||
|
{entity_id: this.stateObj.entityId, away_mode: away_mode})
|
||||||
|
|
||||||
|
.then(function() {
|
||||||
|
this.awayChanged(null, this.stateObj.attributes.away_mode);
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</polymer-element>
|
@ -3,7 +3,9 @@
|
|||||||
<script>
|
<script>
|
||||||
(function() {
|
(function() {
|
||||||
var DOMAINS_WITH_CARD = ['thermostat', 'configurator'];
|
var DOMAINS_WITH_CARD = ['thermostat', 'configurator'];
|
||||||
var DOMAINS_WITH_MORE_INFO = ['light', 'group', 'sun', 'configurator'];
|
var DOMAINS_WITH_MORE_INFO = [
|
||||||
|
'light', 'group', 'sun', 'configurator', 'thermostat'
|
||||||
|
];
|
||||||
|
|
||||||
// Register some polymer filters
|
// Register some polymer filters
|
||||||
|
|
||||||
|
File diff suppressed because one or more lines are too long
@ -101,7 +101,11 @@ def get_entity_ids(hass, entity_id, domain_filter=None):
|
|||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
""" Sets up all groups found definded in the configuration. """
|
""" Sets up all groups found definded in the configuration. """
|
||||||
for name, entity_ids in config.get(DOMAIN, {}).items():
|
for name, entity_ids in config.get(DOMAIN, {}).items():
|
||||||
setup_group(hass, name, entity_ids.split(","))
|
# Support old deprecated method - 2/28/2015
|
||||||
|
if isinstance(entity_ids, str):
|
||||||
|
entity_ids = entity_ids.split(",")
|
||||||
|
|
||||||
|
setup_group(hass, name, entity_ids)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -52,17 +52,18 @@ import logging
|
|||||||
import os
|
import os
|
||||||
import csv
|
import csv
|
||||||
|
|
||||||
from homeassistant.loader import get_component
|
from homeassistant.helpers.device_component import DeviceComponent
|
||||||
|
|
||||||
import homeassistant.util as util
|
import homeassistant.util as util
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF, ATTR_ENTITY_ID)
|
STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF, ATTR_ENTITY_ID)
|
||||||
from homeassistant.helpers import (
|
from homeassistant.helpers import extract_entity_ids
|
||||||
generate_entity_id, extract_entity_ids, config_per_platform)
|
|
||||||
from homeassistant.components import group, discovery, wink
|
from homeassistant.components import group, discovery, wink
|
||||||
|
|
||||||
|
|
||||||
DOMAIN = "light"
|
DOMAIN = "light"
|
||||||
DEPENDENCIES = []
|
DEPENDENCIES = []
|
||||||
|
SCAN_INTERVAL = 30
|
||||||
|
|
||||||
GROUP_NAME_ALL_LIGHTS = 'all lights'
|
GROUP_NAME_ALL_LIGHTS = 'all lights'
|
||||||
ENTITY_ID_ALL_LIGHTS = group.ENTITY_ID_FORMAT.format('all_lights')
|
ENTITY_ID_ALL_LIGHTS = group.ENTITY_ID_FORMAT.format('all_lights')
|
||||||
@ -140,6 +141,13 @@ def turn_off(hass, entity_id=None, transition=None):
|
|||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
""" Exposes light control via statemachine and services. """
|
""" Exposes light control via statemachine and services. """
|
||||||
|
|
||||||
|
component = DeviceComponent(
|
||||||
|
_LOGGER, DOMAIN, hass, SCAN_INTERVAL, DISCOVERY_PLATFORMS,
|
||||||
|
GROUP_NAME_ALL_LIGHTS)
|
||||||
|
component.setup(config)
|
||||||
|
|
||||||
|
lights = component.devices
|
||||||
|
|
||||||
# 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),
|
||||||
@ -168,52 +176,6 @@ def setup(hass, config):
|
|||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Dict to track entity_id -> lights
|
|
||||||
lights = {}
|
|
||||||
|
|
||||||
# Track all lights in a group
|
|
||||||
light_group = group.Group(hass, GROUP_NAME_ALL_LIGHTS, user_defined=False)
|
|
||||||
|
|
||||||
def add_lights(new_lights):
|
|
||||||
""" Add lights to the component to track. """
|
|
||||||
for light in new_lights:
|
|
||||||
if light is not None and light not in lights.values():
|
|
||||||
light.entity_id = generate_entity_id(
|
|
||||||
ENTITY_ID_FORMAT, light.name, lights.keys())
|
|
||||||
|
|
||||||
lights[light.entity_id] = light
|
|
||||||
|
|
||||||
light.update_ha_state(hass)
|
|
||||||
|
|
||||||
light_group.update_tracked_entity_ids(lights.keys())
|
|
||||||
|
|
||||||
for p_type, p_config in config_per_platform(config, DOMAIN, _LOGGER):
|
|
||||||
platform = get_component(ENTITY_ID_FORMAT.format(p_type))
|
|
||||||
|
|
||||||
if platform is None:
|
|
||||||
_LOGGER.error("Unknown type specified: %s", p_type)
|
|
||||||
|
|
||||||
platform.setup_platform(hass, p_config, add_lights)
|
|
||||||
|
|
||||||
def update_lights_state(now):
|
|
||||||
""" Update the states of all the lights. """
|
|
||||||
if lights:
|
|
||||||
_LOGGER.info("Updating light states")
|
|
||||||
|
|
||||||
for light in lights.values():
|
|
||||||
light.update_ha_state(hass, True)
|
|
||||||
|
|
||||||
update_lights_state(None)
|
|
||||||
|
|
||||||
def light_discovered(service, info):
|
|
||||||
""" Called when a light is discovered. """
|
|
||||||
platform = get_component(
|
|
||||||
ENTITY_ID_FORMAT.format(DISCOVERY_PLATFORMS[service]))
|
|
||||||
|
|
||||||
platform.setup_platform(hass, {}, add_lights, info)
|
|
||||||
|
|
||||||
discovery.listen(hass, DISCOVERY_PLATFORMS.keys(), light_discovered)
|
|
||||||
|
|
||||||
def handle_light_service(service):
|
def handle_light_service(service):
|
||||||
""" Hande a turn light on or off service call. """
|
""" Hande a turn light on or off service call. """
|
||||||
# Get and validate data
|
# Get and validate data
|
||||||
@ -298,10 +260,7 @@ def setup(hass, config):
|
|||||||
light.turn_on(**params)
|
light.turn_on(**params)
|
||||||
|
|
||||||
for light in target_lights:
|
for light in target_lights:
|
||||||
light.update_ha_state(hass, True)
|
light.update_ha_state(True)
|
||||||
|
|
||||||
# Update light state every 30 seconds
|
|
||||||
hass.track_time_change(update_lights_state, second=[0, 30])
|
|
||||||
|
|
||||||
# Listen for light on and light off service calls
|
# Listen for light on and light off service calls
|
||||||
hass.services.register(DOMAIN, SERVICE_TURN_ON,
|
hass.services.register(DOMAIN, SERVICE_TURN_ON,
|
||||||
|
69
homeassistant/components/light/demo.py
Normal file
69
homeassistant/components/light/demo.py
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
""" Provides demo lights. """
|
||||||
|
import random
|
||||||
|
|
||||||
|
from homeassistant.helpers import ToggleDevice
|
||||||
|
from homeassistant.const import STATE_ON, STATE_OFF, DEVICE_DEFAULT_NAME
|
||||||
|
from homeassistant.components.light import ATTR_BRIGHTNESS, ATTR_XY_COLOR
|
||||||
|
|
||||||
|
|
||||||
|
LIGHT_COLORS = [
|
||||||
|
[0.861, 0.3259],
|
||||||
|
[0.6389, 0.3028],
|
||||||
|
[0.1684, 0.0416]
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
||||||
|
""" Find and return demo lights. """
|
||||||
|
add_devices_callback([
|
||||||
|
DemoLight("Bed Light", STATE_OFF),
|
||||||
|
DemoLight("Ceiling", STATE_ON),
|
||||||
|
DemoLight("Kitchen", STATE_ON)
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
class DemoLight(ToggleDevice):
|
||||||
|
""" Provides a demo switch. """
|
||||||
|
def __init__(self, name, state, xy=None, brightness=180):
|
||||||
|
self._name = name or DEVICE_DEFAULT_NAME
|
||||||
|
self._state = state
|
||||||
|
self._xy = xy or random.choice(LIGHT_COLORS)
|
||||||
|
self._brightness = brightness
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
""" Returns the name of the device if any. """
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def state(self):
|
||||||
|
""" Returns the name of the device if any. """
|
||||||
|
return self._state
|
||||||
|
|
||||||
|
@property
|
||||||
|
def state_attributes(self):
|
||||||
|
""" Returns optional state attributes. """
|
||||||
|
if self.is_on:
|
||||||
|
return {
|
||||||
|
ATTR_BRIGHTNESS: self._brightness,
|
||||||
|
ATTR_XY_COLOR: self._xy,
|
||||||
|
}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_on(self):
|
||||||
|
""" True if device is on. """
|
||||||
|
return self._state == STATE_ON
|
||||||
|
|
||||||
|
def turn_on(self, **kwargs):
|
||||||
|
""" Turn the device on. """
|
||||||
|
self._state = STATE_ON
|
||||||
|
|
||||||
|
if ATTR_XY_COLOR in kwargs:
|
||||||
|
self._xy = kwargs[ATTR_XY_COLOR]
|
||||||
|
|
||||||
|
if ATTR_BRIGHTNESS in kwargs:
|
||||||
|
self._brightness = kwargs[ATTR_BRIGHTNESS]
|
||||||
|
|
||||||
|
def turn_off(self, **kwargs):
|
||||||
|
""" Turn the device off. """
|
||||||
|
self._state = STATE_OFF
|
@ -7,7 +7,7 @@ from urllib.parse import urlparse
|
|||||||
from homeassistant.loader import get_component
|
from homeassistant.loader import get_component
|
||||||
import homeassistant.util as util
|
import homeassistant.util as util
|
||||||
from homeassistant.helpers import ToggleDevice
|
from homeassistant.helpers import ToggleDevice
|
||||||
from homeassistant.const import ATTR_FRIENDLY_NAME, CONF_HOST
|
from homeassistant.const import CONF_HOST
|
||||||
from homeassistant.components.light import (
|
from homeassistant.components.light import (
|
||||||
ATTR_BRIGHTNESS, ATTR_XY_COLOR, ATTR_TRANSITION,
|
ATTR_BRIGHTNESS, ATTR_XY_COLOR, ATTR_TRANSITION,
|
||||||
ATTR_FLASH, FLASH_LONG, FLASH_SHORT)
|
ATTR_FLASH, FLASH_LONG, FLASH_SHORT)
|
||||||
@ -153,9 +153,7 @@ class HueLight(ToggleDevice):
|
|||||||
@property
|
@property
|
||||||
def state_attributes(self):
|
def state_attributes(self):
|
||||||
""" Returns optional state attributes. """
|
""" Returns optional state attributes. """
|
||||||
attr = {
|
attr = {}
|
||||||
ATTR_FRIENDLY_NAME: self.name
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.is_on:
|
if self.is_on:
|
||||||
attr[ATTR_BRIGHTNESS] = self.info['state']['bri']
|
attr[ATTR_BRIGHTNESS] = self.info['state']['bri']
|
||||||
|
@ -4,86 +4,29 @@ homeassistant.components.sensor
|
|||||||
Component to interface with various sensors that can be monitored.
|
Component to interface with various sensors that can be monitored.
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
from datetime import timedelta
|
|
||||||
|
|
||||||
from homeassistant.loader import get_component
|
from homeassistant.helpers.device_component import DeviceComponent
|
||||||
import homeassistant.util as util
|
from homeassistant.components import wink, zwave
|
||||||
from homeassistant.const import (
|
|
||||||
STATE_OPEN)
|
|
||||||
from homeassistant.helpers import (
|
|
||||||
platform_devices_from_config)
|
|
||||||
from homeassistant.components import group, discovery, wink
|
|
||||||
|
|
||||||
DOMAIN = 'sensor'
|
DOMAIN = 'sensor'
|
||||||
DEPENDENCIES = []
|
DEPENDENCIES = []
|
||||||
|
SCAN_INTERVAL = 30
|
||||||
GROUP_NAME_ALL_SENSORS = 'all_sensors'
|
|
||||||
ENTITY_ID_ALL_SENSORS = group.ENTITY_ID_FORMAT.format(
|
|
||||||
GROUP_NAME_ALL_SENSORS)
|
|
||||||
|
|
||||||
ENTITY_ID_FORMAT = DOMAIN + '.{}'
|
ENTITY_ID_FORMAT = DOMAIN + '.{}'
|
||||||
|
|
||||||
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=1)
|
|
||||||
|
|
||||||
# Maps discovered services to their platforms
|
# Maps discovered services to their platforms
|
||||||
DISCOVERY_PLATFORMS = {
|
DISCOVERY_PLATFORMS = {
|
||||||
wink.DISCOVER_SENSORS: 'wink',
|
wink.DISCOVER_SENSORS: 'wink',
|
||||||
|
zwave.DISCOVER_SENSORS: 'zwave',
|
||||||
}
|
}
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
def is_on(hass, entity_id=None):
|
|
||||||
""" Returns if the sensor is open based on the statemachine. """
|
|
||||||
entity_id = entity_id or ENTITY_ID_ALL_SENSORS
|
|
||||||
|
|
||||||
return hass.states.is_state(entity_id, STATE_OPEN)
|
|
||||||
|
|
||||||
|
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
""" Track states and offer events for sensors. """
|
""" Track states and offer events for sensors. """
|
||||||
logger = logging.getLogger(__name__)
|
component = DeviceComponent(
|
||||||
|
logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL,
|
||||||
|
DISCOVERY_PLATFORMS)
|
||||||
|
|
||||||
sensors = platform_devices_from_config(
|
component.setup(config)
|
||||||
config, DOMAIN, hass, ENTITY_ID_FORMAT, logger)
|
|
||||||
|
|
||||||
@util.Throttle(MIN_TIME_BETWEEN_SCANS)
|
|
||||||
def update_sensor_states(now):
|
|
||||||
""" Update states of all sensors. """
|
|
||||||
if sensors:
|
|
||||||
logger.info("Updating sensor states")
|
|
||||||
|
|
||||||
for sensor in sensors.values():
|
|
||||||
sensor.update_ha_state(hass, True)
|
|
||||||
|
|
||||||
update_sensor_states(None)
|
|
||||||
|
|
||||||
# Track all sensors in a group
|
|
||||||
sensor_group = group.Group(
|
|
||||||
hass, GROUP_NAME_ALL_SENSORS, sensors.keys(), False)
|
|
||||||
|
|
||||||
def sensor_discovered(service, info):
|
|
||||||
""" Called when a sensor is discovered. """
|
|
||||||
platform = get_component("{}.{}".format(
|
|
||||||
DOMAIN, DISCOVERY_PLATFORMS[service]))
|
|
||||||
|
|
||||||
discovered = platform.devices_discovered(hass, config, info)
|
|
||||||
|
|
||||||
for sensor in discovered:
|
|
||||||
if sensor is not None and sensor not in sensors.values():
|
|
||||||
sensor.entity_id = util.ensure_unique_string(
|
|
||||||
ENTITY_ID_FORMAT.format(util.slugify(sensor.name)),
|
|
||||||
sensors.keys())
|
|
||||||
|
|
||||||
sensors[sensor.entity_id] = sensor
|
|
||||||
|
|
||||||
sensor.update_ha_state(hass)
|
|
||||||
|
|
||||||
sensor_group.update_tracked_entity_ids(sensors.keys())
|
|
||||||
|
|
||||||
discovery.listen(hass, DISCOVERY_PLATFORMS.keys(), sensor_discovered)
|
|
||||||
|
|
||||||
# Fire every 3 seconds
|
|
||||||
hass.track_time_change(update_sensor_states, second=range(0, 60, 3))
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
40
homeassistant/components/sensor/demo.py
Normal file
40
homeassistant/components/sensor/demo.py
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
""" Support for Wink sensors. """
|
||||||
|
from homeassistant.helpers import Device
|
||||||
|
from homeassistant.const import (
|
||||||
|
TEMP_CELCIUS, ATTR_UNIT_OF_MEASUREMENT, ATTR_FRIENDLY_NAME)
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=unused-argument
|
||||||
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
|
""" Sets up the Demo sensors. """
|
||||||
|
add_devices([
|
||||||
|
DemoSensor('Outside Temperature', 15.6, TEMP_CELCIUS),
|
||||||
|
DemoSensor('Outside Humidity', 54, '%'),
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
class DemoSensor(Device):
|
||||||
|
""" A Demo sensor. """
|
||||||
|
|
||||||
|
def __init__(self, name, state, unit_of_measurement):
|
||||||
|
self._name = name
|
||||||
|
self._state = state
|
||||||
|
self._unit_of_measurement = unit_of_measurement
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
""" Returns the name of the device. """
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def state(self):
|
||||||
|
""" Returns the state of the device. """
|
||||||
|
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,
|
||||||
|
}
|
@ -8,26 +8,17 @@ from homeassistant.components.wink import WinkSensorDevice
|
|||||||
from homeassistant.const import CONF_ACCESS_TOKEN
|
from homeassistant.const import CONF_ACCESS_TOKEN
|
||||||
|
|
||||||
|
|
||||||
def get_devices(hass, config):
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
""" Find and return Wink sensors. """
|
""" Sets up the Wink platform. """
|
||||||
token = config.get(CONF_ACCESS_TOKEN)
|
if discovery_info is None:
|
||||||
|
token = config.get(CONF_ACCESS_TOKEN)
|
||||||
|
|
||||||
if token is None:
|
if token is None:
|
||||||
logging.getLogger(__name__).error(
|
logging.getLogger(__name__).error(
|
||||||
"Missing wink access_token - "
|
"Missing wink access_token - "
|
||||||
"get one at https://winkbearertoken.appspot.com/")
|
"get one at https://winkbearertoken.appspot.com/")
|
||||||
return []
|
return
|
||||||
|
|
||||||
pywink.set_bearer_token(token)
|
pywink.set_bearer_token(token)
|
||||||
|
|
||||||
return get_sensors()
|
add_devices(WinkSensorDevice(sensor) for sensor in pywink.get_sensors())
|
||||||
|
|
||||||
|
|
||||||
def devices_discovered(hass, config, info):
|
|
||||||
""" Called when a device is discovered. """
|
|
||||||
return get_sensors()
|
|
||||||
|
|
||||||
|
|
||||||
def get_sensors():
|
|
||||||
""" Returns the Wink sensors. """
|
|
||||||
return [WinkSensorDevice(sensor) for sensor in pywink.get_sensors()]
|
|
||||||
|
135
homeassistant/components/sensor/zwave.py
Normal file
135
homeassistant/components/sensor/zwave.py
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
"""
|
||||||
|
homeassistant.components.sensor.zwave
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Interfaces with Z-Wave sensors.
|
||||||
|
"""
|
||||||
|
# pylint: disable=import-error
|
||||||
|
from openzwave.network import ZWaveNetwork
|
||||||
|
from pydispatch import dispatcher
|
||||||
|
|
||||||
|
import homeassistant.components.zwave as zwave
|
||||||
|
from homeassistant.helpers import Device
|
||||||
|
from homeassistant.const import (
|
||||||
|
ATTR_BATTERY_LEVEL, ATTR_UNIT_OF_MEASUREMENT, STATE_ON, STATE_OFF,
|
||||||
|
TEMP_CELCIUS, TEMP_FAHRENHEIT, ATTR_LOCATION)
|
||||||
|
|
||||||
|
|
||||||
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
|
""" Sets up Z-Wave sensors. """
|
||||||
|
node = zwave.NETWORK.nodes[discovery_info[zwave.ATTR_NODE_ID]]
|
||||||
|
value = node.values[discovery_info[zwave.ATTR_VALUE_ID]]
|
||||||
|
|
||||||
|
value.set_change_verified(False)
|
||||||
|
|
||||||
|
if zwave.NETWORK.controller.node_id not in node.groups[1].associations:
|
||||||
|
node.groups[1].add_association(zwave.NETWORK.controller.node_id)
|
||||||
|
|
||||||
|
if value.command_class == zwave.COMMAND_CLASS_SENSOR_BINARY:
|
||||||
|
return [ZWaveBinarySensor(value)]
|
||||||
|
|
||||||
|
elif value.command_class == zwave.COMMAND_CLASS_SENSOR_MULTILEVEL:
|
||||||
|
return [ZWaveMultilevelSensor(value)]
|
||||||
|
|
||||||
|
|
||||||
|
class ZWaveSensor(Device):
|
||||||
|
""" Represents a Z-Wave sensor. """
|
||||||
|
def __init__(self, sensor_value):
|
||||||
|
self._value = sensor_value
|
||||||
|
self._node = sensor_value.node
|
||||||
|
|
||||||
|
dispatcher.connect(
|
||||||
|
self._value_changed, ZWaveNetwork.SIGNAL_VALUE_CHANGED)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def should_poll(self):
|
||||||
|
""" False because we will push our own state to HA when changed. """
|
||||||
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def unique_id(self):
|
||||||
|
""" Returns a unique id. """
|
||||||
|
return "ZWAVE-{}-{}".format(self._node.node_id, self._value.object_id)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
""" Returns the name of the device. """
|
||||||
|
name = self._node.name or "{} {}".format(
|
||||||
|
self._node.manufacturer_name, self._node.product_name)
|
||||||
|
|
||||||
|
return "{} {}".format(name, self._value.label)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def state(self):
|
||||||
|
""" Returns the state of the sensor. """
|
||||||
|
return self._value.data
|
||||||
|
|
||||||
|
@property
|
||||||
|
def state_attributes(self):
|
||||||
|
""" Returns the state attributes. """
|
||||||
|
attrs = {
|
||||||
|
zwave.ATTR_NODE_ID: self._node.node_id,
|
||||||
|
}
|
||||||
|
|
||||||
|
battery_level = self._node.get_battery_level()
|
||||||
|
|
||||||
|
if battery_level is not None:
|
||||||
|
attrs[ATTR_BATTERY_LEVEL] = battery_level
|
||||||
|
|
||||||
|
unit = self.unit
|
||||||
|
|
||||||
|
if unit:
|
||||||
|
attrs[ATTR_UNIT_OF_MEASUREMENT] = unit
|
||||||
|
|
||||||
|
location = self._node.location
|
||||||
|
|
||||||
|
if location:
|
||||||
|
attrs[ATTR_LOCATION] = location
|
||||||
|
|
||||||
|
return attrs
|
||||||
|
|
||||||
|
@property
|
||||||
|
def unit(self):
|
||||||
|
""" Unit if sensor has one. """
|
||||||
|
return self._value.units
|
||||||
|
|
||||||
|
def _value_changed(self, value):
|
||||||
|
""" Called when a value has changed on the network. """
|
||||||
|
if self._value.value_id == value.value_id:
|
||||||
|
self.update_ha_state()
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=too-few-public-methods
|
||||||
|
class ZWaveBinarySensor(ZWaveSensor):
|
||||||
|
""" Represents a binary sensor within Z-Wave. """
|
||||||
|
|
||||||
|
@property
|
||||||
|
def state(self):
|
||||||
|
""" Returns the state of the sensor. """
|
||||||
|
return STATE_ON if self._value.data else STATE_OFF
|
||||||
|
|
||||||
|
|
||||||
|
class ZWaveMultilevelSensor(ZWaveSensor):
|
||||||
|
""" Represents a multi level sensor Z-Wave sensor. """
|
||||||
|
|
||||||
|
@property
|
||||||
|
def state(self):
|
||||||
|
""" Returns the state of the sensor. """
|
||||||
|
value = self._value.data
|
||||||
|
|
||||||
|
if self._value.units in ('C', 'F'):
|
||||||
|
return round(value, 1)
|
||||||
|
|
||||||
|
return value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def unit(self):
|
||||||
|
""" Unit of this sensor. """
|
||||||
|
unit = self._value.units
|
||||||
|
|
||||||
|
if unit == 'C':
|
||||||
|
return TEMP_CELCIUS
|
||||||
|
elif unit == 'F':
|
||||||
|
return TEMP_FAHRENHEIT
|
||||||
|
else:
|
||||||
|
return unit
|
@ -96,8 +96,8 @@ def setup(hass, config):
|
|||||||
|
|
||||||
sun = ephem.Sun() # pylint: disable=no-member
|
sun = ephem.Sun() # pylint: disable=no-member
|
||||||
|
|
||||||
latitude = config[ha.DOMAIN][CONF_LATITUDE]
|
latitude = str(config[ha.DOMAIN][CONF_LATITUDE])
|
||||||
longitude = config[ha.DOMAIN][CONF_LONGITUDE]
|
longitude = str(config[ha.DOMAIN][CONF_LONGITUDE])
|
||||||
|
|
||||||
# Validate latitude and longitude
|
# Validate latitude and longitude
|
||||||
observer = ephem.Observer()
|
observer = ephem.Observer()
|
||||||
|
@ -6,16 +6,16 @@ Component to interface with various switches that can be controlled remotely.
|
|||||||
import logging
|
import logging
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
from homeassistant.loader import get_component
|
from homeassistant.helpers.device_component import DeviceComponent
|
||||||
import homeassistant.util as util
|
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF, ATTR_ENTITY_ID)
|
STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF, ATTR_ENTITY_ID)
|
||||||
from homeassistant.helpers import (
|
from homeassistant.helpers import extract_entity_ids
|
||||||
generate_entity_id, extract_entity_ids, platform_devices_from_config)
|
|
||||||
from homeassistant.components import group, discovery, wink
|
from homeassistant.components import group, discovery, wink
|
||||||
|
|
||||||
DOMAIN = 'switch'
|
DOMAIN = 'switch'
|
||||||
DEPENDENCIES = []
|
DEPENDENCIES = []
|
||||||
|
SCAN_INTERVAL = 30
|
||||||
|
|
||||||
GROUP_NAME_ALL_SWITCHES = 'all switches'
|
GROUP_NAME_ALL_SWITCHES = 'all switches'
|
||||||
ENTITY_ID_ALL_SWITCHES = group.ENTITY_ID_FORMAT.format('all_switches')
|
ENTITY_ID_ALL_SWITCHES = group.ENTITY_ID_FORMAT.format('all_switches')
|
||||||
@ -59,45 +59,12 @@ def turn_off(hass, entity_id=None):
|
|||||||
|
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
""" Track states and offer events for switches. """
|
""" Track states and offer events for switches. """
|
||||||
logger = logging.getLogger(__name__)
|
component = DeviceComponent(
|
||||||
|
_LOGGER, DOMAIN, hass, SCAN_INTERVAL, DISCOVERY_PLATFORMS,
|
||||||
|
GROUP_NAME_ALL_SWITCHES)
|
||||||
|
component.setup(config)
|
||||||
|
|
||||||
switches = platform_devices_from_config(
|
switches = component.devices
|
||||||
config, DOMAIN, hass, ENTITY_ID_FORMAT, logger)
|
|
||||||
|
|
||||||
@util.Throttle(MIN_TIME_BETWEEN_SCANS)
|
|
||||||
def update_states(now):
|
|
||||||
""" Update states of all switches. """
|
|
||||||
if switches:
|
|
||||||
logger.info("Updating switch states")
|
|
||||||
|
|
||||||
for switch in switches.values():
|
|
||||||
switch.update_ha_state(hass, True)
|
|
||||||
|
|
||||||
update_states(None)
|
|
||||||
|
|
||||||
# Track all switches in a group
|
|
||||||
switch_group = group.Group(
|
|
||||||
hass, GROUP_NAME_ALL_SWITCHES, switches.keys(), False)
|
|
||||||
|
|
||||||
def switch_discovered(service, info):
|
|
||||||
""" Called when a switch is discovered. """
|
|
||||||
platform = get_component("{}.{}".format(
|
|
||||||
DOMAIN, DISCOVERY_PLATFORMS[service]))
|
|
||||||
|
|
||||||
discovered = platform.devices_discovered(hass, config, info)
|
|
||||||
|
|
||||||
for switch in discovered:
|
|
||||||
if switch is not None and switch not in switches.values():
|
|
||||||
switch.entity_id = generate_entity_id(
|
|
||||||
ENTITY_ID_FORMAT, switch.name, switches.keys())
|
|
||||||
|
|
||||||
switches[switch.entity_id] = switch
|
|
||||||
|
|
||||||
switch.update_ha_state(hass)
|
|
||||||
|
|
||||||
switch_group.update_tracked_entity_ids(switches.keys())
|
|
||||||
|
|
||||||
discovery.listen(hass, DISCOVERY_PLATFORMS.keys(), switch_discovered)
|
|
||||||
|
|
||||||
def handle_switch_service(service):
|
def handle_switch_service(service):
|
||||||
""" Handles calls to the switch services. """
|
""" Handles calls to the switch services. """
|
||||||
@ -114,10 +81,7 @@ def setup(hass, config):
|
|||||||
else:
|
else:
|
||||||
switch.turn_off()
|
switch.turn_off()
|
||||||
|
|
||||||
switch.update_ha_state(hass)
|
switch.update_ha_state(True)
|
||||||
|
|
||||||
# Update state every 30 seconds
|
|
||||||
hass.track_time_change(update_states, second=[0, 30])
|
|
||||||
|
|
||||||
hass.services.register(DOMAIN, SERVICE_TURN_OFF, handle_switch_service)
|
hass.services.register(DOMAIN, SERVICE_TURN_OFF, handle_switch_service)
|
||||||
|
|
||||||
|
42
homeassistant/components/switch/demo.py
Normal file
42
homeassistant/components/switch/demo.py
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
""" Demo platform that has two fake switchces. """
|
||||||
|
from homeassistant.helpers import ToggleDevice
|
||||||
|
from homeassistant.const import STATE_ON, STATE_OFF, DEVICE_DEFAULT_NAME
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=unused-argument
|
||||||
|
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
||||||
|
""" Find and return demo switches. """
|
||||||
|
add_devices_callback([
|
||||||
|
DemoSwitch('Ceiling', STATE_ON),
|
||||||
|
DemoSwitch('AC', STATE_OFF)
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
class DemoSwitch(ToggleDevice):
|
||||||
|
""" Provides a demo switch. """
|
||||||
|
def __init__(self, name, state):
|
||||||
|
self._name = name or DEVICE_DEFAULT_NAME
|
||||||
|
self._state = state
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
""" Returns the name of the device if any. """
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def state(self):
|
||||||
|
""" Returns the name of the device if any. """
|
||||||
|
return self._state
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_on(self):
|
||||||
|
""" True if device is on. """
|
||||||
|
return self._state == STATE_ON
|
||||||
|
|
||||||
|
def turn_on(self, **kwargs):
|
||||||
|
""" Turn the device on. """
|
||||||
|
self._state = STATE_ON
|
||||||
|
|
||||||
|
def turn_off(self, **kwargs):
|
||||||
|
""" Turn the device off. """
|
||||||
|
self._state = STATE_OFF
|
@ -7,14 +7,15 @@ from homeassistant.helpers import ToggleDevice
|
|||||||
import tellcore.constants as tellcore_constants
|
import tellcore.constants as tellcore_constants
|
||||||
|
|
||||||
|
|
||||||
def get_devices(hass, config):
|
# pylint: disable=unused-argument
|
||||||
""" Find and return Tellstick switches. """
|
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
||||||
|
""" Find and return tellstick switches. """
|
||||||
try:
|
try:
|
||||||
import tellcore.telldus as telldus
|
import tellcore.telldus as telldus
|
||||||
except ImportError:
|
except ImportError:
|
||||||
logging.getLogger(__name__).exception(
|
logging.getLogger(__name__).exception(
|
||||||
"Failed to import tellcore")
|
"Failed to import tellcore")
|
||||||
return []
|
return
|
||||||
|
|
||||||
core = telldus.TelldusCore()
|
core = telldus.TelldusCore()
|
||||||
switches_and_lights = core.devices()
|
switches_and_lights = core.devices()
|
||||||
@ -25,7 +26,7 @@ def get_devices(hass, config):
|
|||||||
if not switch.methods(tellcore_constants.TELLSTICK_DIM):
|
if not switch.methods(tellcore_constants.TELLSTICK_DIM):
|
||||||
switches.append(TellstickSwitchDevice(switch))
|
switches.append(TellstickSwitchDevice(switch))
|
||||||
|
|
||||||
return switches
|
add_devices_callback(switches)
|
||||||
|
|
||||||
|
|
||||||
class TellstickSwitchDevice(ToggleDevice):
|
class TellstickSwitchDevice(ToggleDevice):
|
||||||
|
@ -2,55 +2,40 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from homeassistant.helpers import ToggleDevice
|
from homeassistant.helpers import ToggleDevice
|
||||||
from homeassistant.const import ATTR_FRIENDLY_NAME
|
|
||||||
from homeassistant.components.switch import (
|
from homeassistant.components.switch import (
|
||||||
ATTR_TODAY_MWH, ATTR_CURRENT_POWER_MWH)
|
ATTR_TODAY_MWH, ATTR_CURRENT_POWER_MWH)
|
||||||
|
|
||||||
|
|
||||||
def get_devices(hass, config):
|
# pylint: disable=unused-argument
|
||||||
""" Find and return WeMo switches. """
|
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
||||||
|
""" Find and return wemo switches. """
|
||||||
pywemo, _ = get_pywemo()
|
|
||||||
|
|
||||||
if pywemo is None:
|
|
||||||
return []
|
|
||||||
|
|
||||||
logging.getLogger(__name__).info("Scanning for WeMo devices")
|
|
||||||
switches = pywemo.discover_devices()
|
|
||||||
|
|
||||||
# Filter out the switches and wrap in WemoSwitch object
|
|
||||||
return [WemoSwitch(switch) for switch in switches
|
|
||||||
if isinstance(switch, pywemo.Switch)]
|
|
||||||
|
|
||||||
|
|
||||||
def devices_discovered(hass, config, info):
|
|
||||||
""" Called when a device is discovered. """
|
|
||||||
_, discovery = get_pywemo()
|
|
||||||
|
|
||||||
if discovery is None:
|
|
||||||
return []
|
|
||||||
|
|
||||||
device = discovery.device_from_description(info)
|
|
||||||
|
|
||||||
return [] if device is None else [WemoSwitch(device)]
|
|
||||||
|
|
||||||
|
|
||||||
def get_pywemo():
|
|
||||||
""" Tries to import PyWemo. """
|
|
||||||
try:
|
try:
|
||||||
# pylint: disable=no-name-in-module, import-error
|
# pylint: disable=no-name-in-module, import-error
|
||||||
import homeassistant.external.pywemo.pywemo as pywemo
|
import homeassistant.external.pywemo.pywemo as pywemo
|
||||||
import homeassistant.external.pywemo.pywemo.discovery as discovery
|
import homeassistant.external.pywemo.pywemo.discovery as discovery
|
||||||
|
|
||||||
return pywemo, discovery
|
|
||||||
|
|
||||||
except ImportError:
|
except ImportError:
|
||||||
logging.getLogger(__name__).exception((
|
logging.getLogger(__name__).exception((
|
||||||
"Failed to import pywemo. "
|
"Failed to import pywemo. "
|
||||||
"Did you maybe not run `git submodule init` "
|
"Did you maybe not run `git submodule init` "
|
||||||
"and `git submodule update`?"))
|
"and `git submodule update`?"))
|
||||||
|
|
||||||
return None, None
|
return
|
||||||
|
|
||||||
|
if discovery_info is not None:
|
||||||
|
device = discovery.device_from_description(discovery_info)
|
||||||
|
|
||||||
|
if device:
|
||||||
|
add_devices_callback([WemoSwitch(device)])
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
logging.getLogger(__name__).info("Scanning for WeMo devices")
|
||||||
|
switches = pywemo.discover_devices()
|
||||||
|
|
||||||
|
# Filter out the switches and wrap in WemoSwitch object
|
||||||
|
add_devices_callback(
|
||||||
|
[WemoSwitch(switch) for switch in switches
|
||||||
|
if isinstance(switch, pywemo.Switch)])
|
||||||
|
|
||||||
|
|
||||||
class WemoSwitch(ToggleDevice):
|
class WemoSwitch(ToggleDevice):
|
||||||
@ -75,12 +60,9 @@ class WemoSwitch(ToggleDevice):
|
|||||||
cur_info = self.wemo.insight_params
|
cur_info = self.wemo.insight_params
|
||||||
|
|
||||||
return {
|
return {
|
||||||
ATTR_FRIENDLY_NAME: self.wemo.name,
|
|
||||||
ATTR_CURRENT_POWER_MWH: cur_info['currentpower'],
|
ATTR_CURRENT_POWER_MWH: cur_info['currentpower'],
|
||||||
ATTR_TODAY_MWH: cur_info['todaymw']
|
ATTR_TODAY_MWH: cur_info['todaymw']
|
||||||
}
|
}
|
||||||
else:
|
|
||||||
return {ATTR_FRIENDLY_NAME: self.wemo.name}
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_on(self):
|
def is_on(self):
|
||||||
|
@ -8,26 +8,17 @@ from homeassistant.components.wink import WinkToggleDevice
|
|||||||
from homeassistant.const import CONF_ACCESS_TOKEN
|
from homeassistant.const import CONF_ACCESS_TOKEN
|
||||||
|
|
||||||
|
|
||||||
def get_devices(hass, config):
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
""" Find and return Wink switches. """
|
""" Sets up the Wink platform. """
|
||||||
token = config.get(CONF_ACCESS_TOKEN)
|
if discovery_info is None:
|
||||||
|
token = config.get(CONF_ACCESS_TOKEN)
|
||||||
|
|
||||||
if token is None:
|
if token is None:
|
||||||
logging.getLogger(__name__).error(
|
logging.getLogger(__name__).error(
|
||||||
"Missing wink access_token - "
|
"Missing wink access_token - "
|
||||||
"get one at https://winkbearertoken.appspot.com/")
|
"get one at https://winkbearertoken.appspot.com/")
|
||||||
return []
|
return
|
||||||
|
|
||||||
pywink.set_bearer_token(token)
|
pywink.set_bearer_token(token)
|
||||||
|
|
||||||
return get_switches()
|
add_devices(WinkToggleDevice(switch) for switch in pywink.get_switches())
|
||||||
|
|
||||||
|
|
||||||
def devices_discovered(hass, config, info):
|
|
||||||
""" Called when a device is discovered. """
|
|
||||||
return get_switches()
|
|
||||||
|
|
||||||
|
|
||||||
def get_switches():
|
|
||||||
""" Returns the Wink switches. """
|
|
||||||
return [WinkToggleDevice(switch) for switch in pywink.get_switches()]
|
|
||||||
|
@ -5,25 +5,22 @@ homeassistant.components.thermostat
|
|||||||
Provides functionality to interact with thermostats.
|
Provides functionality to interact with thermostats.
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
from datetime import timedelta
|
|
||||||
|
|
||||||
from homeassistant.helpers import (
|
from homeassistant.helpers.device_component import DeviceComponent
|
||||||
extract_entity_ids, platform_devices_from_config)
|
|
||||||
import homeassistant.util as util
|
import homeassistant.util as util
|
||||||
from homeassistant.helpers import Device
|
from homeassistant.helpers import Device, extract_entity_ids
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_ENTITY_ID, ATTR_TEMPERATURE, ATTR_UNIT_OF_MEASUREMENT,
|
ATTR_ENTITY_ID, ATTR_TEMPERATURE, ATTR_UNIT_OF_MEASUREMENT,
|
||||||
STATE_ON, STATE_OFF)
|
STATE_ON, STATE_OFF)
|
||||||
|
|
||||||
DOMAIN = "thermostat"
|
DOMAIN = "thermostat"
|
||||||
ENTITY_ID_FORMAT = DOMAIN + ".{}"
|
|
||||||
|
|
||||||
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10)
|
|
||||||
|
|
||||||
DEPENDENCIES = []
|
DEPENDENCIES = []
|
||||||
|
|
||||||
SERVICE_TURN_AWAY_MODE_ON = "turn_away_mode_on"
|
ENTITY_ID_FORMAT = DOMAIN + ".{}"
|
||||||
SERVICE_TURN_AWAY_MODE_OFF = "turn_away_mode_off"
|
SCAN_INTERVAL = 60
|
||||||
|
|
||||||
|
SERVICE_SET_AWAY_MODE = "set_away_mode"
|
||||||
SERVICE_SET_TEMPERATURE = "set_temperature"
|
SERVICE_SET_TEMPERATURE = "set_temperature"
|
||||||
|
|
||||||
ATTR_CURRENT_TEMPERATURE = "current_temperature"
|
ATTR_CURRENT_TEMPERATURE = "current_temperature"
|
||||||
@ -32,18 +29,16 @@ ATTR_AWAY_MODE = "away_mode"
|
|||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def turn_away_mode_on(hass, entity_id=None):
|
def set_away_mode(hass, away_mode, entity_id=None):
|
||||||
""" Turn all or specified thermostat away mode on. """
|
""" Turn all or specified thermostat away mode on. """
|
||||||
data = {ATTR_ENTITY_ID: entity_id} if entity_id else None
|
data = {
|
||||||
|
ATTR_AWAY_MODE: away_mode
|
||||||
|
}
|
||||||
|
|
||||||
hass.services.call(DOMAIN, SERVICE_TURN_AWAY_MODE_ON, data)
|
if entity_id:
|
||||||
|
data[ATTR_ENTITY_ID] = entity_id
|
||||||
|
|
||||||
|
hass.services.call(DOMAIN, SERVICE_SET_AWAY_MODE, data)
|
||||||
def turn_away_mode_off(hass, entity_id=None):
|
|
||||||
""" Turn all or specified thermostat away mode off. """
|
|
||||||
data = {ATTR_ENTITY_ID: entity_id} if entity_id else None
|
|
||||||
|
|
||||||
hass.services.call(DOMAIN, SERVICE_TURN_AWAY_MODE_OFF, data)
|
|
||||||
|
|
||||||
|
|
||||||
def set_temperature(hass, temperature, entity_id=None):
|
def set_temperature(hass, temperature, entity_id=None):
|
||||||
@ -58,26 +53,10 @@ def set_temperature(hass, temperature, entity_id=None):
|
|||||||
|
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
""" Setup thermostats. """
|
""" Setup thermostats. """
|
||||||
|
component = DeviceComponent(_LOGGER, DOMAIN, hass, SCAN_INTERVAL)
|
||||||
|
component.setup(config)
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
thermostats = component.devices
|
||||||
|
|
||||||
thermostats = platform_devices_from_config(
|
|
||||||
config, DOMAIN, hass, ENTITY_ID_FORMAT, _LOGGER)
|
|
||||||
|
|
||||||
if not thermostats:
|
|
||||||
return False
|
|
||||||
|
|
||||||
@util.Throttle(MIN_TIME_BETWEEN_SCANS)
|
|
||||||
def update_state(now):
|
|
||||||
""" Update thermostat state. """
|
|
||||||
logger.info("Updating thermostat state")
|
|
||||||
|
|
||||||
for thermostat in thermostats.values():
|
|
||||||
thermostat.update_ha_state(hass, True)
|
|
||||||
|
|
||||||
# Update state every minute
|
|
||||||
hass.track_time_change(update_state, second=[0])
|
|
||||||
update_state(None)
|
|
||||||
|
|
||||||
def thermostat_service(service):
|
def thermostat_service(service):
|
||||||
""" Handles calls to the services. """
|
""" Handles calls to the services. """
|
||||||
@ -90,13 +69,20 @@ def setup(hass, config):
|
|||||||
if not target_thermostats:
|
if not target_thermostats:
|
||||||
target_thermostats = thermostats.values()
|
target_thermostats = thermostats.values()
|
||||||
|
|
||||||
if service.service == SERVICE_TURN_AWAY_MODE_ON:
|
if service.service == SERVICE_SET_AWAY_MODE:
|
||||||
for thermostat in target_thermostats:
|
away_mode = service.data.get(ATTR_AWAY_MODE)
|
||||||
thermostat.turn_away_mode_on()
|
|
||||||
|
|
||||||
elif service.service == SERVICE_TURN_AWAY_MODE_OFF:
|
if away_mode is None:
|
||||||
for thermostat in target_thermostats:
|
_LOGGER.error(
|
||||||
thermostat.turn_away_mode_off()
|
"Received call to %s without attribute %s",
|
||||||
|
SERVICE_SET_AWAY_MODE, ATTR_AWAY_MODE)
|
||||||
|
|
||||||
|
elif away_mode:
|
||||||
|
for thermostat in target_thermostats:
|
||||||
|
thermostat.turn_away_mode_on()
|
||||||
|
else:
|
||||||
|
for thermostat in target_thermostats:
|
||||||
|
thermostat.turn_away_mode_off()
|
||||||
|
|
||||||
elif service.service == SERVICE_SET_TEMPERATURE:
|
elif service.service == SERVICE_SET_TEMPERATURE:
|
||||||
temperature = util.convert(
|
temperature = util.convert(
|
||||||
@ -109,13 +95,10 @@ def setup(hass, config):
|
|||||||
thermostat.set_temperature(temperature)
|
thermostat.set_temperature(temperature)
|
||||||
|
|
||||||
for thermostat in target_thermostats:
|
for thermostat in target_thermostats:
|
||||||
thermostat.update_ha_state(hass, True)
|
thermostat.update_ha_state(True)
|
||||||
|
|
||||||
hass.services.register(
|
hass.services.register(
|
||||||
DOMAIN, SERVICE_TURN_AWAY_MODE_OFF, thermostat_service)
|
DOMAIN, SERVICE_SET_AWAY_MODE, thermostat_service)
|
||||||
|
|
||||||
hass.services.register(
|
|
||||||
DOMAIN, SERVICE_TURN_AWAY_MODE_ON, thermostat_service)
|
|
||||||
|
|
||||||
hass.services.register(
|
hass.services.register(
|
||||||
DOMAIN, SERVICE_SET_TEMPERATURE, thermostat_service)
|
DOMAIN, SERVICE_SET_TEMPERATURE, thermostat_service)
|
||||||
|
64
homeassistant/components/thermostat/demo.py
Normal file
64
homeassistant/components/thermostat/demo.py
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
"""
|
||||||
|
Demo platform that offers a fake thermostat.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from homeassistant.components.thermostat import ThermostatDevice
|
||||||
|
from homeassistant.const import TEMP_CELCIUS, TEMP_FAHRENHEIT
|
||||||
|
|
||||||
|
|
||||||
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
|
""" Sets up the Demo thermostats. """
|
||||||
|
add_devices([
|
||||||
|
DemoThermostat("Nest", 21, TEMP_CELCIUS, False, 19),
|
||||||
|
DemoThermostat("Thermostat", 68, TEMP_FAHRENHEIT, True, 77),
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=too-many-arguments
|
||||||
|
class DemoThermostat(ThermostatDevice):
|
||||||
|
""" Represents a HeatControl within Home Assistant. """
|
||||||
|
|
||||||
|
def __init__(self, name, target_temperature, unit_of_measurement,
|
||||||
|
away, current_temperature):
|
||||||
|
self._name = name
|
||||||
|
self._target_temperature = target_temperature
|
||||||
|
self._unit_of_measurement = unit_of_measurement
|
||||||
|
self._away = away
|
||||||
|
self._current_temperature = current_temperature
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
""" Returns the name. """
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def unit_of_measurement(self):
|
||||||
|
""" Returns the unit of measurement. """
|
||||||
|
return self._unit_of_measurement
|
||||||
|
|
||||||
|
@property
|
||||||
|
def current_temperature(self):
|
||||||
|
""" Returns the current temperature. """
|
||||||
|
return self._current_temperature
|
||||||
|
|
||||||
|
@property
|
||||||
|
def target_temperature(self):
|
||||||
|
""" Returns the temperature we try to reach. """
|
||||||
|
return self._target_temperature
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_away_mode_on(self):
|
||||||
|
""" Returns if away mode is on. """
|
||||||
|
return self._away
|
||||||
|
|
||||||
|
def set_temperature(self, temperature):
|
||||||
|
""" Set new target temperature """
|
||||||
|
self._target_temperature = temperature
|
||||||
|
|
||||||
|
def turn_away_mode_on(self):
|
||||||
|
""" Turns away mode on. """
|
||||||
|
self._away = True
|
||||||
|
|
||||||
|
def turn_away_mode_off(self):
|
||||||
|
""" Turns away mode off. """
|
||||||
|
self._away = False
|
@ -69,11 +69,11 @@ TOL_TEMP = 0.3
|
|||||||
|
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
def get_devices(hass, config):
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
""" Gets thermostats. """
|
""" Sets up the heat control thermostat. """
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
return [HeatControl(hass, config, logger)]
|
add_devices([HeatControl(hass, config, logger)])
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=too-many-instance-attributes
|
# pylint: disable=too-many-instance-attributes
|
||||||
|
@ -7,8 +7,9 @@ from homeassistant.components.thermostat import ThermostatDevice
|
|||||||
from homeassistant.const import (CONF_USERNAME, CONF_PASSWORD, TEMP_CELCIUS)
|
from homeassistant.const import (CONF_USERNAME, CONF_PASSWORD, TEMP_CELCIUS)
|
||||||
|
|
||||||
|
|
||||||
def get_devices(hass, config):
|
# pylint: disable=unused-argument
|
||||||
""" Gets Nest thermostats. """
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
|
""" Sets up the nest thermostat. """
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
username = config.get(CONF_USERNAME)
|
username = config.get(CONF_USERNAME)
|
||||||
@ -17,7 +18,7 @@ def get_devices(hass, config):
|
|||||||
if username is None or password is None:
|
if username is None or password is None:
|
||||||
logger.error("Missing required configuration items %s or %s",
|
logger.error("Missing required configuration items %s or %s",
|
||||||
CONF_USERNAME, CONF_PASSWORD)
|
CONF_USERNAME, CONF_PASSWORD)
|
||||||
return []
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import nest
|
import nest
|
||||||
@ -26,14 +27,15 @@ def get_devices(hass, config):
|
|||||||
"Error while importing dependency nest. "
|
"Error while importing dependency nest. "
|
||||||
"Did you maybe not install the python-nest dependency?")
|
"Did you maybe not install the python-nest dependency?")
|
||||||
|
|
||||||
return []
|
return
|
||||||
|
|
||||||
napi = nest.Nest(username, password)
|
napi = nest.Nest(username, password)
|
||||||
|
|
||||||
return [
|
add_devices([
|
||||||
NestThermostat(structure, device)
|
NestThermostat(structure, device)
|
||||||
for structure in napi.structures
|
for structure in napi.structures
|
||||||
for device in structure.devices]
|
for device in structure.devices
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
class NestThermostat(ThermostatDevice):
|
class NestThermostat(ThermostatDevice):
|
||||||
|
@ -92,7 +92,7 @@ class WinkSensorDevice(Device):
|
|||||||
|
|
||||||
|
|
||||||
class WinkToggleDevice(ToggleDevice):
|
class WinkToggleDevice(ToggleDevice):
|
||||||
""" represents a WeMo switch within home assistant. """
|
""" represents a Wink switch within home assistant. """
|
||||||
|
|
||||||
def __init__(self, wink):
|
def __init__(self, wink):
|
||||||
self.wink = wink
|
self.wink = wink
|
||||||
|
126
homeassistant/components/zwave.py
Normal file
126
homeassistant/components/zwave.py
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
"""
|
||||||
|
homeassistant.components.zwave
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Connects Home Assistant to a Z-Wave network.
|
||||||
|
"""
|
||||||
|
from pprint import pprint
|
||||||
|
|
||||||
|
from homeassistant import bootstrap
|
||||||
|
from homeassistant.const import (
|
||||||
|
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP,
|
||||||
|
EVENT_PLATFORM_DISCOVERED, ATTR_SERVICE, ATTR_DISCOVERED)
|
||||||
|
|
||||||
|
DOMAIN = "zwave"
|
||||||
|
DEPENDENCIES = []
|
||||||
|
|
||||||
|
CONF_USB_STICK_PATH = "usb_path"
|
||||||
|
DEFAULT_CONF_USB_STICK_PATH = "/zwaveusbstick"
|
||||||
|
CONF_DEBUG = "debug"
|
||||||
|
|
||||||
|
DISCOVER_SENSORS = "zwave.sensors"
|
||||||
|
|
||||||
|
COMMAND_CLASS_SENSOR_BINARY = 48
|
||||||
|
COMMAND_CLASS_SENSOR_MULTILEVEL = 49
|
||||||
|
COMMAND_CLASS_BATTERY = 128
|
||||||
|
|
||||||
|
# list of tuple (DOMAIN, discovered service, supported command classes)
|
||||||
|
DISCOVERY_COMPONENTS = [
|
||||||
|
('sensor', DISCOVER_SENSORS,
|
||||||
|
[COMMAND_CLASS_SENSOR_BINARY, COMMAND_CLASS_SENSOR_MULTILEVEL]),
|
||||||
|
]
|
||||||
|
|
||||||
|
ATTR_NODE_ID = "node_id"
|
||||||
|
ATTR_VALUE_ID = "value_id"
|
||||||
|
|
||||||
|
NETWORK = None
|
||||||
|
|
||||||
|
|
||||||
|
def _obj_to_dict(obj):
|
||||||
|
""" Converts an obj into a hash for debug. """
|
||||||
|
return {key: getattr(obj, key) for key
|
||||||
|
in dir(obj)
|
||||||
|
if key[0] != '_' and not hasattr(getattr(obj, key), '__call__')}
|
||||||
|
|
||||||
|
|
||||||
|
def nice_print_node(node):
|
||||||
|
""" Prints a nice formatted node to the output (debug method) """
|
||||||
|
node_dict = _obj_to_dict(node)
|
||||||
|
node_dict['values'] = {value_id: _obj_to_dict(value)
|
||||||
|
for value_id, value in node.values.items()}
|
||||||
|
|
||||||
|
print("\n\n\n")
|
||||||
|
print("FOUND NODE", node.product_name)
|
||||||
|
pprint(node_dict)
|
||||||
|
print("\n\n\n")
|
||||||
|
|
||||||
|
|
||||||
|
def setup(hass, config):
|
||||||
|
"""
|
||||||
|
Setup Z-wave.
|
||||||
|
Will automatically load components to support devices found on the network.
|
||||||
|
"""
|
||||||
|
# pylint: disable=global-statement, import-error
|
||||||
|
global NETWORK
|
||||||
|
|
||||||
|
from pydispatch import dispatcher
|
||||||
|
from openzwave.option import ZWaveOption
|
||||||
|
from openzwave.network import ZWaveNetwork
|
||||||
|
|
||||||
|
use_debug = config[DOMAIN].get(CONF_DEBUG) == '1'
|
||||||
|
|
||||||
|
# Setup options
|
||||||
|
options = ZWaveOption(
|
||||||
|
config[DOMAIN].get(CONF_USB_STICK_PATH, DEFAULT_CONF_USB_STICK_PATH),
|
||||||
|
user_path=hass.config_dir)
|
||||||
|
|
||||||
|
options.set_console_output(use_debug)
|
||||||
|
options.lock()
|
||||||
|
|
||||||
|
NETWORK = ZWaveNetwork(options, autostart=False)
|
||||||
|
|
||||||
|
if use_debug:
|
||||||
|
def log_all(signal, value=None):
|
||||||
|
""" Log all the louie signals. """
|
||||||
|
print("")
|
||||||
|
print("LOUIE SIGNAL *****", signal)
|
||||||
|
if value and signal in (ZWaveNetwork.SIGNAL_VALUE_CHANGED,
|
||||||
|
ZWaveNetwork.SIGNAL_VALUE_ADDED):
|
||||||
|
pprint(_obj_to_dict(value))
|
||||||
|
print("")
|
||||||
|
|
||||||
|
dispatcher.connect(log_all, weak=False)
|
||||||
|
|
||||||
|
def value_added(node, value):
|
||||||
|
""" Called when a value is added to a node on the network. """
|
||||||
|
for component, discovery_service, command_ids in DISCOVERY_COMPONENTS:
|
||||||
|
if value.command_class in command_ids:
|
||||||
|
# Ensure component is loaded
|
||||||
|
if component not in hass.components:
|
||||||
|
bootstrap.setup_component(hass, component, config)
|
||||||
|
|
||||||
|
# Fire discovery event
|
||||||
|
hass.bus.fire(EVENT_PLATFORM_DISCOVERED, {
|
||||||
|
ATTR_SERVICE: discovery_service,
|
||||||
|
ATTR_DISCOVERED: {
|
||||||
|
ATTR_NODE_ID: node.node_id,
|
||||||
|
ATTR_VALUE_ID: value.value_id,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
dispatcher.connect(
|
||||||
|
value_added, ZWaveNetwork.SIGNAL_VALUE_ADDED, weak=False)
|
||||||
|
|
||||||
|
def stop_zwave(event):
|
||||||
|
""" Stop Z-wave. """
|
||||||
|
NETWORK.stop()
|
||||||
|
|
||||||
|
def start_zwave(event):
|
||||||
|
""" Called when Home Assistant starts up. """
|
||||||
|
NETWORK.start()
|
||||||
|
|
||||||
|
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_zwave)
|
||||||
|
|
||||||
|
hass.bus.listen_once(EVENT_HOMEASSISTANT_START, start_zwave)
|
||||||
|
|
||||||
|
return True
|
@ -65,14 +65,17 @@ ATTR_UNIT_OF_MEASUREMENT = "unit_of_measurement"
|
|||||||
|
|
||||||
# Temperature attribute
|
# Temperature attribute
|
||||||
ATTR_TEMPERATURE = "temperature"
|
ATTR_TEMPERATURE = "temperature"
|
||||||
|
|
||||||
# #### MISC ####
|
|
||||||
TEMP_CELCIUS = "°C"
|
TEMP_CELCIUS = "°C"
|
||||||
TEMP_FAHRENHEIT = "°F"
|
TEMP_FAHRENHEIT = "°F"
|
||||||
|
|
||||||
# Contains the information that is discovered
|
# Contains the information that is discovered
|
||||||
ATTR_DISCOVERED = "discovered"
|
ATTR_DISCOVERED = "discovered"
|
||||||
|
|
||||||
|
# Location of the device/sensor
|
||||||
|
ATTR_LOCATION = "location"
|
||||||
|
|
||||||
|
ATTR_BATTERY_LEVEL = "battery_level"
|
||||||
|
|
||||||
# #### SERVICES ####
|
# #### SERVICES ####
|
||||||
SERVICE_HOMEASSISTANT_STOP = "stop"
|
SERVICE_HOMEASSISTANT_STOP = "stop"
|
||||||
|
|
||||||
|
2
homeassistant/external/netdisco
vendored
2
homeassistant/external/netdisco
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 68877783cb989b874cbcaec5f388a8a4345891a6
|
Subproject commit 6e712dd65e474bf623b35c54f5290dbac192c7e4
|
@ -7,8 +7,8 @@ from homeassistant import NoEntitySpecifiedError
|
|||||||
|
|
||||||
from homeassistant.loader import get_component
|
from homeassistant.loader import get_component
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_ENTITY_ID, STATE_ON, STATE_OFF, CONF_PLATFORM, CONF_TYPE,
|
ATTR_ENTITY_ID, ATTR_FRIENDLY_NAME, STATE_ON, STATE_OFF, CONF_PLATFORM,
|
||||||
DEVICE_DEFAULT_NAME)
|
CONF_TYPE, DEVICE_DEFAULT_NAME)
|
||||||
from homeassistant.util import ensure_unique_string, slugify
|
from homeassistant.util import ensure_unique_string, slugify
|
||||||
|
|
||||||
|
|
||||||
@ -159,6 +159,8 @@ def platform_devices_from_config(config, domain, hass,
|
|||||||
no_name_count = 0
|
no_name_count = 0
|
||||||
|
|
||||||
for device in devices:
|
for device in devices:
|
||||||
|
device.hass = hass
|
||||||
|
|
||||||
# Get the name or set to default if none given
|
# Get the name or set to default if none given
|
||||||
name = device.name or DEVICE_DEFAULT_NAME
|
name = device.name or DEVICE_DEFAULT_NAME
|
||||||
|
|
||||||
@ -179,8 +181,17 @@ class Device(object):
|
|||||||
""" ABC for Home Assistant devices. """
|
""" ABC for Home Assistant devices. """
|
||||||
# pylint: disable=no-self-use
|
# pylint: disable=no-self-use
|
||||||
|
|
||||||
|
hass = None
|
||||||
entity_id = None
|
entity_id = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def should_poll(self):
|
||||||
|
"""
|
||||||
|
Return True if device has to be polled for state.
|
||||||
|
False if device pushes its state to HA.
|
||||||
|
"""
|
||||||
|
return True
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def unique_id(self):
|
def unique_id(self):
|
||||||
""" Returns a unique id. """
|
""" Returns a unique id. """
|
||||||
@ -216,17 +227,20 @@ class Device(object):
|
|||||||
|
|
||||||
def get_state_attributes(self):
|
def get_state_attributes(self):
|
||||||
""" Returns optional state attributes. """
|
""" Returns optional state attributes. """
|
||||||
return {}
|
return None
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
""" Retrieve latest state from the real device. """
|
""" Retrieve latest state from the real device. """
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def update_ha_state(self, hass, force_refresh=False):
|
def update_ha_state(self, force_refresh=False):
|
||||||
"""
|
"""
|
||||||
Updates Home Assistant with current state of device.
|
Updates Home Assistant with current state of device.
|
||||||
If force_refresh == True will update device before setting state.
|
If force_refresh == True will update device before setting state.
|
||||||
"""
|
"""
|
||||||
|
if self.hass is None:
|
||||||
|
raise RuntimeError("Attribute hass is None for {}".format(self))
|
||||||
|
|
||||||
if self.entity_id is None:
|
if self.entity_id is None:
|
||||||
raise NoEntitySpecifiedError(
|
raise NoEntitySpecifiedError(
|
||||||
"No entity specified for device {}".format(self.name))
|
"No entity specified for device {}".format(self.name))
|
||||||
@ -234,13 +248,20 @@ class Device(object):
|
|||||||
if force_refresh:
|
if force_refresh:
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
return hass.states.set(self.entity_id, self.state,
|
attr = self.state_attributes or {}
|
||||||
self.state_attributes)
|
|
||||||
|
if ATTR_FRIENDLY_NAME not in attr and self.name:
|
||||||
|
attr[ATTR_FRIENDLY_NAME] = self.name
|
||||||
|
|
||||||
|
return self.hass.states.set(self.entity_id, self.state, attr)
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
return (isinstance(other, Device) and
|
return (isinstance(other, Device) and
|
||||||
other.unique_id == self.unique_id)
|
other.unique_id == self.unique_id)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<Device {}: {}>".format(self.name, self.state)
|
||||||
|
|
||||||
|
|
||||||
class ToggleDevice(Device):
|
class ToggleDevice(Device):
|
||||||
""" ABC for devices that can be turned on and off. """
|
""" ABC for devices that can be turned on and off. """
|
127
homeassistant/helpers/device_component.py
Normal file
127
homeassistant/helpers/device_component.py
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
"""
|
||||||
|
Provides helpers for components that handle devices.
|
||||||
|
"""
|
||||||
|
from homeassistant.loader import get_component
|
||||||
|
from homeassistant.helpers import generate_entity_id, config_per_platform
|
||||||
|
from homeassistant.components import group, discovery
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceComponent(object):
|
||||||
|
# pylint: disable=too-many-instance-attributes
|
||||||
|
# pylint: disable=too-many-arguments,too-few-public-methods
|
||||||
|
"""
|
||||||
|
Helper class that will help a device component manage its devices.
|
||||||
|
"""
|
||||||
|
def __init__(self, logger, domain, hass, scan_interval,
|
||||||
|
discovery_platforms=None, group_name=None):
|
||||||
|
self.logger = logger
|
||||||
|
self.hass = hass
|
||||||
|
|
||||||
|
self.domain = domain
|
||||||
|
self.entity_id_format = domain + '.{}'
|
||||||
|
self.scan_interval = scan_interval
|
||||||
|
self.discovery_platforms = discovery_platforms
|
||||||
|
self.group_name = group_name
|
||||||
|
|
||||||
|
self.devices = {}
|
||||||
|
self.group = None
|
||||||
|
self.is_polling = False
|
||||||
|
|
||||||
|
def setup(self, config):
|
||||||
|
"""
|
||||||
|
Sets up a full device component:
|
||||||
|
- Loads the platforms from the config
|
||||||
|
- Will update devices on an interval
|
||||||
|
- Will listen for supported discovered platforms
|
||||||
|
"""
|
||||||
|
|
||||||
|
# only setup group if name is given
|
||||||
|
if self.group_name is None:
|
||||||
|
self.group = None
|
||||||
|
else:
|
||||||
|
self.group = group.Group(self.hass, self.group_name,
|
||||||
|
user_defined=False)
|
||||||
|
|
||||||
|
# Look in config for Domain, Domain 2, Domain 3 etc and load them
|
||||||
|
for p_type, p_config in \
|
||||||
|
config_per_platform(config, self.domain, self.logger):
|
||||||
|
|
||||||
|
self._setup_platform(p_type, p_config)
|
||||||
|
|
||||||
|
if self.discovery_platforms:
|
||||||
|
discovery.listen(self.hass, self.discovery_platforms.keys(),
|
||||||
|
self._device_discovered)
|
||||||
|
|
||||||
|
def _update_device_states(self, now):
|
||||||
|
""" Update the states of all the lights. """
|
||||||
|
self.logger.info("Updating %s states", self.domain)
|
||||||
|
|
||||||
|
for device in self.devices.values():
|
||||||
|
if device.should_poll:
|
||||||
|
device.update_ha_state(True)
|
||||||
|
|
||||||
|
def _device_discovered(self, service, info):
|
||||||
|
""" Called when a device is discovered. """
|
||||||
|
if service not in self.discovery_platforms:
|
||||||
|
return
|
||||||
|
|
||||||
|
self._setup_platform(self.discovery_platforms[service], {}, info)
|
||||||
|
|
||||||
|
def _add_devices(self, new_devices):
|
||||||
|
"""
|
||||||
|
Takes in a list of new devices. For each device will see if it already
|
||||||
|
exists. If not, will add it, set it up and push the first state.
|
||||||
|
"""
|
||||||
|
for device in new_devices:
|
||||||
|
if device is not None and device not in self.devices.values():
|
||||||
|
device.hass = self.hass
|
||||||
|
|
||||||
|
device.entity_id = generate_entity_id(
|
||||||
|
self.entity_id_format, device.name, self.devices.keys())
|
||||||
|
|
||||||
|
self.devices[device.entity_id] = device
|
||||||
|
|
||||||
|
device.update_ha_state()
|
||||||
|
|
||||||
|
if self.group is not None:
|
||||||
|
self.group.update_tracked_entity_ids(self.devices.keys())
|
||||||
|
|
||||||
|
self._start_polling()
|
||||||
|
|
||||||
|
def _start_polling(self):
|
||||||
|
""" Start polling device states if necessary. """
|
||||||
|
if self.is_polling or \
|
||||||
|
not any(device.should_poll for device in self.devices.values()):
|
||||||
|
return
|
||||||
|
|
||||||
|
self.is_polling = True
|
||||||
|
|
||||||
|
self.hass.track_time_change(
|
||||||
|
self._update_device_states,
|
||||||
|
second=range(0, 60, self.scan_interval))
|
||||||
|
|
||||||
|
def _setup_platform(self, platform_type, config, discovery_info=None):
|
||||||
|
""" Tries to setup a platform for this component. """
|
||||||
|
platform_name = '{}.{}'.format(self.domain, platform_type)
|
||||||
|
platform = get_component(platform_name)
|
||||||
|
|
||||||
|
if platform is None:
|
||||||
|
self.logger.error('Unable to find platform %s', platform_type)
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
platform.setup_platform(
|
||||||
|
self.hass, config, self._add_devices, discovery_info)
|
||||||
|
except AttributeError:
|
||||||
|
# Support old deprecated method for now - 3/1/2015
|
||||||
|
if hasattr(platform, 'get_devices'):
|
||||||
|
self.logger.warning(
|
||||||
|
"Please upgrade %s to return new devices using "
|
||||||
|
"setup_platform. See %s/demo.py for an example.",
|
||||||
|
platform_name, self.domain)
|
||||||
|
self._add_devices(platform.get_devices(self.hass, config))
|
||||||
|
|
||||||
|
else:
|
||||||
|
# AttributeError if setup_platform does not exist
|
||||||
|
self.logger.exception(
|
||||||
|
"Error setting up %s", platform_type)
|
@ -132,10 +132,13 @@ def get_component(comp_name):
|
|||||||
|
|
||||||
return module
|
return module
|
||||||
|
|
||||||
except ImportError:
|
except ImportError as err:
|
||||||
_LOGGER.exception(
|
# This error happens if for example custom_components/switch
|
||||||
("Error loading %s. Make sure all "
|
# exists and we try to load switch.demo.
|
||||||
"dependencies are installed"), path)
|
if str(err) != "No module named '{}'".format(path):
|
||||||
|
_LOGGER.exception(
|
||||||
|
("Error loading %s. Make sure all "
|
||||||
|
"dependencies are installed"), path)
|
||||||
|
|
||||||
_LOGGER.error("Unable to find component %s", comp_name)
|
_LOGGER.error("Unable to find component %s", comp_name)
|
||||||
|
|
||||||
|
@ -29,3 +29,9 @@ pushbullet.py>=0.7.1
|
|||||||
|
|
||||||
# thermostat.nest
|
# thermostat.nest
|
||||||
python-nest>=2.1
|
python-nest>=2.1
|
||||||
|
|
||||||
|
# z-wave
|
||||||
|
pydispatcher>=2.0.5
|
||||||
|
|
||||||
|
# pyyaml
|
||||||
|
pyyaml
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# Builds the frontend for production
|
||||||
# Call 'build_frontend demo' to build a demo frontend.
|
# Call 'build_frontend demo' to build a demo frontend.
|
||||||
|
|
||||||
# If current pwd is scripts, go 1 up.
|
# If current pwd is scripts, go 1 up.
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
# Builds the JS for production
|
||||||
|
|
||||||
# If current pwd is scripts, go 1 up.
|
# If current pwd is scripts, go 1 up.
|
||||||
if [ ${PWD##*/} == "scripts" ]; then
|
if [ ${PWD##*/} == "scripts" ]; then
|
||||||
cd ..
|
cd ..
|
||||||
|
22
scripts/build_python_openzwave
Executable file
22
scripts/build_python_openzwave
Executable file
@ -0,0 +1,22 @@
|
|||||||
|
# Sets up and builds python open zwave to be used with Home Assistant
|
||||||
|
# Dependencies that need to be installed:
|
||||||
|
# apt-get install cython3 libudev-dev python-sphinx python3-setuptools mercurial
|
||||||
|
# pip3 install cython
|
||||||
|
|
||||||
|
# If current pwd is scripts, go 1 up.
|
||||||
|
if [ ${PWD##*/} == "scripts" ]; then
|
||||||
|
cd ..
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
|
||||||
|
hg clone https://code.google.com/r/balloob-python-openzwave/
|
||||||
|
cd balloob-python-openzwave
|
||||||
|
./update.sh
|
||||||
|
|
||||||
|
# Fix an issue with openzwave
|
||||||
|
sed -i '253s/.*//' openzwave/cpp/src/value_classes/ValueID.h
|
||||||
|
|
||||||
|
./compile.sh
|
||||||
|
./install.sh
|
@ -1,3 +1,5 @@
|
|||||||
|
# Run style checks
|
||||||
|
|
||||||
# If current pwd is scripts, go 1 up.
|
# If current pwd is scripts, go 1 up.
|
||||||
if [ ${PWD##*/} == "scripts" ]; then
|
if [ ${PWD##*/} == "scripts" ]; then
|
||||||
cd ..
|
cd ..
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
# Build and run Home Assinstant in Docker
|
||||||
|
|
||||||
# Optional: pass in a timezone as first argument
|
# Optional: pass in a timezone as first argument
|
||||||
# If not given will attempt to mount /etc/localtime
|
# If not given will attempt to mount /etc/localtime
|
||||||
|
|
||||||
@ -12,6 +14,7 @@ if [ $# -gt 0 ]
|
|||||||
then
|
then
|
||||||
docker run \
|
docker run \
|
||||||
--net=host \
|
--net=host \
|
||||||
|
--device=/dev/ttyUSB0:/zwaveusbstick:rwm \
|
||||||
-e "TZ=$1" \
|
-e "TZ=$1" \
|
||||||
-v `pwd`:/usr/src/app \
|
-v `pwd`:/usr/src/app \
|
||||||
-v `pwd`/config:/config \
|
-v `pwd`/config:/config \
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
# Builds the JS for developing, rebuilds when files change
|
||||||
|
|
||||||
# If current pwd is scripts, go 1 up.
|
# If current pwd is scripts, go 1 up.
|
||||||
if [ ${PWD##*/} == "scripts" ]; then
|
if [ ${PWD##*/} == "scripts" ]; then
|
||||||
cd ..
|
cd ..
|
||||||
|
19
scripts/dev_openzwave_docker
Executable file
19
scripts/dev_openzwave_docker
Executable file
@ -0,0 +1,19 @@
|
|||||||
|
# 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
|
||||||
|
|
||||||
|
if [ $# -gt 0 ]
|
||||||
|
then
|
||||||
|
docker build -t home-assistant-dev .
|
||||||
|
fi
|
||||||
|
|
||||||
|
docker run \
|
||||||
|
--device=/dev/ttyUSB0:/zwaveusbstick:rwm \
|
||||||
|
-v `pwd`:/usr/src/app \
|
||||||
|
-p 8123:8123 \
|
||||||
|
-t -i home-assistant-dev \
|
||||||
|
/bin/bash
|
@ -27,8 +27,3 @@ def init(empty=False):
|
|||||||
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
||||||
""" Returns mock devices. """
|
""" Returns mock devices. """
|
||||||
add_devices_callback(DEVICES)
|
add_devices_callback(DEVICES)
|
||||||
|
|
||||||
|
|
||||||
def get_lights():
|
|
||||||
""" Helper method to get current light objects. """
|
|
||||||
return DEVICES
|
|
||||||
|
@ -24,6 +24,6 @@ def init(empty=False):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def get_switches(hass, config):
|
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
||||||
""" Returns mock devices. """
|
""" Find and return test switches. """
|
||||||
return DEVICES
|
add_devices_callback(DEVICES)
|
||||||
|
@ -103,7 +103,7 @@ class TestLight(unittest.TestCase):
|
|||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
light.setup(self.hass, {light.DOMAIN: {CONF_TYPE: 'test'}}))
|
light.setup(self.hass, {light.DOMAIN: {CONF_TYPE: 'test'}}))
|
||||||
|
|
||||||
dev1, dev2, dev3 = platform.get_lights()
|
dev1, dev2, dev3 = platform.DEVICES
|
||||||
|
|
||||||
# Test init
|
# Test init
|
||||||
self.assertTrue(light.is_on(self.hass, dev1.entity_id))
|
self.assertTrue(light.is_on(self.hass, dev1.entity_id))
|
||||||
@ -244,7 +244,7 @@ class TestLight(unittest.TestCase):
|
|||||||
self.hass, {light.DOMAIN: {CONF_TYPE: 'test'}}
|
self.hass, {light.DOMAIN: {CONF_TYPE: 'test'}}
|
||||||
))
|
))
|
||||||
|
|
||||||
dev1, dev2, dev3 = platform.get_lights()
|
dev1, dev2, dev3 = platform.DEVICES
|
||||||
|
|
||||||
light.turn_on(self.hass, dev1.entity_id, profile='test')
|
light.turn_on(self.hass, dev1.entity_id, profile='test')
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ class TestSwitch(unittest.TestCase):
|
|||||||
|
|
||||||
# Switch 1 is ON, switch 2 is OFF
|
# Switch 1 is ON, switch 2 is OFF
|
||||||
self.switch_1, self.switch_2, self.switch_3 = \
|
self.switch_1, self.switch_2, self.switch_3 = \
|
||||||
platform.get_switches(None, None)
|
platform.DEVICES
|
||||||
|
|
||||||
def tearDown(self): # pylint: disable=invalid-name
|
def tearDown(self): # pylint: disable=invalid-name
|
||||||
""" Stop down stuff we started. """
|
""" Stop down stuff we started. """
|
||||||
|
Loading…
x
Reference in New Issue
Block a user