Added state groups and migrated code base to use them.
This commit is contained in:
parent
367433acb2
commit
48026c28c1
12 changed files with 296 additions and 160 deletions
|
@ -5,16 +5,16 @@ longitude=-117.22743
|
||||||
[httpinterface]
|
[httpinterface]
|
||||||
api_password=mypass
|
api_password=mypass
|
||||||
|
|
||||||
[hue]
|
[light.hue]
|
||||||
host=192.168.1.2
|
host=192.168.1.2
|
||||||
|
|
||||||
[tomato]
|
[device_tracker.tomato]
|
||||||
host=192.168.1.1
|
host=192.168.1.1
|
||||||
username=admin
|
username=admin
|
||||||
password=PASSWORD
|
password=PASSWORD
|
||||||
http_id=aaaaaaaaaaaaaaa
|
http_id=aaaaaaaaaaaaaaa
|
||||||
|
|
||||||
[netgear]
|
[device_tracker.netgear]
|
||||||
host=192.168.1.1
|
host=192.168.1.1
|
||||||
username=admin
|
username=admin
|
||||||
password=PASSWORD
|
password=PASSWORD
|
||||||
|
@ -24,3 +24,13 @@ host=192.168.1.3
|
||||||
|
|
||||||
[downloader]
|
[downloader]
|
||||||
download_dir=downloads
|
download_dir=downloads
|
||||||
|
|
||||||
|
[device_sun_light_trigger]
|
||||||
|
# Example how you can specify a specific group that has to be turned on
|
||||||
|
# light_group=living_room
|
||||||
|
|
||||||
|
# A comma seperated list of states that have to be tracked
|
||||||
|
# As a single group
|
||||||
|
[groups]
|
||||||
|
living_room=light.Bowl,light.Ceiling,light.TV_back_light
|
||||||
|
bedroom=light.Bed_light
|
||||||
|
|
|
@ -16,7 +16,12 @@ logging.basicConfig(level=logging.INFO)
|
||||||
|
|
||||||
ALL_EVENTS = '*'
|
ALL_EVENTS = '*'
|
||||||
|
|
||||||
DOMAIN_HOMEASSISTANT = "homeassistant"
|
DOMAIN = "homeassistant"
|
||||||
|
|
||||||
|
STATE_ON = "on"
|
||||||
|
STATE_OFF = "off"
|
||||||
|
STATE_NOT_HOME = 'device_not_home'
|
||||||
|
STATE_HOME = 'device_home'
|
||||||
|
|
||||||
SERVICE_TURN_ON = "turn_on"
|
SERVICE_TURN_ON = "turn_on"
|
||||||
SERVICE_TURN_OFF = "turn_off"
|
SERVICE_TURN_OFF = "turn_off"
|
||||||
|
@ -40,7 +45,7 @@ def start_home_assistant(bus):
|
||||||
""" Start home assistant. """
|
""" Start home assistant. """
|
||||||
request_shutdown = threading.Event()
|
request_shutdown = threading.Event()
|
||||||
|
|
||||||
bus.register_service(DOMAIN_HOMEASSISTANT, SERVICE_HOMEASSISTANT_STOP,
|
bus.register_service(DOMAIN, SERVICE_HOMEASSISTANT_STOP,
|
||||||
lambda service: request_shutdown.set())
|
lambda service: request_shutdown.set())
|
||||||
|
|
||||||
Timer(bus)
|
Timer(bus)
|
||||||
|
@ -88,25 +93,19 @@ def _matcher(subject, pattern):
|
||||||
return '*' in pattern or subject in pattern
|
return '*' in pattern or subject in pattern
|
||||||
|
|
||||||
|
|
||||||
def get_grouped_state_cats(statemachine, cat_format_string, strip_prefix):
|
def split_state_category(category):
|
||||||
""" Get states that are part of a group of states.
|
""" Splits a state category into domain, object_id. """
|
||||||
|
return category.split(".", 1)
|
||||||
|
|
||||||
Example category_format_string can be "devices.{}"
|
|
||||||
|
|
||||||
If input states are devices, devices.paulus and devices.paulus.charging
|
def filter_categories(categories, domain_filter=None, object_id_only=False):
|
||||||
then the output will be paulus if strip_prefix is True, else devices.paulus
|
""" Filter a list of categories based on domain. Setting object_id_only
|
||||||
"""
|
will only return the object_ids. """
|
||||||
group_prefix = cat_format_string.format("")
|
return [
|
||||||
|
split_state_category(cat)[1] if object_id_only else cat
|
||||||
if strip_prefix:
|
for cat in categories if
|
||||||
id_part = slice(len(group_prefix), None)
|
not domain_filter or cat.startswith(domain_filter)
|
||||||
|
]
|
||||||
return [cat[id_part] for cat in statemachine.categories
|
|
||||||
if cat.startswith(group_prefix) and cat.count(".") == 1]
|
|
||||||
|
|
||||||
else:
|
|
||||||
return [cat for cat in statemachine.categories
|
|
||||||
if cat.startswith(group_prefix) and cat.count(".") == 1]
|
|
||||||
|
|
||||||
|
|
||||||
def create_state(state, attributes=None, last_changed=None):
|
def create_state(state, attributes=None, last_changed=None):
|
||||||
|
@ -119,10 +118,10 @@ def create_state(state, attributes=None, last_changed=None):
|
||||||
'last_changed': datetime_to_str(last_changed)}
|
'last_changed': datetime_to_str(last_changed)}
|
||||||
|
|
||||||
|
|
||||||
def track_state_change(bus, category, from_state, to_state, action):
|
def track_state_change(bus, category, action, from_state=None, to_state=None):
|
||||||
""" Helper method to track specific state changes. """
|
""" Helper method to track specific state changes. """
|
||||||
from_state = _ensure_list(from_state)
|
from_state = _ensure_list(from_state) if from_state else [ALL_EVENTS]
|
||||||
to_state = _ensure_list(to_state)
|
to_state = _ensure_list(to_state) if to_state else [ALL_EVENTS]
|
||||||
|
|
||||||
def listener(event):
|
def listener(event):
|
||||||
""" State change listener that listens for specific state changes. """
|
""" State change listener that listens for specific state changes. """
|
||||||
|
|
|
@ -7,9 +7,9 @@ import logging
|
||||||
|
|
||||||
import homeassistant as ha
|
import homeassistant as ha
|
||||||
from homeassistant.components import (general, chromecast,
|
from homeassistant.components import (general, chromecast,
|
||||||
device_sun_light_trigger, device,
|
device_sun_light_trigger, device_tracker,
|
||||||
downloader, keyboard, light, sun,
|
downloader, keyboard, light, sun,
|
||||||
browser, httpinterface)
|
browser, httpinterface, group)
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=too-many-branches,too-many-locals,too-many-statements
|
# pylint: disable=too-many-branches,too-many-locals,too-many-statements
|
||||||
|
@ -34,6 +34,13 @@ def from_config_file(config_path):
|
||||||
has_section = config.has_section
|
has_section = config.has_section
|
||||||
add_status = lambda name, result: statusses.append((name, result))
|
add_status = lambda name, result: statusses.append((name, result))
|
||||||
|
|
||||||
|
def get_opt_safe(section, option, default=None):
|
||||||
|
""" Failure proof option retriever. """
|
||||||
|
try:
|
||||||
|
return config.get(section, option)
|
||||||
|
except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
|
||||||
|
return default
|
||||||
|
|
||||||
# Device scanner
|
# Device scanner
|
||||||
dev_scan = None
|
dev_scan = None
|
||||||
|
|
||||||
|
@ -41,23 +48,23 @@ def from_config_file(config_path):
|
||||||
# For the error message if not all option fields exist
|
# For the error message if not all option fields exist
|
||||||
opt_fields = "host, username, password"
|
opt_fields = "host, username, password"
|
||||||
|
|
||||||
if has_section('tomato'):
|
if has_section('device_tracker.tomato'):
|
||||||
dev_scan_name = "Tomato"
|
dev_scan_name = "Tomato"
|
||||||
opt_fields += ", http_id"
|
opt_fields += ", http_id"
|
||||||
|
|
||||||
dev_scan = device.TomatoDeviceScanner(
|
dev_scan = device_tracker.TomatoDeviceScanner(
|
||||||
get_opt('tomato', 'host'),
|
get_opt('device_tracker.tomato', 'host'),
|
||||||
get_opt('tomato', 'username'),
|
get_opt('device_tracker.tomato', 'username'),
|
||||||
get_opt('tomato', 'password'),
|
get_opt('device_tracker.tomato', 'password'),
|
||||||
get_opt('tomato', 'http_id'))
|
get_opt('device_tracker.tomato', 'http_id'))
|
||||||
|
|
||||||
elif has_section('netgear'):
|
elif has_section('device_tracker.netgear'):
|
||||||
dev_scan_name = "Netgear"
|
dev_scan_name = "Netgear"
|
||||||
|
|
||||||
dev_scan = device.NetgearDeviceScanner(
|
dev_scan = device_tracker.NetgearDeviceScanner(
|
||||||
get_opt('netgear', 'host'),
|
get_opt('device_tracker.netgear', 'host'),
|
||||||
get_opt('netgear', 'username'),
|
get_opt('device_tracker.netgear', 'username'),
|
||||||
get_opt('netgear', 'password'))
|
get_opt('device_tracker.netgear', 'password'))
|
||||||
|
|
||||||
except ConfigParser.NoOptionError:
|
except ConfigParser.NoOptionError:
|
||||||
# If one of the options didn't exist
|
# If one of the options didn't exist
|
||||||
|
@ -76,7 +83,7 @@ def from_config_file(config_path):
|
||||||
|
|
||||||
# Device Tracker
|
# Device Tracker
|
||||||
if dev_scan:
|
if dev_scan:
|
||||||
device.DeviceTracker(bus, statemachine, dev_scan)
|
device_tracker.DeviceTracker(bus, statemachine, dev_scan)
|
||||||
|
|
||||||
add_status("Device Tracker", True)
|
add_status("Device Tracker", True)
|
||||||
|
|
||||||
|
@ -100,11 +107,8 @@ def from_config_file(config_path):
|
||||||
chromecast_started = False
|
chromecast_started = False
|
||||||
|
|
||||||
# Light control
|
# Light control
|
||||||
if has_section("hue"):
|
if has_section("light.hue"):
|
||||||
if has_opt("hue", "host"):
|
light_control = light.HueLightControl(get_opt_safe("hue", "host"))
|
||||||
light_control = light.HueLightControl(get_opt("hue", "host"))
|
|
||||||
else:
|
|
||||||
light_control = light.HueLightControl()
|
|
||||||
|
|
||||||
add_status("Light Control - Hue", light_control.success_init)
|
add_status("Light Control - Hue", light_control.success_init)
|
||||||
|
|
||||||
|
@ -112,11 +116,6 @@ def from_config_file(config_path):
|
||||||
else:
|
else:
|
||||||
light_control = None
|
light_control = None
|
||||||
|
|
||||||
# Light trigger
|
|
||||||
if light_control:
|
|
||||||
add_status("Light Trigger",
|
|
||||||
device_sun_light_trigger.setup(bus, statemachine))
|
|
||||||
|
|
||||||
if has_opt("downloader", "download_dir"):
|
if has_opt("downloader", "download_dir"):
|
||||||
add_status("Downloader", downloader.setup(
|
add_status("Downloader", downloader.setup(
|
||||||
bus, get_opt("downloader", "download_dir")))
|
bus, get_opt("downloader", "download_dir")))
|
||||||
|
@ -137,6 +136,21 @@ def from_config_file(config_path):
|
||||||
|
|
||||||
add_status("HTTPInterface", True)
|
add_status("HTTPInterface", True)
|
||||||
|
|
||||||
|
# Init groups
|
||||||
|
if has_section("groups"):
|
||||||
|
for name, categories in config.items("groups"):
|
||||||
|
add_status("Group - {}".format(name),
|
||||||
|
group.setup(bus, statemachine, name,
|
||||||
|
categories.split(",")))
|
||||||
|
|
||||||
|
# Light trigger
|
||||||
|
if light_control:
|
||||||
|
light_group = get_opt_safe("device_sun_light_trigger", "light_group")
|
||||||
|
|
||||||
|
add_status("Light Trigger",
|
||||||
|
device_sun_light_trigger.setup(bus, statemachine,
|
||||||
|
light_group))
|
||||||
|
|
||||||
for component, success_init in statusses:
|
for component, success_init in statusses:
|
||||||
status = "initialized" if success_init else "Failed to initialize"
|
status = "initialized" if success_init else "Failed to initialize"
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ homeassistant.components.browser
|
||||||
Provides functionality to launch a webbrowser on the host machine.
|
Provides functionality to launch a webbrowser on the host machine.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
DOMAIN_BROWSER = "browser"
|
DOMAIN = "browser"
|
||||||
|
|
||||||
SERVICE_BROWSE_URL = "browse_url"
|
SERVICE_BROWSE_URL = "browse_url"
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ def setup(bus):
|
||||||
|
|
||||||
import webbrowser
|
import webbrowser
|
||||||
|
|
||||||
bus.register_service(DOMAIN_BROWSER, SERVICE_BROWSE_URL,
|
bus.register_service(DOMAIN, SERVICE_BROWSE_URL,
|
||||||
lambda service: webbrowser.open(service.data['url']))
|
lambda service: webbrowser.open(service.data['url']))
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
|
@ -4,6 +4,7 @@ homeassistant.components.chromecast
|
||||||
|
|
||||||
Provides functionality to interact with Chromecasts.
|
Provides functionality to interact with Chromecasts.
|
||||||
"""
|
"""
|
||||||
|
import logging
|
||||||
|
|
||||||
from homeassistant.external import pychromecast
|
from homeassistant.external import pychromecast
|
||||||
|
|
||||||
|
@ -11,11 +12,11 @@ import homeassistant as ha
|
||||||
import homeassistant.util as util
|
import homeassistant.util as util
|
||||||
|
|
||||||
|
|
||||||
DOMAIN_CHROMECAST = "chromecast"
|
DOMAIN = "chromecast"
|
||||||
|
|
||||||
SERVICE_YOUTUBE_VIDEO = "play_youtube_video"
|
SERVICE_YOUTUBE_VIDEO = "play_youtube_video"
|
||||||
|
|
||||||
STATE_CATEGORY_FORMAT = 'chromecasts.{}'
|
STATE_CATEGORY_FORMAT = DOMAIN + '.{}'
|
||||||
STATE_NO_APP = "none"
|
STATE_NO_APP = "none"
|
||||||
|
|
||||||
ATTR_FRIENDLY_NAME = "friendly_name"
|
ATTR_FRIENDLY_NAME = "friendly_name"
|
||||||
|
@ -24,24 +25,12 @@ ATTR_STATE = "state"
|
||||||
ATTR_OPTIONS = "options"
|
ATTR_OPTIONS = "options"
|
||||||
|
|
||||||
|
|
||||||
def get_ids(statemachine):
|
|
||||||
""" Gets the IDs of the different Chromecasts that are being tracked. """
|
|
||||||
return ha.get_grouped_state_cats(statemachine, STATE_CATEGORY_FORMAT, True)
|
|
||||||
|
|
||||||
|
|
||||||
def get_categories(statemachine):
|
|
||||||
""" Gets the categories of the different Chromecasts that are being
|
|
||||||
tracked. """
|
|
||||||
return ha.get_grouped_state_cats(statemachine, STATE_CATEGORY_FORMAT,
|
|
||||||
False)
|
|
||||||
|
|
||||||
|
|
||||||
def turn_off(statemachine, cc_id=None):
|
def turn_off(statemachine, cc_id=None):
|
||||||
""" Exits any running app on the specified ChromeCast and shows
|
""" Exits any running app on the specified ChromeCast and shows
|
||||||
idle screen. Will quit all ChromeCasts if nothing specified. """
|
idle screen. Will quit all ChromeCasts if nothing specified. """
|
||||||
|
|
||||||
cats = [STATE_CATEGORY_FORMAT.format(cc_id)] if cc_id \
|
cats = [STATE_CATEGORY_FORMAT.format(cc_id)] if cc_id \
|
||||||
else get_categories(statemachine)
|
else ha.filter_categories(statemachine.categories, DOMAIN)
|
||||||
|
|
||||||
for cat in cats:
|
for cat in cats:
|
||||||
state = statemachine.get_state(cat)
|
state = statemachine.get_state(cat)
|
||||||
|
@ -55,34 +44,39 @@ def turn_off(statemachine, cc_id=None):
|
||||||
|
|
||||||
def setup(bus, statemachine, host):
|
def setup(bus, statemachine, host):
|
||||||
""" Listen for chromecast events. """
|
""" Listen for chromecast events. """
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
logger.info("Getting device status")
|
||||||
device = pychromecast.get_device_status(host)
|
device = pychromecast.get_device_status(host)
|
||||||
|
|
||||||
if not device:
|
if not device:
|
||||||
|
logger.error("Could not find Chromecast")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
category = STATE_CATEGORY_FORMAT.format(util.slugify(
|
category = STATE_CATEGORY_FORMAT.format(util.slugify(
|
||||||
device.friendly_name))
|
device.friendly_name))
|
||||||
|
|
||||||
bus.register_service(DOMAIN_CHROMECAST, ha.SERVICE_TURN_OFF,
|
bus.register_service(DOMAIN, ha.SERVICE_TURN_OFF,
|
||||||
lambda service:
|
lambda service:
|
||||||
turn_off(statemachine,
|
turn_off(statemachine,
|
||||||
service.data.get("cc_id", None)))
|
service.data.get("cc_id", None)))
|
||||||
|
|
||||||
bus.register_service(DOMAIN_CHROMECAST, "start_fireplace",
|
bus.register_service(DOMAIN, "start_fireplace",
|
||||||
lambda service:
|
lambda service:
|
||||||
pychromecast.play_youtube_video(host, "eyU3bRy2x44"))
|
pychromecast.play_youtube_video(host, "eyU3bRy2x44"))
|
||||||
|
|
||||||
bus.register_service(DOMAIN_CHROMECAST, "start_epic_sax",
|
bus.register_service(DOMAIN, "start_epic_sax",
|
||||||
lambda service:
|
lambda service:
|
||||||
pychromecast.play_youtube_video(host, "kxopViU98Xo"))
|
pychromecast.play_youtube_video(host, "kxopViU98Xo"))
|
||||||
|
|
||||||
bus.register_service(DOMAIN_CHROMECAST, SERVICE_YOUTUBE_VIDEO,
|
bus.register_service(DOMAIN, SERVICE_YOUTUBE_VIDEO,
|
||||||
lambda service:
|
lambda service:
|
||||||
pychromecast.play_youtube_video(
|
pychromecast.play_youtube_video(
|
||||||
host, service.data['video']))
|
host, service.data['video']))
|
||||||
|
|
||||||
def update_chromecast_state(time): # pylint: disable=unused-argument
|
def update_chromecast_state(time): # pylint: disable=unused-argument
|
||||||
""" Retrieve state of Chromecast and update statemachine. """
|
""" Retrieve state of Chromecast and update statemachine. """
|
||||||
|
logger.info("Updating app status")
|
||||||
status = pychromecast.get_app_status(host)
|
status = pychromecast.get_app_status(host)
|
||||||
|
|
||||||
if status:
|
if status:
|
||||||
|
|
|
@ -10,28 +10,34 @@ from datetime import datetime, timedelta
|
||||||
|
|
||||||
import homeassistant as ha
|
import homeassistant as ha
|
||||||
|
|
||||||
from . import light, sun, device, general
|
from . import light, sun, device_tracker, general, group
|
||||||
|
|
||||||
|
|
||||||
LIGHT_TRANSITION_TIME = timedelta(minutes=15)
|
LIGHT_TRANSITION_TIME = timedelta(minutes=15)
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=too-many-branches
|
# pylint: disable=too-many-branches
|
||||||
def setup(bus, statemachine):
|
def setup(bus, statemachine, light_group=None):
|
||||||
""" Triggers to turn lights on or off based on device precense. """
|
""" Triggers to turn lights on or off based on device precense. """
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
device_state_categories = device.get_categories(statemachine)
|
device_state_categories = ha.filter_categories(statemachine.categories,
|
||||||
|
device_tracker.DOMAIN)
|
||||||
|
|
||||||
if len(device_state_categories) == 0:
|
if not device_state_categories:
|
||||||
logger.error("LightTrigger:No devices given to track")
|
logger.error("LightTrigger:No devices found to track")
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
light_ids = light.get_ids(statemachine)
|
if not light_group:
|
||||||
|
light_group = light.STATE_GROUP_NAME_ALL_LIGHTS
|
||||||
|
|
||||||
if len(light_ids) == 0:
|
# Get the light IDs from the specified group
|
||||||
|
light_ids = ha.filter_categories(
|
||||||
|
group.get_categories(statemachine, light_group), light.DOMAIN, True)
|
||||||
|
|
||||||
|
if not light_ids:
|
||||||
logger.error("LightTrigger:No lights found to turn on ")
|
logger.error("LightTrigger:No lights found to turn on ")
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
@ -50,7 +56,7 @@ def setup(bus, statemachine):
|
||||||
def turn_light_on_before_sunset(light_id):
|
def turn_light_on_before_sunset(light_id):
|
||||||
""" Helper function to turn on lights slowly if there
|
""" Helper function to turn on lights slowly if there
|
||||||
are devices home and the light is not on yet. """
|
are devices home and the light is not on yet. """
|
||||||
if (device.is_home(statemachine) and
|
if (device_tracker.is_home(statemachine) and
|
||||||
not light.is_on(statemachine, light_id)):
|
not light.is_on(statemachine, light_id)):
|
||||||
|
|
||||||
light.turn_on(bus, light_id, LIGHT_TRANSITION_TIME.seconds)
|
light.turn_on(bus, light_id, LIGHT_TRANSITION_TIME.seconds)
|
||||||
|
@ -70,8 +76,8 @@ def setup(bus, statemachine):
|
||||||
|
|
||||||
# Track every time sun rises so we can schedule a time-based
|
# Track every time sun rises so we can schedule a time-based
|
||||||
# pre-sun set event
|
# pre-sun set event
|
||||||
ha.track_state_change(bus, sun.STATE_CATEGORY, sun.STATE_BELOW_HORIZON,
|
ha.track_state_change(bus, sun.STATE_CATEGORY, handle_sun_rising,
|
||||||
sun.STATE_ABOVE_HORIZON, handle_sun_rising)
|
sun.STATE_BELOW_HORIZON, sun.STATE_ABOVE_HORIZON)
|
||||||
|
|
||||||
# If the sun is already above horizon
|
# If the sun is already above horizon
|
||||||
# schedule the time-based pre-sun set event
|
# schedule the time-based pre-sun set event
|
||||||
|
@ -85,8 +91,8 @@ def setup(bus, statemachine):
|
||||||
light_needed = not (lights_are_on or sun.is_up(statemachine))
|
light_needed = not (lights_are_on or sun.is_up(statemachine))
|
||||||
|
|
||||||
# Specific device came home ?
|
# Specific device came home ?
|
||||||
if (category != device.STATE_CATEGORY_ALL_DEVICES and
|
if (category != device_tracker.STATE_CATEGORY_ALL_DEVICES and
|
||||||
new_state['state'] == device.STATE_HOME):
|
new_state['state'] == ha.STATE_HOME):
|
||||||
|
|
||||||
# These variables are needed for the elif check
|
# These variables are needed for the elif check
|
||||||
now = datetime.now()
|
now = datetime.now()
|
||||||
|
@ -120,8 +126,8 @@ def setup(bus, statemachine):
|
||||||
break
|
break
|
||||||
|
|
||||||
# Did all devices leave the house?
|
# Did all devices leave the house?
|
||||||
elif (category == device.STATE_CATEGORY_ALL_DEVICES and
|
elif (category == device_tracker.STATE_CATEGORY_ALL_DEVICES and
|
||||||
new_state['state'] == device.STATE_NOT_HOME and lights_are_on):
|
new_state['state'] == ha.STATE_NOT_HOME and lights_are_on):
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
"Everyone has left but there are devices on. Turning them off")
|
"Everyone has left but there are devices on. Turning them off")
|
||||||
|
@ -130,13 +136,12 @@ def setup(bus, statemachine):
|
||||||
|
|
||||||
# Track home coming of each seperate device
|
# Track home coming of each seperate device
|
||||||
for category in device_state_categories:
|
for category in device_state_categories:
|
||||||
ha.track_state_change(bus, category,
|
ha.track_state_change(bus, category, handle_device_state_change,
|
||||||
device.STATE_NOT_HOME, device.STATE_HOME,
|
ha.STATE_NOT_HOME, ha.STATE_HOME)
|
||||||
handle_device_state_change)
|
|
||||||
|
|
||||||
# Track when all devices are gone to shut down lights
|
# Track when all devices are gone to shut down lights
|
||||||
ha.track_state_change(bus, device.STATE_CATEGORY_ALL_DEVICES,
|
ha.track_state_change(bus, device_tracker.STATE_CATEGORY_ALL_DEVICES,
|
||||||
device.STATE_HOME, device.STATE_NOT_HOME,
|
handle_device_state_change, ha.STATE_HOME,
|
||||||
handle_device_state_change)
|
ha.STATE_NOT_HOME)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
"""
|
"""
|
||||||
homeassistant.components.sun
|
homeassistant.components.tracker
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Provides functionality to keep track of devices.
|
Provides functionality to keep track of devices.
|
||||||
|
@ -16,23 +16,23 @@ import requests
|
||||||
|
|
||||||
import homeassistant as ha
|
import homeassistant as ha
|
||||||
import homeassistant.util as util
|
import homeassistant.util as util
|
||||||
|
import homeassistant.components.group as group
|
||||||
|
|
||||||
import homeassistant.external.pynetgear as pynetgear
|
import homeassistant.external.pynetgear as pynetgear
|
||||||
|
|
||||||
DOMAIN_DEVICE_TRACKER = "device_tracker"
|
DOMAIN = "device_tracker"
|
||||||
|
|
||||||
SERVICE_DEVICE_TRACKER_RELOAD = "reload_devices_csv"
|
SERVICE_DEVICE_TRACKER_RELOAD = "reload_devices_csv"
|
||||||
|
|
||||||
STATE_CATEGORY_ALL_DEVICES = 'devices'
|
STATE_GROUP_NAME_ALL_DEVICES = 'all_tracked_devices'
|
||||||
STATE_CATEGORY_FORMAT = 'devices.{}'
|
STATE_CATEGORY_ALL_DEVICES = group.STATE_CATEGORY_FORMAT.format(
|
||||||
|
STATE_GROUP_NAME_ALL_DEVICES)
|
||||||
STATE_NOT_HOME = 'device_not_home'
|
|
||||||
STATE_HOME = 'device_home'
|
|
||||||
|
|
||||||
|
STATE_CATEGORY_FORMAT = DOMAIN + '.{}'
|
||||||
|
|
||||||
# After how much time do we consider a device not home if
|
# After how much time do we consider a device not home if
|
||||||
# it does not show up on scans
|
# it does not show up on scans
|
||||||
TIME_SPAN_FOR_ERROR_IN_SCANNING = timedelta(minutes=1)
|
TIME_SPAN_FOR_ERROR_IN_SCANNING = timedelta(minutes=3)
|
||||||
|
|
||||||
# Return cached results if last scan was less then this time ago
|
# Return cached results if last scan was less then this time ago
|
||||||
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)
|
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)
|
||||||
|
@ -41,26 +41,15 @@ MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)
|
||||||
KNOWN_DEVICES_FILE = "known_devices.csv"
|
KNOWN_DEVICES_FILE = "known_devices.csv"
|
||||||
|
|
||||||
|
|
||||||
def get_categories(statemachine):
|
|
||||||
""" Returns the categories of devices that are being tracked in the
|
|
||||||
statemachine. """
|
|
||||||
return ha.get_grouped_state_cats(statemachine, STATE_CATEGORY_FORMAT,
|
|
||||||
False)
|
|
||||||
|
|
||||||
|
|
||||||
def get_ids(statemachine):
|
|
||||||
""" Returns the devices that are being tracked in the statemachine. """
|
|
||||||
return ha.get_grouped_state_cats(statemachine, STATE_CATEGORY_FORMAT, True)
|
|
||||||
|
|
||||||
|
|
||||||
def is_home(statemachine, device_id=None):
|
def is_home(statemachine, device_id=None):
|
||||||
""" Returns if any or specified device is home. """
|
""" Returns if any or specified device is home. """
|
||||||
category = STATE_CATEGORY_FORMAT.format(device_id) if device_id \
|
category = STATE_CATEGORY_FORMAT.format(device_id) if device_id \
|
||||||
else STATE_CATEGORY_ALL_DEVICES
|
else STATE_CATEGORY_ALL_DEVICES
|
||||||
|
|
||||||
return statemachine.is_state(category, STATE_HOME)
|
return statemachine.is_state(category, ha.STATE_HOME)
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=too-many-instance-attributes
|
||||||
class DeviceTracker(object):
|
class DeviceTracker(object):
|
||||||
""" Class that tracks which devices are home and which are not. """
|
""" Class that tracks which devices are home and which are not. """
|
||||||
|
|
||||||
|
@ -88,12 +77,15 @@ class DeviceTracker(object):
|
||||||
self.update_devices(
|
self.update_devices(
|
||||||
device_scanner.scan_devices()))
|
device_scanner.scan_devices()))
|
||||||
|
|
||||||
bus.register_service(DOMAIN_DEVICE_TRACKER,
|
bus.register_service(DOMAIN,
|
||||||
SERVICE_DEVICE_TRACKER_RELOAD,
|
SERVICE_DEVICE_TRACKER_RELOAD,
|
||||||
lambda service: self._read_known_devices_file())
|
lambda service: self._read_known_devices_file())
|
||||||
|
|
||||||
self.update_devices(device_scanner.scan_devices())
|
self.update_devices(device_scanner.scan_devices())
|
||||||
|
|
||||||
|
group.setup(bus, statemachine, STATE_GROUP_NAME_ALL_DEVICES,
|
||||||
|
list(self.device_state_categories))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def device_state_categories(self):
|
def device_state_categories(self):
|
||||||
""" Returns a set containing all categories
|
""" Returns a set containing all categories
|
||||||
|
@ -119,7 +111,7 @@ class DeviceTracker(object):
|
||||||
self.known_devices[device]['last_seen'] = now
|
self.known_devices[device]['last_seen'] = now
|
||||||
|
|
||||||
self.statemachine.set_state(
|
self.statemachine.set_state(
|
||||||
self.known_devices[device]['category'], STATE_HOME)
|
self.known_devices[device]['category'], ha.STATE_HOME)
|
||||||
|
|
||||||
# For all devices we did not find, set state to NH
|
# For all devices we did not find, set state to NH
|
||||||
# But only if they have been gone for longer then the error time span
|
# But only if they have been gone for longer then the error time span
|
||||||
|
@ -131,18 +123,7 @@ class DeviceTracker(object):
|
||||||
|
|
||||||
self.statemachine.set_state(
|
self.statemachine.set_state(
|
||||||
self.known_devices[device]['category'],
|
self.known_devices[device]['category'],
|
||||||
STATE_NOT_HOME)
|
ha.STATE_NOT_HOME)
|
||||||
|
|
||||||
# Get the currently used statuses
|
|
||||||
states_of_devices = [self.statemachine.get_state(category)['state']
|
|
||||||
for category in self.device_state_categories]
|
|
||||||
|
|
||||||
# Update the all devices category
|
|
||||||
all_devices_state = (STATE_HOME if STATE_HOME
|
|
||||||
in states_of_devices else STATE_NOT_HOME)
|
|
||||||
|
|
||||||
self.statemachine.set_state(STATE_CATEGORY_ALL_DEVICES,
|
|
||||||
all_devices_state)
|
|
||||||
|
|
||||||
# If we come along any unknown devices we will write them to the
|
# If we come along any unknown devices we will write them to the
|
||||||
# known devices file but only if we did not encounter an invalid
|
# known devices file but only if we did not encounter an invalid
|
||||||
|
@ -407,8 +388,15 @@ class NetgearDeviceScanner(object):
|
||||||
self.date_updated = None
|
self.date_updated = None
|
||||||
self.last_results = []
|
self.last_results = []
|
||||||
|
|
||||||
|
self.logger.info("Netgear:Logging in")
|
||||||
|
if self._api.login():
|
||||||
self.success_init = self._update_info()
|
self.success_init = self._update_info()
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.logger.error("Netgear:Failed to Login")
|
||||||
|
|
||||||
|
self.success_init = False
|
||||||
|
|
||||||
def scan_devices(self):
|
def scan_devices(self):
|
||||||
""" Scans for new devices and return a
|
""" Scans for new devices and return a
|
||||||
list containing found device ids. """
|
list containing found device ids. """
|
||||||
|
@ -446,6 +434,8 @@ class NetgearDeviceScanner(object):
|
||||||
|
|
||||||
self.last_results = self._api.get_attached_devices()
|
self.last_results = self._api.get_attached_devices()
|
||||||
|
|
||||||
|
self.date_updated = datetime.now()
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
else:
|
else:
|
|
@ -12,7 +12,7 @@ import requests
|
||||||
|
|
||||||
import homeassistant.util as util
|
import homeassistant.util as util
|
||||||
|
|
||||||
DOMAIN_DOWNLOADER = "downloader"
|
DOMAIN = "downloader"
|
||||||
|
|
||||||
SERVICE_DOWNLOAD_FILE = "download_file"
|
SERVICE_DOWNLOAD_FILE = "download_file"
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@ def setup(bus, download_path):
|
||||||
logger.exception("FileDownloader:ConnectionError occured for {}".
|
logger.exception("FileDownloader:ConnectionError occured for {}".
|
||||||
format(service.data['url']))
|
format(service.data['url']))
|
||||||
|
|
||||||
bus.register_service(DOMAIN_DOWNLOADER, SERVICE_DOWNLOAD_FILE,
|
bus.register_service(DOMAIN, SERVICE_DOWNLOAD_FILE,
|
||||||
download_file)
|
download_file)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
|
@ -20,7 +20,7 @@ def shutdown_devices(bus, statemachine):
|
||||||
def setup(bus, statemachine):
|
def setup(bus, statemachine):
|
||||||
""" Setup services related to homeassistant. """
|
""" Setup services related to homeassistant. """
|
||||||
|
|
||||||
bus.register_service(ha.DOMAIN_HOMEASSISTANT, SERVICE_SHUTDOWN_DEVICES,
|
bus.register_service(ha.DOMAIN, SERVICE_SHUTDOWN_DEVICES,
|
||||||
lambda service: shutdown_devices(bus, statemachine))
|
lambda service: shutdown_devices(bus, statemachine))
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
122
homeassistant/components/group.py
Normal file
122
homeassistant/components/group.py
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
"""
|
||||||
|
homeassistant.components.groups
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Provides functionality to group devices that can be turned on or off.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import homeassistant as ha
|
||||||
|
|
||||||
|
DOMAIN = "group"
|
||||||
|
|
||||||
|
STATE_CATEGORY_FORMAT = DOMAIN + ".{}"
|
||||||
|
|
||||||
|
STATE_ATTR_CATEGORIES = "categories"
|
||||||
|
|
||||||
|
_GROUP_TYPES = {
|
||||||
|
"on_off": (ha.STATE_ON, ha.STATE_OFF),
|
||||||
|
"home_not_home": (ha.STATE_HOME, ha.STATE_NOT_HOME)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _get_group_type(state):
|
||||||
|
""" Determine the group type based on the given group type. """
|
||||||
|
for group_type, states in _GROUP_TYPES.items():
|
||||||
|
if state in states:
|
||||||
|
return group_type
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def get_categories(statemachine, group_name):
|
||||||
|
""" Get the categories that make up this group. """
|
||||||
|
state = statemachine.get_state(STATE_CATEGORY_FORMAT.format(group_name))
|
||||||
|
|
||||||
|
return state['attributes'][STATE_ATTR_CATEGORIES] if state else []
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=too-many-branches
|
||||||
|
def setup(bus, statemachine, name, categories):
|
||||||
|
""" Sets up a group state that is the combined state of
|
||||||
|
several states. Supports ON/OFF and DEVICE_HOME/DEVICE_NOT_HOME. """
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# Loop over the given categories to:
|
||||||
|
# - determine which group type this is (on_off, device_home)
|
||||||
|
# - if all states exist and have valid states
|
||||||
|
# - retrieve the current state of the group
|
||||||
|
errors = []
|
||||||
|
group_type, group_on, group_off, group_state = None, None, None, None
|
||||||
|
|
||||||
|
for cat in categories:
|
||||||
|
state = statemachine.get_state(cat)
|
||||||
|
|
||||||
|
# Try to determine group type if we didn't yet
|
||||||
|
if not group_type and state:
|
||||||
|
group_type = _get_group_type(state['state'])
|
||||||
|
|
||||||
|
if group_type:
|
||||||
|
group_on, group_off = _GROUP_TYPES[group_type]
|
||||||
|
group_state = group_off
|
||||||
|
|
||||||
|
else:
|
||||||
|
# We did not find a matching group_type
|
||||||
|
errors.append("Found unexpected state '{}'".format(
|
||||||
|
name, state['state']))
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
# Check if category exists
|
||||||
|
if not state:
|
||||||
|
errors.append("Category {} does not exist".format(cat))
|
||||||
|
|
||||||
|
# Check if category is valid state
|
||||||
|
elif state['state'] != group_off and state['state'] != group_on:
|
||||||
|
|
||||||
|
errors.append("State of {} is {} (expected: {}, {})".format(
|
||||||
|
cat, state['state'], group_off, group_on))
|
||||||
|
|
||||||
|
# Keep track of the group state to init later on
|
||||||
|
elif group_state == group_off and state['state'] == group_on:
|
||||||
|
group_state = group_on
|
||||||
|
|
||||||
|
if errors:
|
||||||
|
logger.error("Error setting up state group {}: {}".format(
|
||||||
|
name, ", ".join(errors)))
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
group_cat = STATE_CATEGORY_FORMAT.format(name)
|
||||||
|
state_attr = {STATE_ATTR_CATEGORIES: categories}
|
||||||
|
|
||||||
|
# pylint: disable=unused-argument
|
||||||
|
def _update_group_state(category, old_state, new_state):
|
||||||
|
""" Updates the group state based on a state change by a tracked
|
||||||
|
category. """
|
||||||
|
|
||||||
|
cur_group_state = statemachine.get_state(group_cat)['state']
|
||||||
|
|
||||||
|
# if cur_group_state = OFF and new_state = ON: set ON
|
||||||
|
# if cur_group_state = ON and new_state = OFF: research
|
||||||
|
# else: ignore
|
||||||
|
|
||||||
|
if cur_group_state == group_off and new_state['state'] == group_on:
|
||||||
|
|
||||||
|
statemachine.set_state(group_cat, group_on, state_attr)
|
||||||
|
|
||||||
|
elif cur_group_state == group_on and new_state['state'] == group_off:
|
||||||
|
|
||||||
|
# Check if any of the other states is still on
|
||||||
|
if not any([statemachine.is_state(cat, group_on)
|
||||||
|
for cat in categories if cat != category]):
|
||||||
|
statemachine.set_state(group_cat, group_off, state_attr)
|
||||||
|
|
||||||
|
for cat in categories:
|
||||||
|
ha.track_state_change(bus, cat, _update_group_state)
|
||||||
|
|
||||||
|
statemachine.set_state(group_cat, group_state, state_attr)
|
||||||
|
|
||||||
|
return True
|
|
@ -6,7 +6,7 @@ Provides functionality to emulate keyboard presses on host machine.
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
DOMAIN_KEYBOARD = "keyboard"
|
DOMAIN = "keyboard"
|
||||||
|
|
||||||
SERVICE_KEYBOARD_VOLUME_UP = "volume_up"
|
SERVICE_KEYBOARD_VOLUME_UP = "volume_up"
|
||||||
SERVICE_KEYBOARD_VOLUME_DOWN = "volume_down"
|
SERVICE_KEYBOARD_VOLUME_DOWN = "volume_down"
|
||||||
|
@ -29,27 +29,27 @@ def setup(bus):
|
||||||
keyboard = pykeyboard.PyKeyboard()
|
keyboard = pykeyboard.PyKeyboard()
|
||||||
keyboard.special_key_assignment()
|
keyboard.special_key_assignment()
|
||||||
|
|
||||||
bus.register_service(DOMAIN_KEYBOARD, SERVICE_KEYBOARD_VOLUME_UP,
|
bus.register_service(DOMAIN, SERVICE_KEYBOARD_VOLUME_UP,
|
||||||
lambda service:
|
lambda service:
|
||||||
keyboard.tap_key(keyboard.volume_up_key))
|
keyboard.tap_key(keyboard.volume_up_key))
|
||||||
|
|
||||||
bus.register_service(DOMAIN_KEYBOARD, SERVICE_KEYBOARD_VOLUME_DOWN,
|
bus.register_service(DOMAIN, SERVICE_KEYBOARD_VOLUME_DOWN,
|
||||||
lambda service:
|
lambda service:
|
||||||
keyboard.tap_key(keyboard.volume_down_key))
|
keyboard.tap_key(keyboard.volume_down_key))
|
||||||
|
|
||||||
bus.register_service(DOMAIN_KEYBOARD, SERVICE_KEYBOARD_VOLUME_MUTE,
|
bus.register_service(DOMAIN, SERVICE_KEYBOARD_VOLUME_MUTE,
|
||||||
lambda service:
|
lambda service:
|
||||||
keyboard.tap_key(keyboard.volume_mute_key))
|
keyboard.tap_key(keyboard.volume_mute_key))
|
||||||
|
|
||||||
bus.register_service(DOMAIN_KEYBOARD, SERVICE_KEYBOARD_MEDIA_PLAY_PAUSE,
|
bus.register_service(DOMAIN, SERVICE_KEYBOARD_MEDIA_PLAY_PAUSE,
|
||||||
lambda service:
|
lambda service:
|
||||||
keyboard.tap_key(keyboard.media_play_pause_key))
|
keyboard.tap_key(keyboard.media_play_pause_key))
|
||||||
|
|
||||||
bus.register_service(DOMAIN_KEYBOARD, SERVICE_KEYBOARD_MEDIA_NEXT_TRACK,
|
bus.register_service(DOMAIN, SERVICE_KEYBOARD_MEDIA_NEXT_TRACK,
|
||||||
lambda service:
|
lambda service:
|
||||||
keyboard.tap_key(keyboard.media_next_track_key))
|
keyboard.tap_key(keyboard.media_next_track_key))
|
||||||
|
|
||||||
bus.register_service(DOMAIN_KEYBOARD, SERVICE_KEYBOARD_MEDIA_PREV_TRACK,
|
bus.register_service(DOMAIN, SERVICE_KEYBOARD_MEDIA_PREV_TRACK,
|
||||||
lambda service:
|
lambda service:
|
||||||
keyboard.tap_key(keyboard.media_prev_track_key))
|
keyboard.tap_key(keyboard.media_prev_track_key))
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
"""
|
"""
|
||||||
homeassistant.components.sun
|
homeassistant.components.light
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Provides functionality to interact with lights.
|
Provides functionality to interact with lights.
|
||||||
|
@ -10,14 +10,15 @@ from datetime import datetime, timedelta
|
||||||
|
|
||||||
import homeassistant as ha
|
import homeassistant as ha
|
||||||
import homeassistant.util as util
|
import homeassistant.util as util
|
||||||
|
import homeassistant.components.group as group
|
||||||
|
|
||||||
DOMAIN = "light"
|
DOMAIN = "light"
|
||||||
|
|
||||||
STATE_CATEGORY_ALL_LIGHTS = 'lights'
|
STATE_GROUP_NAME_ALL_LIGHTS = 'all_lights'
|
||||||
STATE_CATEGORY_FORMAT = "lights.{}"
|
STATE_CATEGORY_ALL_LIGHTS = group.STATE_CATEGORY_FORMAT.format(
|
||||||
|
STATE_GROUP_NAME_ALL_LIGHTS)
|
||||||
|
|
||||||
STATE_ON = "on"
|
STATE_CATEGORY_FORMAT = DOMAIN + ".{}"
|
||||||
STATE_OFF = "off"
|
|
||||||
|
|
||||||
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10)
|
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10)
|
||||||
|
|
||||||
|
@ -27,7 +28,7 @@ def is_on(statemachine, light_id=None):
|
||||||
category = STATE_CATEGORY_FORMAT.format(light_id) if light_id \
|
category = STATE_CATEGORY_FORMAT.format(light_id) if light_id \
|
||||||
else STATE_CATEGORY_ALL_LIGHTS
|
else STATE_CATEGORY_ALL_LIGHTS
|
||||||
|
|
||||||
return statemachine.is_state(category, STATE_ON)
|
return statemachine.is_state(category, ha.STATE_ON)
|
||||||
|
|
||||||
|
|
||||||
def turn_on(bus, light_id=None, transition_seconds=None):
|
def turn_on(bus, light_id=None, transition_seconds=None):
|
||||||
|
@ -56,14 +57,11 @@ def turn_off(bus, light_id=None, transition_seconds=None):
|
||||||
bus.call_service(DOMAIN, ha.SERVICE_TURN_OFF, data)
|
bus.call_service(DOMAIN, ha.SERVICE_TURN_OFF, data)
|
||||||
|
|
||||||
|
|
||||||
def get_ids(statemachine):
|
|
||||||
""" Get the light IDs that are being tracked in the statemachine. """
|
|
||||||
return ha.get_grouped_state_cats(statemachine, STATE_CATEGORY_FORMAT, True)
|
|
||||||
|
|
||||||
|
|
||||||
def setup(bus, statemachine, light_control):
|
def setup(bus, statemachine, light_control):
|
||||||
""" Exposes light control via statemachine and services. """
|
""" Exposes light control via statemachine and services. """
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
def update_light_state(time): # pylint: disable=unused-argument
|
def update_light_state(time): # pylint: disable=unused-argument
|
||||||
""" Track the state of the lights. """
|
""" Track the state of the lights. """
|
||||||
try:
|
try:
|
||||||
|
@ -74,6 +72,8 @@ def setup(bus, statemachine, light_control):
|
||||||
should_update = True
|
should_update = True
|
||||||
|
|
||||||
if should_update:
|
if should_update:
|
||||||
|
logger.info("Updating light status")
|
||||||
|
|
||||||
update_light_state.last_updated = datetime.now()
|
update_light_state.last_updated = datetime.now()
|
||||||
|
|
||||||
status = {light_id: light_control.is_light_on(light_id)
|
status = {light_id: light_control.is_light_on(light_id)
|
||||||
|
@ -82,17 +82,21 @@ def setup(bus, statemachine, light_control):
|
||||||
for light_id, state in status.items():
|
for light_id, state in status.items():
|
||||||
state_category = STATE_CATEGORY_FORMAT.format(light_id)
|
state_category = STATE_CATEGORY_FORMAT.format(light_id)
|
||||||
|
|
||||||
statemachine.set_state(state_category,
|
new_state = ha.STATE_ON if state else ha.STATE_OFF
|
||||||
STATE_ON if state
|
|
||||||
else STATE_OFF)
|
|
||||||
|
|
||||||
statemachine.set_state(STATE_CATEGORY_ALL_LIGHTS,
|
statemachine.set_state(state_category, new_state)
|
||||||
STATE_ON if True in status.values()
|
|
||||||
else STATE_OFF)
|
|
||||||
|
|
||||||
ha.track_time_change(bus, update_light_state, second=[0, 30])
|
ha.track_time_change(bus, update_light_state, second=[0, 30])
|
||||||
|
|
||||||
def handle_light_event(service):
|
update_light_state(None)
|
||||||
|
|
||||||
|
# Track the all lights state
|
||||||
|
light_cats = [STATE_CATEGORY_FORMAT.format(light_id) for light_id
|
||||||
|
in light_control.light_ids]
|
||||||
|
|
||||||
|
group.setup(bus, statemachine, STATE_GROUP_NAME_ALL_LIGHTS, light_cats)
|
||||||
|
|
||||||
|
def handle_light_service(service):
|
||||||
""" Hande a turn light on or off service call. """
|
""" Hande a turn light on or off service call. """
|
||||||
light_id = service.data.get("light_id", None)
|
light_id = service.data.get("light_id", None)
|
||||||
transition_seconds = service.data.get("transition_seconds", None)
|
transition_seconds = service.data.get("transition_seconds", None)
|
||||||
|
@ -106,12 +110,10 @@ def setup(bus, statemachine, light_control):
|
||||||
|
|
||||||
# Listen for light on and light off events
|
# Listen for light on and light off events
|
||||||
bus.register_service(DOMAIN, ha.SERVICE_TURN_ON,
|
bus.register_service(DOMAIN, ha.SERVICE_TURN_ON,
|
||||||
handle_light_event)
|
handle_light_service)
|
||||||
|
|
||||||
bus.register_service(DOMAIN, ha.SERVICE_TURN_OFF,
|
bus.register_service(DOMAIN, ha.SERVICE_TURN_OFF,
|
||||||
handle_light_event)
|
handle_light_service)
|
||||||
|
|
||||||
update_light_state(None)
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue