diff --git a/README.md b/README.md index 49d1168d884..c501649edc2 100644 --- a/README.md +++ b/README.md @@ -5,37 +5,37 @@ Home Assistant provides a platform for home automation. It does so by having mod It is currently able to do the following things: * Track if devices are home by monitoring connected devices to a wireless router (currently supporting modern Netgear routers or routers running Tomato firmware) - * Track which lights are on - * Track what your Chromecasts are up to + * Track and control lights + * Track and control WeMo switches + * Track and control Chromecasts * Turn on the lights when people get home when the sun is setting or has set * Slowly turn on the lights to compensate for light loss when the sun sets and people are home * Turn off lights and connected devices when everybody leaves the house - * Start YouTube video’s on the Chromecast - * Quit current running application on a Chromecast * Download files to the host machine * Open a url in the default browser at the host machine * Simulate key presses on the host for Play/Pause, Next track, Prev track, Volume up, Volume Down * Controllable via a REST API and web interface - * Support for thin client Home Assistant instances that will forward all their commands to the main instance - * Android Tasker project to control Home Assistant from your phone and report charging state. Combine it with AutoVoice to be able to tell your phones to turn the lights off! + * Support for remoting Home Assistant instances through a Python API + * Android Tasker project to control Home Assistant from your phone and report charging state. ![screenshot-states](https://raw.github.com/balloob/home-assistant/master/docs/states.png) Current compatible devices: - * Wireless router running [Tomato firmware](http://www.polarcloud.com/tomato) - * Netgear wireless routers (tested with R6300) + * [WeMo switches](http://www.belkin.com/us/Products/home-automation/c/wemo-home-automation/) * [Philips Hue](http://meethue.com) * [Google Chromecast](http://www.google.com/intl/en/chrome/devices/chromecast) + * Wireless router running [Tomato firmware](http://www.polarcloud.com/tomato) + * Netgear wireless routers (tested with R6300) -The system is built modular so support for other wireless routers, other devices or actions can be implemented easily. +The system is built modular so support for other devices or actions can be implemented easily. Installation instructions ------------------------- -* Install python modules [PyEphem](http://rhodesmill.org/pyephem/), [Requests](http://python-requests.org) and [PHue](https://github.com/studioimaginaire/phue): `pip install pyephem requests phue` +* The core depends on [PyEphem](http://rhodesmill.org/pyephem/) and [Requests](http://python-requests.org). Depending on the components you would like to use you will need [PHue](https://github.com/studioimaginaire/phue) for Philips Hue support, [PyChromecast](https://github.com/balloob/pychromecast) for Chromecast support and [ouimeaux](https://github.com/iancmcc/ouimeaux) for WeMo support. Install these using `pip install pyephem requests phue ouimeaux pychromecast`. * Clone the repository and pull in the submodules `git clone --recursive https://github.com/balloob/home-assistant.git` * Copy home-assistant.conf.default to home-assistant.conf and adjust the config values to match your setup. * For Tomato you will have to not only setup your host, username and password but also a http_id. The http_id can be retrieved by going to the admin console of your router, view the source of any of the pages and search for `http_id`. -* Setup PHue by running `python -m phue --host HUE_BRIDGE_IP_ADDRESS` from the commandline. +* If you want to use Hue, setup PHue by running `python -m phue --host HUE_BRIDGE_IP_ADDRESS` from the commandline and follow the instructions. * While running the script it will create and maintain a file called `known_devices.csv` which will contain the detected devices. Adjust the track variable for the devices you want the script to act on and restart the script or call the service `device_tracker/reload_devices_csv`. Done. Start it now by running `python start.py` @@ -205,6 +205,9 @@ Action: sets the state per device and maintains a combined state called `all_dev **Light** Keeps track which lights are turned on and can control the lights. +**WeMo** +Keeps track which WeMo switches are in the network and allows you to control them. + **device_sun_light_trigger** Turns lights on or off using a light control component based on state of the sun and devices that are home. Depends on: light control, track_sun, DeviceTracker diff --git a/homeassistant/bootstrap.py b/homeassistant/bootstrap.py index 72dbddd5ac7..098b96ec52a 100644 --- a/homeassistant/bootstrap.py +++ b/homeassistant/bootstrap.py @@ -128,6 +128,12 @@ def from_config_file(config_path): else: chromecast_started = False + # WeMo + if has_section("wemo"): + wemo = load_module('wemo') + + add_status("WeMo", wemo.setup(bus, statemachine)) + # Light control if has_section("light.hue"): light = load_module('light') diff --git a/homeassistant/components/__init__.py b/homeassistant/components/__init__.py index 5fc9862c696..aaeb9057789 100644 --- a/homeassistant/components/__init__.py +++ b/homeassistant/components/__init__.py @@ -21,6 +21,7 @@ import homeassistant as ha import homeassistant.util as util ATTR_ENTITY_ID = 'entity_id' +ATTR_FRIENDLY_NAME = "friendly_name" STATE_ON = 'on' STATE_OFF = 'off' diff --git a/homeassistant/components/wemo.py b/homeassistant/components/wemo.py new file mode 100644 index 00000000000..3517943274d --- /dev/null +++ b/homeassistant/components/wemo.py @@ -0,0 +1,143 @@ +""" +Component to interface with WeMo devices on the network. +""" +import logging +import socket +from datetime import datetime, timedelta + +import homeassistant as ha +import homeassistant.util as util +from homeassistant.components import (group, STATE_ON, STATE_OFF, + SERVICE_TURN_ON, SERVICE_TURN_OFF, + ATTR_ENTITY_ID, ATTR_FRIENDLY_NAME) +DOMAIN = 'wemo' + +GROUP_NAME_ALL_WEMOS = 'all_wemos' +ENTITY_ID_ALL_WEMOS = group.ENTITY_ID_FORMAT.format( + GROUP_NAME_ALL_WEMOS) + +ENTITY_ID_FORMAT = DOMAIN + '.{}' + +ATTR_TODAY_KWH = "today_kwh" +ATTR_CURRENT_POWER = "current_power" +ATTR_TODAY_ON_TIME = "today_on_time" +ATTR_TODAY_STANDBY_TIME = "today_standby_time" + +MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10) + + +# pylint: disable=too-many-branches +def setup(bus, statemachine): + """ Track states and offer events for WeMo switches. """ + logger = logging.getLogger(__name__) + + try: + import ouimeaux.environment as wemo_env + import ouimeaux.device.switch as wemo_switch + import ouimeaux.device.insight as wemo_insight + except ImportError: + logger.exception(("Failed to import ouimeaux. " + "Did you maybe not install the 'ouimeaux' " + "dependency?")) + + return False + + env = wemo_env.Environment() + + try: + env.start() + except socket.error: + # If the socket is already in use + logger.exception("Error starting WeMo environment") + return False + + env.discover(5) + + if len(env.list_switches()) == 0: + logger.error("No WeMo switches found") + return False + + # Dict mapping serial no to entity IDs + sno_to_ent = {} + # Dict mapping entity IDs to devices + ent_to_dev = {} + + def _update_wemo_state(device): + """ Update the state of specified WeMo device. """ + + # We currently only support switches + if not isinstance(device, wemo_switch.Switch): + return + + try: + entity_id = sno_to_ent[device.serialnumber] + + except KeyError: + # New device, set it up + entity_id = ENTITY_ID_FORMAT.format(util.slugify(device.name)) + + sno_to_ent[device.serialnumber] = entity_id + ent_to_dev[entity_id] = device + + state = STATE_ON if device.get_state(True) else STATE_OFF + + state_attr = {ATTR_FRIENDLY_NAME: device.name} + + if isinstance(device, wemo_insight.Insight): + pass + # Should work but doesn't.. + #state_attr[ATTR_TODAY_KWH] = device.today_kwh + #state_attr[ATTR_CURRENT_POWER] = device.current_power + #state_attr[ATTR_TODAY_ON_TIME] = device.today_on_time + #state_attr[ATTR_TODAY_STANDBY_TIME] = device.today_standby_time + + statemachine.set_state(entity_id, state, state_attr) + + # pylint: disable=unused-argument + def _update_wemos_state(time, force_reload=False): + """ Update states of all WeMo devices. """ + + # First time this method gets called, force_reload should be True + if (force_reload or + datetime.now() - _update_wemos_state.last_updated > + MIN_TIME_BETWEEN_SCANS): + + logger.info("Updating WeMo status") + _update_wemos_state.last_updated = datetime.now() + + for device in env: + _update_wemo_state(device) + + _update_wemos_state(None, True) + + # Track all lights in a group + group.setup(bus, statemachine, + GROUP_NAME_ALL_WEMOS, sno_to_ent.values()) + + def _handle_wemo_service(service): + """ Handles calls to the WeMo service. """ + dat = service.data + + if ATTR_ENTITY_ID in dat: + device = ent_to_dev.get(dat[ATTR_ENTITY_ID]) + + devices = [device] if device is not None else [] + else: + devices = ent_to_dev.values() + + for device in devices: + if service.service == SERVICE_TURN_ON: + device.on() + else: + device.off() + + _update_wemo_state(device) + + # Update WeMo state every 30 seconds + ha.track_time_change(bus, _update_wemos_state, second=[0, 30]) + + bus.register_service(DOMAIN, SERVICE_TURN_OFF, _handle_wemo_service) + + bus.register_service(DOMAIN, SERVICE_TURN_ON, _handle_wemo_service) + + return True