Migrated wemo component to be part of a switch component
This commit is contained in:
parent
222d57bda7
commit
5770cc03a1
9 changed files with 269 additions and 193 deletions
|
@ -182,13 +182,13 @@ Optional service data:
|
|||
- `rgb_color` - three comma seperated integers that represent the color in RGB
|
||||
- `brightness` - integer between 0 and 255 for how bright the color should be
|
||||
|
||||
**wemo**
|
||||
Keeps track which WeMo switches are in the network, their state and allows you to control them.
|
||||
**switch**
|
||||
Keeps track which switches are in the network, their state and allows you to control them.
|
||||
|
||||
Registers services `wemo/turn_on` and `wemo/turn_off` to turn a or all wemo switches on or off.
|
||||
Registers services `switch/turn_on` and `switch/turn_off` to turn a or all switches on or off.
|
||||
|
||||
Optional service data:
|
||||
- `entity_id` - only act on specific WeMo switch. Else targets all.
|
||||
- `entity_id` - only act on specific switch. Else targets all.
|
||||
|
||||
**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.
|
||||
|
|
|
@ -23,9 +23,9 @@ password=PASSWORD
|
|||
# instead of scanning the network
|
||||
# hosts=192.168.1.9,192.168.1.12
|
||||
|
||||
[wemo]
|
||||
# Optional: hard code the hosts (comma seperated) to find WeMos
|
||||
# instead of scanning the network
|
||||
[switch]
|
||||
type=wemo
|
||||
# Optional: hard code the hosts (comma seperated) to avoid scanning the network
|
||||
# hosts=192.168.1.9,192.168.1.12
|
||||
|
||||
[downloader]
|
||||
|
|
|
@ -640,7 +640,14 @@ class Timer(threading.Thread):
|
|||
|
||||
class HomeAssistantError(Exception):
|
||||
""" General Home Assistant exception occured. """
|
||||
pass
|
||||
|
||||
|
||||
class InvalidEntityFormatError(HomeAssistantError):
|
||||
""" When an invalid formatted entity is encountered. """
|
||||
pass
|
||||
|
||||
|
||||
class NoEntitySpecifiedError(HomeAssistantError):
|
||||
""" When no entity is specified. """
|
||||
pass
|
||||
|
|
|
@ -74,20 +74,20 @@ def setup(hass, config):
|
|||
|
||||
group.setup_group(hass, GROUP_NAME_ALL_LIGHTS, lights, False)
|
||||
|
||||
# Setup Wemo
|
||||
wemos = ['wemo.AC', 'wemo.Christmas_Lights']
|
||||
# Setup switch
|
||||
switches = ['switch.AC', 'switch.Christmas_Lights']
|
||||
|
||||
hass.services.register('wemo', SERVICE_TURN_ON, mock_turn_on)
|
||||
hass.services.register('wemo', SERVICE_TURN_OFF, mock_turn_off)
|
||||
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('wemo', SERVICE_TURN_ON,
|
||||
{'entity_id': wemos[0:1]}))
|
||||
mock_turn_off(ha.ServiceCall('wemo', SERVICE_TURN_OFF,
|
||||
{'entity_id': wemos[1:]}))
|
||||
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
|
||||
group.setup_group(hass, 'living_room', lights[0:3] + wemos[0:1])
|
||||
group.setup_group(hass, 'bedroom', [lights[3]] + wemos[1:])
|
||||
group.setup_group(hass, 'living_room', lights[0:3] + switches[0:1])
|
||||
group.setup_group(hass, 'bedroom', [lights[3]] + switches[1:])
|
||||
|
||||
# Setup process
|
||||
hass.states.set("process.XBMC", STATE_ON)
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
""" DO NOT MODIFY. Auto-generated by build_frontend script """
|
||||
VERSION = "eabfdd5cb0e712c8d6d5d837fb9bfeb9"
|
||||
VERSION = "6d353f9599942124690691fb22c115ee"
|
||||
|
|
|
@ -17747,8 +17747,8 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
|||
case "device_tracker":
|
||||
return "social:person";
|
||||
|
||||
case "wemo":
|
||||
return "settings-input-svideo";
|
||||
case "switch":
|
||||
return "image:flash-on";
|
||||
|
||||
case "chromecast":
|
||||
if(state && state != "idle") {
|
||||
|
|
|
@ -24,8 +24,8 @@
|
|||
case "device_tracker":
|
||||
return "social:person";
|
||||
|
||||
case "wemo":
|
||||
return "settings-input-svideo";
|
||||
case "switch":
|
||||
return "image:flash-on";
|
||||
|
||||
case "chromecast":
|
||||
if(state && state != "idle") {
|
||||
|
|
240
homeassistant/components/switch.py
Normal file
240
homeassistant/components/switch.py
Normal file
|
@ -0,0 +1,240 @@
|
|||
"""
|
||||
homeassistant.components.switch
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Component to interface with various switches that can be controlled remotely.
|
||||
"""
|
||||
import logging
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import homeassistant as ha
|
||||
import homeassistant.util as util
|
||||
from homeassistant.components import (group, extract_entity_ids,
|
||||
STATE_ON, STATE_OFF,
|
||||
SERVICE_TURN_ON, SERVICE_TURN_OFF,
|
||||
ATTR_ENTITY_ID, ATTR_FRIENDLY_NAME)
|
||||
DOMAIN = 'switch'
|
||||
DEPENDENCIES = []
|
||||
|
||||
GROUP_NAME_ALL_SWITCHES = 'all_switches'
|
||||
ENTITY_ID_ALL_SWITCHES = group.ENTITY_ID_FORMAT.format(
|
||||
GROUP_NAME_ALL_SWITCHES)
|
||||
|
||||
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)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def is_on(hass, entity_id=None):
|
||||
""" Returns if the switch is on based on the statemachine. """
|
||||
entity_id = entity_id or ENTITY_ID_ALL_SWITCHES
|
||||
|
||||
return hass.states.is_state(entity_id, STATE_ON)
|
||||
|
||||
|
||||
def turn_on(hass, entity_id=None):
|
||||
""" Turns all or specified switch on. """
|
||||
data = {ATTR_ENTITY_ID: entity_id} if entity_id else None
|
||||
|
||||
hass.call_service(DOMAIN, SERVICE_TURN_ON, data)
|
||||
|
||||
|
||||
def turn_off(hass, entity_id=None):
|
||||
""" Turns all or specified switch off. """
|
||||
data = {ATTR_ENTITY_ID: entity_id} if entity_id else None
|
||||
|
||||
hass.call_service(DOMAIN, SERVICE_TURN_OFF, data)
|
||||
|
||||
|
||||
# pylint: disable=too-many-branches
|
||||
def setup(hass, config):
|
||||
""" Track states and offer events for switches. """
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
if not util.validate_config(config, {DOMAIN: [ha.CONF_TYPE]}, logger):
|
||||
return False
|
||||
|
||||
switch_type = config[DOMAIN][ha.CONF_TYPE]
|
||||
|
||||
if switch_type == 'wemo':
|
||||
switch_init = get_wemo_switches
|
||||
|
||||
else:
|
||||
logger.error("Unknown switch type specified: %s", switch_type)
|
||||
|
||||
return False
|
||||
|
||||
switches = switch_init(config[DOMAIN])
|
||||
|
||||
if len(switches) == 0:
|
||||
logger.error("No switches found")
|
||||
return False
|
||||
|
||||
# Setup a dict mapping entity IDs to devices
|
||||
ent_to_switch = {}
|
||||
|
||||
no_name_count = 1
|
||||
|
||||
for switch in switches:
|
||||
name = switch.get_name()
|
||||
|
||||
if name is None:
|
||||
name = "Switch #{}".format(no_name_count)
|
||||
no_name_count += 1
|
||||
|
||||
entity_id = util.ensure_unique_string(
|
||||
ENTITY_ID_FORMAT.format(util.slugify(name)),
|
||||
list(ent_to_switch.keys()))
|
||||
|
||||
switch.entity_id = entity_id
|
||||
ent_to_switch[entity_id] = switch
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def update_states(time, force_reload=False):
|
||||
""" Update states of all switches. """
|
||||
|
||||
# First time this method gets called, force_reload should be True
|
||||
if force_reload or \
|
||||
datetime.now() - update_states.last_updated > \
|
||||
MIN_TIME_BETWEEN_SCANS:
|
||||
|
||||
logger.info("Updating switch states")
|
||||
update_states.last_updated = datetime.now()
|
||||
|
||||
for switch in switches:
|
||||
switch.update_ha_state(hass)
|
||||
|
||||
update_states(None, True)
|
||||
|
||||
def handle_switch_service(service):
|
||||
""" Handles calls to the switch services. """
|
||||
devices = [ent_to_switch[entity_id] for entity_id
|
||||
in extract_entity_ids(hass, service)
|
||||
if entity_id in ent_to_switch]
|
||||
|
||||
if not devices:
|
||||
devices = switches
|
||||
|
||||
for switch in devices:
|
||||
if service.service == SERVICE_TURN_ON:
|
||||
switch.turn_on()
|
||||
else:
|
||||
switch.turn_off()
|
||||
|
||||
switch.update_ha_state(hass)
|
||||
|
||||
# Track all wemos in a group
|
||||
group.setup_group(hass, GROUP_NAME_ALL_SWITCHES,
|
||||
ent_to_switch.keys(), False)
|
||||
|
||||
# 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_ON, handle_switch_service)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
class Switch(object):
|
||||
""" ABC for Switches within Home Assistant. """
|
||||
# pylint: disable=no-self-use
|
||||
|
||||
entity_id = None
|
||||
|
||||
def get_name(self):
|
||||
""" Returns the name of the switch if any. """
|
||||
return None
|
||||
|
||||
def turn_on(self, dimming=100):
|
||||
"""
|
||||
Turns the switch on.
|
||||
Dimming is a number between 0-100 and specifies how much switch has
|
||||
to be dimmed. There is no guarantee that the switch supports dimming.
|
||||
"""
|
||||
pass
|
||||
|
||||
def turn_off(self):
|
||||
""" Turns the switch off. """
|
||||
pass
|
||||
|
||||
def is_on(self):
|
||||
""" True if switch is on. """
|
||||
return False
|
||||
|
||||
def get_state_attributes(self):
|
||||
""" Returns optional state attributes. """
|
||||
return None
|
||||
|
||||
def update_ha_state(self, hass):
|
||||
""" Updates Home Assistant with its current state. """
|
||||
if self.entity_id is None:
|
||||
raise ha.NoEntitySpecifiedError(
|
||||
"No entity specified for switch {}".format(self.get_name()))
|
||||
|
||||
state = STATE_ON if self.is_on() else STATE_OFF
|
||||
|
||||
return hass.states.set(self.entity_id, state,
|
||||
self.get_state_attributes())
|
||||
|
||||
|
||||
def get_wemo_switches(config):
|
||||
""" Find and return WeMo switches. """
|
||||
|
||||
try:
|
||||
# Pylint does not play nice if not every folders has an __init__.py
|
||||
# pylint: disable=no-name-in-module, import-error
|
||||
import homeassistant.external.pywemo.pywemo as pywemo
|
||||
except ImportError:
|
||||
_LOGGER.exception((
|
||||
"Wemo:Failed to import pywemo. "
|
||||
"Did you maybe not run `git submodule init` "
|
||||
"and `git submodule update`?"))
|
||||
|
||||
return []
|
||||
|
||||
if ha.CONF_HOSTS in config:
|
||||
switches = (pywemo.device_from_host(host) for host
|
||||
in config[ha.CONF_HOSTS].split(","))
|
||||
|
||||
else:
|
||||
_LOGGER.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)]
|
||||
|
||||
|
||||
class WemoSwitch(Switch):
|
||||
""" represents a WeMo switch within home assistant. """
|
||||
def __init__(self, wemo):
|
||||
self.wemo = wemo
|
||||
self.state_attr = {ATTR_FRIENDLY_NAME: wemo.name}
|
||||
|
||||
def get_name(self):
|
||||
""" Returns the name of the switch if any. """
|
||||
return self.wemo.name
|
||||
|
||||
def turn_on(self, dimming=100):
|
||||
""" Turns the switch on. """
|
||||
self.wemo.on()
|
||||
|
||||
def turn_off(self):
|
||||
""" Turns the switch off. """
|
||||
self.wemo.off()
|
||||
|
||||
def is_on(self):
|
||||
""" True if switch is on. """
|
||||
return self.wemo.get_state(True)
|
||||
|
||||
def get_state_attributes(self):
|
||||
""" Returns optional state attributes. """
|
||||
return self.state_attr
|
|
@ -1,171 +0,0 @@
|
|||
"""
|
||||
Component to interface with WeMo devices on the network.
|
||||
"""
|
||||
import logging
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import homeassistant as ha
|
||||
import homeassistant.util as util
|
||||
from homeassistant.components import (group, extract_entity_ids,
|
||||
STATE_ON, STATE_OFF,
|
||||
SERVICE_TURN_ON, SERVICE_TURN_OFF,
|
||||
ATTR_ENTITY_ID, ATTR_FRIENDLY_NAME)
|
||||
DOMAIN = 'wemo'
|
||||
DEPENDENCIES = []
|
||||
|
||||
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)
|
||||
|
||||
|
||||
def is_on(hass, entity_id=None):
|
||||
""" Returns if the wemo is on based on the statemachine. """
|
||||
entity_id = entity_id or ENTITY_ID_ALL_WEMOS
|
||||
|
||||
return hass.states.is_state(entity_id, STATE_ON)
|
||||
|
||||
|
||||
def turn_on(hass, entity_id=None):
|
||||
""" Turns all or specified wemo on. """
|
||||
data = {ATTR_ENTITY_ID: entity_id} if entity_id else None
|
||||
|
||||
hass.call_service(DOMAIN, SERVICE_TURN_ON, data)
|
||||
|
||||
|
||||
def turn_off(hass, entity_id=None):
|
||||
""" Turns all or specified wemo off. """
|
||||
data = {ATTR_ENTITY_ID: entity_id} if entity_id else None
|
||||
|
||||
hass.call_service(DOMAIN, SERVICE_TURN_OFF, data)
|
||||
|
||||
|
||||
# pylint: disable=too-many-branches
|
||||
def setup(hass, config):
|
||||
""" Track states and offer events for WeMo switches. """
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
try:
|
||||
# Pylint does not play nice if not every folders has an __init__.py
|
||||
# pylint: disable=no-name-in-module, import-error
|
||||
import homeassistant.external.pywemo.pywemo as pywemo
|
||||
except ImportError:
|
||||
logger.exception((
|
||||
"Failed to import pywemo. "
|
||||
"Did you maybe not run `git submodule init` "
|
||||
"and `git submodule update`?"))
|
||||
|
||||
return False
|
||||
|
||||
if ha.CONF_HOSTS in config[DOMAIN]:
|
||||
devices = []
|
||||
|
||||
for host in config[DOMAIN][ha.CONF_HOSTS].split(","):
|
||||
device = pywemo.device_from_host(host)
|
||||
|
||||
if device:
|
||||
devices.append(device)
|
||||
|
||||
else:
|
||||
logger.info("Scanning for WeMo devices")
|
||||
devices = pywemo.discover_devices()
|
||||
|
||||
is_switch = lambda switch: isinstance(switch, pywemo.Switch)
|
||||
|
||||
switches = [device for device in devices if is_switch(device)]
|
||||
|
||||
if len(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 is_switch(device):
|
||||
return
|
||||
|
||||
try:
|
||||
entity_id = sno_to_ent[device.serialnumber]
|
||||
|
||||
except KeyError:
|
||||
# New device, set it up
|
||||
entity_id = util.ensure_unique_string(
|
||||
ENTITY_ID_FORMAT.format(util.slugify(device.name)),
|
||||
list(ent_to_dev.keys()))
|
||||
|
||||
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, pywemo.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
|
||||
|
||||
hass.states.set(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 switches:
|
||||
update_wemo_state(device)
|
||||
|
||||
update_wemos_state(None, True)
|
||||
|
||||
# Track all wemos in a group
|
||||
group.setup_group(hass, GROUP_NAME_ALL_WEMOS, sno_to_ent.values(), False)
|
||||
|
||||
def handle_wemo_service(service):
|
||||
""" Handles calls to the WeMo service. """
|
||||
devices = [ent_to_dev[entity_id] for entity_id
|
||||
in extract_entity_ids(hass, service)
|
||||
if entity_id in ent_to_dev]
|
||||
|
||||
if not devices:
|
||||
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
|
||||
hass.track_time_change(update_wemos_state, second=[0, 30])
|
||||
|
||||
hass.services.register(DOMAIN, SERVICE_TURN_OFF, handle_wemo_service)
|
||||
|
||||
hass.services.register(DOMAIN, SERVICE_TURN_ON, handle_wemo_service)
|
||||
|
||||
return True
|
Loading…
Add table
Reference in a new issue