def setup(hass, config): """ Sets up the HTTP API and debug interface. """ if not util.validate_config(config, {DOMAIN: [CONF_API_PASSWORD]}, _LOGGER): return False api_password = config[DOMAIN]['api_password'] # If no server host is given, accept all incoming requests server_host = config[DOMAIN].get(CONF_SERVER_HOST, '0.0.0.0') server_port = config[DOMAIN].get(CONF_SERVER_PORT, rem.SERVER_PORT) development = config[DOMAIN].get(CONF_DEVELOPMENT, "") == "1" server = HomeAssistantHTTPServer((server_host, server_port), RequestHandler, hass, api_password, development) hass.listen_once_event( ha.EVENT_HOMEASSISTANT_START, lambda event: threading.Thread(target=server.start, daemon=True).start()) # If no local api set, set one with known information if isinstance(hass, rem.HomeAssistant) and hass.local_api is None: hass.local_api = \ rem.API(util.get_local_ip(), api_password, server_port) return True
def setup(hass, config): """ Sets up the HTTP API and debug interface. """ if not util.validate_config(config, {DOMAIN: [CONF_API_PASSWORD]}, _LOGGER): return False api_password = config[DOMAIN]['api_password'] # If no server host is given, accept all incoming requests server_host = config[DOMAIN].get(CONF_SERVER_HOST, '0.0.0.0') server_port = config[DOMAIN].get(CONF_SERVER_PORT, rem.SERVER_PORT) development = config[DOMAIN].get(CONF_DEVELOPMENT, "") == "1" server = HomeAssistantHTTPServer((server_host, server_port), RequestHandler, hass, api_password, development) hass.listen_once_event( ha.EVENT_HOMEASSISTANT_START, lambda event: threading.Thread(target=server.start, daemon=True).start()) hass.listen_once_event( ha.EVENT_HOMEASSISTANT_STOP, lambda event: server.shutdown()) # If no local api set, set one with known information if isinstance(hass, rem.HomeAssistant) and hass.local_api is None: hass.local_api = \ rem.API(util.get_local_ip(), api_password, server_port) return True
def setup(hass, config): """ Sets up the device tracker. """ if not util.validate_config(config, {DOMAIN: [ha.CONF_TYPE]}, _LOGGER): return False tracker_type = config[DOMAIN][ha.CONF_TYPE] tracker_implementation = get_component( 'device_tracker.{}'.format(tracker_type)) if tracker_implementation is None: _LOGGER.error("Unknown device_tracker type specified.") return False device_scanner = tracker_implementation.get_scanner(hass, config) if device_scanner is None: _LOGGER.error("Failed to initialize device scanner for %s", tracker_type) return False DeviceTracker(hass, device_scanner) return True
def setup(hass, config): """ Tracks the state of the sun. """ logger = logging.getLogger(__name__) if not util.validate_config(config, {ha.DOMAIN: [ha.CONF_LATITUDE, ha.CONF_LONGITUDE]}, logger): return False try: import ephem except ImportError: logger.exception("Error while importing dependency ephem.") return False sun = ephem.Sun() # pylint: disable=no-member latitude = config[ha.DOMAIN][ha.CONF_LATITUDE] longitude = config[ha.DOMAIN][ha.CONF_LONGITUDE] def update_sun_state(now): # pylint: disable=unused-argument """ Method to update the current state of the sun and set time of next setting and rising. """ observer = ephem.Observer() observer.lat = latitude # pylint: disable=assigning-non-slot observer.long = longitude # pylint: disable=assigning-non-slot next_rising_dt = ephem.localtime(observer.next_rising(sun)) next_setting_dt = ephem.localtime(observer.next_setting(sun)) if next_rising_dt > next_setting_dt: new_state = STATE_ABOVE_HORIZON next_change = next_setting_dt else: new_state = STATE_BELOW_HORIZON next_change = next_rising_dt logger.info( "{}. Next change: {}".format(new_state, next_change.strftime("%H:%M"))) state_attributes = { STATE_ATTR_NEXT_RISING: util.datetime_to_str(next_rising_dt), STATE_ATTR_NEXT_SETTING: util.datetime_to_str(next_setting_dt) } hass.states.set(ENTITY_ID, new_state, state_attributes) # +10 seconds to be sure that the change has occured hass.track_point_in_time(update_sun_state, next_change + timedelta(seconds=10)) update_sun_state(None) return True
def get_scanner(hass, config): """ Validates config and returns a Tomato scanner. """ if not util.validate_config(config, {DOMAIN: [ha.CONF_HOST, ha.CONF_USERNAME, ha.CONF_PASSWORD, CONF_HTTP_ID]}, _LOGGER): return None return TomatoDeviceScanner(config[DOMAIN])
def get_scanner(hass, config): """ Validates config and returns a Tomato scanner. """ if not util.validate_config(config, { DOMAIN: [ha.CONF_HOST, ha.CONF_USERNAME, ha.CONF_PASSWORD, CONF_HTTP_ID] }, _LOGGER): return None return TomatoDeviceScanner(config[DOMAIN])
def get_scanner(hass, config): """ Validates config and returns a Netgear scanner. """ if not util.validate_config( config, {DOMAIN: [ha.CONF_HOST, ha.CONF_USERNAME, ha.CONF_PASSWORD]}, _LOGGER): return None scanner = NetgearDeviceScanner(config[DOMAIN]) return scanner if scanner.success_init else None
def setup(hass, config): """ Tracks the state of the sun. """ logger = logging.getLogger(__name__) if not util.validate_config( config, {ha.DOMAIN: [ha.CONF_LATITUDE, ha.CONF_LONGITUDE]}, logger): return False try: import ephem except ImportError: logger.exception("Error while importing dependency ephem.") return False sun = ephem.Sun() # pylint: disable=no-member latitude = config[ha.DOMAIN][ha.CONF_LATITUDE] longitude = config[ha.DOMAIN][ha.CONF_LONGITUDE] def update_sun_state(now): # pylint: disable=unused-argument """ Method to update the current state of the sun and set time of next setting and rising. """ observer = ephem.Observer() observer.lat = latitude # pylint: disable=assigning-non-slot observer.long = longitude # pylint: disable=assigning-non-slot next_rising_dt = ephem.localtime(observer.next_rising(sun)) next_setting_dt = ephem.localtime(observer.next_setting(sun)) if next_rising_dt > next_setting_dt: new_state = STATE_ABOVE_HORIZON next_change = next_setting_dt else: new_state = STATE_BELOW_HORIZON next_change = next_rising_dt logger.info("%s. Next change: %s", new_state, next_change.strftime("%H:%M")) state_attributes = { STATE_ATTR_NEXT_RISING: util.datetime_to_str(next_rising_dt), STATE_ATTR_NEXT_SETTING: util.datetime_to_str(next_setting_dt) } hass.states.set(ENTITY_ID, new_state, state_attributes) # +10 seconds to be sure that the change has occured hass.track_point_in_time(update_sun_state, next_change + timedelta(seconds=10)) update_sun_state(None) return True
def get_scanner(hass, config): """ Validates config and returns a Luci scanner. """ if not util.validate_config(config, {DOMAIN: [ha.CONF_HOST, ha.CONF_USERNAME, ha.CONF_PASSWORD]}, _LOGGER): return None scanner = LuciDeviceScanner(config[DOMAIN]) return scanner if scanner.success_init else None
def setup(hass, config): """ Sets up the device tracker. """ logger = logging.getLogger(__name__) # We have flexible requirements for device tracker so # we cannot use util.validate_config conf = config[DOMAIN] if ha.CONF_TYPE not in conf: logger.error( 'Missing required configuration item in {}: {}'.format( DOMAIN, ha.CONF_TYPE)) return False fields = [ha.CONF_HOST, ha.CONF_USERNAME, ha.CONF_PASSWORD] router_type = conf[ha.CONF_TYPE] if router_type == 'tomato': fields.append(CONF_HTTP_ID) scanner = TomatoDeviceScanner elif router_type == 'netgear': scanner = NetgearDeviceScanner elif router_type == 'luci': scanner = LuciDeviceScanner else: logger.error('Found unknown router type {}'.format(router_type)) return False if not util.validate_config(config, {DOMAIN: fields}, logger): return False device_scanner = scanner(conf) if not device_scanner.success_init: logger.error( "Failed to initialize device scanner for {}".format(router_type)) return False DeviceTracker(hass, device_scanner) return True
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] switch_init = get_component('switch.{}'.format(switch_type)) if switch_init is None: logger.error("Error loading switch component %s", switch_type) return False switches = switch_init.get_switches(hass, 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
def setup(hass, config): """ Tracks the state of the sun. """ logger = logging.getLogger(__name__) if not util.validate_config(config, {ha.DOMAIN: [ha.CONF_LATITUDE, ha.CONF_LONGITUDE]}, logger): return False try: import ephem except ImportError: logger.exception("Error while importing dependency ephem.") return False sun = ephem.Sun() # pylint: disable=no-member latitude = config[ha.DOMAIN][ha.CONF_LATITUDE] longitude = config[ha.DOMAIN][ha.CONF_LONGITUDE] # Validate latitude and longitude observer = ephem.Observer() errors = [] try: observer.lat = latitude # pylint: disable=assigning-non-slot except ValueError: errors.append("invalid value for latitude given: {}".format(latitude)) try: observer.long = longitude # pylint: disable=assigning-non-slot except ValueError: errors.append("invalid value for latitude given: {}".format(latitude)) if errors: logger.error("Error setting up: %s", ", ".join(errors)) return False def update_sun_state(now): """ Method to update the current state of the sun and set time of next setting and rising. """ utc_offset = datetime.utcnow() - datetime.now() utc_now = now + utc_offset observer = ephem.Observer() observer.lat = latitude # pylint: disable=assigning-non-slot observer.long = longitude # pylint: disable=assigning-non-slot next_rising_dt = ephem.localtime( observer.next_rising(sun, start=utc_now)) next_setting_dt = ephem.localtime( observer.next_setting(sun, start=utc_now)) if next_rising_dt > next_setting_dt: new_state = STATE_ABOVE_HORIZON next_change = next_setting_dt else: new_state = STATE_BELOW_HORIZON next_change = next_rising_dt logger.info("%s. Next change: %s", new_state, next_change.strftime("%H:%M")) state_attributes = { STATE_ATTR_NEXT_RISING: util.datetime_to_str(next_rising_dt), STATE_ATTR_NEXT_SETTING: util.datetime_to_str(next_setting_dt) } hass.states.set(ENTITY_ID, new_state, state_attributes) # +1 second so Ephem will report it has set hass.track_point_in_time(update_sun_state, next_change + timedelta(seconds=1)) update_sun_state(datetime.now()) return True
def setup(hass, config): """ Exposes light control via statemachine and services. """ if not util.validate_config(config, {DOMAIN: [ha.CONF_TYPE]}, _LOGGER): return False # Load built-in profiles and custom profiles profile_paths = [os.path.join(os.path.dirname(__file__), LIGHT_PROFILES_FILE), hass.get_config_path(LIGHT_PROFILES_FILE)] profiles = {} for profile_path in profile_paths: if os.path.isfile(profile_path): with open(profile_path) as inp: reader = csv.reader(inp) # Skip the header next(reader, None) try: for profile_id, color_x, color_y, brightness in reader: profiles[profile_id] = (float(color_x), float(color_y), int(brightness)) except ValueError: # ValueError if not 4 values per row # ValueError if convert to float/int failed _LOGGER.error( "Error parsing light profiles from %s", profile_path) return False # Load platform light_type = config[DOMAIN][ha.CONF_TYPE] light_init = get_component('light.{}'.format(light_type)) if light_init is None: _LOGGER.error("Unknown light type specified: %s", light_type) return False lights = light_init.get_lights(hass, config[DOMAIN]) if len(lights) == 0: _LOGGER.error("No lights found") return False ent_to_light = {} no_name_count = 1 for light in lights: name = light.get_name() if name is None: name = "Light #{}".format(no_name_count) no_name_count += 1 entity_id = util.ensure_unique_string( ENTITY_ID_FORMAT.format(util.slugify(name)), list(ent_to_light.keys())) light.entity_id = entity_id ent_to_light[entity_id] = light # pylint: disable=unused-argument def update_lights_state(now): """ Update the states of all the lights. """ for light in lights: light.update_ha_state(hass) update_lights_state(None) # Track all lights in a group group.setup_group( hass, GROUP_NAME_ALL_LIGHTS, ent_to_light.keys(), False) def handle_light_service(service): """ Hande a turn light on or off service call. """ # Get and validate data dat = service.data # Convert the entity ids to valid light ids lights = [ent_to_light[entity_id] for entity_id in extract_entity_ids(hass, service) if entity_id in ent_to_light] if not lights: lights = list(ent_to_light.values()) params = {} transition = util.convert(dat.get(ATTR_TRANSITION), int) if transition is not None: params[ATTR_TRANSITION] = transition if service.service == SERVICE_TURN_OFF: for light in lights: # pylint: disable=star-args light.turn_off(**params) else: # Processing extra data for turn light on request # We process the profile first so that we get the desired # behavior that extra service data attributes overwrite # profile values profile = profiles.get(dat.get(ATTR_PROFILE)) if profile: # *color, bright = profile *params[ATTR_XY_COLOR], params[ATTR_BRIGHTNESS] = profile if ATTR_BRIGHTNESS in dat: # We pass in the old value as the default parameter if parsing # of the new one goes wrong. params[ATTR_BRIGHTNESS] = util.convert( dat.get(ATTR_BRIGHTNESS), int, params.get(ATTR_BRIGHTNESS)) if ATTR_XY_COLOR in dat: try: # xy_color should be a list containing 2 floats xycolor = dat.get(ATTR_XY_COLOR) # Without this check, a xycolor with value '99' would work if not isinstance(xycolor, str): params[ATTR_XY_COLOR] = [float(val) for val in xycolor] except (TypeError, ValueError): # TypeError if xy_color is not iterable # ValueError if value could not be converted to float pass if ATTR_RGB_COLOR in dat: try: # rgb_color should be a list containing 3 ints rgb_color = dat.get(ATTR_RGB_COLOR) if len(rgb_color) == 3: params[ATTR_XY_COLOR] = \ util.color_RGB_to_xy(int(rgb_color[0]), int(rgb_color[1]), int(rgb_color[2])) except (TypeError, ValueError): # TypeError if rgb_color is not iterable # ValueError if not all values can be converted to int pass for light in lights: # pylint: disable=star-args light.turn_on(**params) for light in lights: light.update_ha_state(hass, 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 hass.services.register(DOMAIN, SERVICE_TURN_ON, handle_light_service) hass.services.register(DOMAIN, SERVICE_TURN_OFF, handle_light_service) return True
def setup(hass, config): """ Listens for download events to download files. """ logger = logging.getLogger(__name__) try: import requests except ImportError: logger.exception(("Failed to import requests. " "Did you maybe not execute 'pip install requests'?")) return False if not util.validate_config(config, {DOMAIN: [CONF_DOWNLOAD_DIR]}, logger): return False download_path = config[DOMAIN][CONF_DOWNLOAD_DIR] if not os.path.isdir(download_path): logger.error( "Download path %s does not exist. File Downloader not active.", download_path) return False def download_file(service): """ Starts thread to download file specified in the url. """ if ATTR_URL not in service.data: logger.error("Service called but 'url' parameter not specified.") return def do_download(): """ Downloads the file. """ try: url = service.data[ATTR_URL] subdir = service.data.get(ATTR_SUBDIR) if subdir: subdir = util.sanitize_filename(subdir) final_path = None req = requests.get(url, stream=True, timeout=10) if req.status_code == 200: filename = None if 'content-disposition' in req.headers: match = re.findall(r"filename=(\S+)", req.headers['content-disposition']) if len(match) > 0: filename = match[0].strip("'\" ") if not filename: filename = os.path.basename( url).strip() if not filename: filename = "ha_download" # Remove stuff to ruin paths filename = util.sanitize_filename(filename) # Do we want to download to subdir, create if needed if subdir: subdir_path = os.path.join(download_path, subdir) # Ensure subdir exist if not os.path.isdir(subdir_path): os.makedirs(subdir_path) final_path = os.path.join(subdir_path, filename) else: final_path = os.path.join(download_path, filename) path, ext = os.path.splitext(final_path) # If file exist append a number. # We test filename, filename_2.. tries = 1 final_path = path + ext while os.path.isfile(final_path): tries += 1 final_path = "{}_{}.{}".format(path, tries, ext) logger.info("%s -> %s", url, final_path) with open(final_path, 'wb') as fil: for chunk in req.iter_content(1024): fil.write(chunk) logger.info("Downloading of %s done", url) except requests.exceptions.ConnectionError: logger.exception("ConnectionError occured for %s", url) # Remove file if we started downloading but failed if final_path and os.path.isfile(final_path): os.remove(final_path) threading.Thread(target=do_download).start() hass.services.register(DOMAIN, SERVICE_DOWNLOAD_FILE, download_file) return True
def setup(hass, config): """ Exposes light control via statemachine and services. """ if not util.validate_config(config, {DOMAIN: [ha.CONF_TYPE]}, _LOGGER): return False light_type = config[DOMAIN][ha.CONF_TYPE] light_init = get_component('light.{}'.format(light_type)) if light_init is None: _LOGGER.error("Unknown light type specified: %s", light_type) return False lights = light_init.get_lights(hass, config[DOMAIN]) if len(lights) == 0: _LOGGER.error("No lights found") return False ent_to_light = {} no_name_count = 1 for light in lights: name = light.get_name() if name is None: name = "Light #{}".format(no_name_count) no_name_count += 1 entity_id = util.ensure_unique_string( ENTITY_ID_FORMAT.format(util.slugify(name)), list(ent_to_light.keys())) light.entity_id = entity_id ent_to_light[entity_id] = light # Load built-in profiles and custom profiles profile_paths = [ os.path.join(os.path.dirname(__file__), LIGHT_PROFILES_FILE), hass.get_config_path(LIGHT_PROFILES_FILE) ] profiles = {} for profile_path in profile_paths: if os.path.isfile(profile_path): with open(profile_path) as inp: reader = csv.reader(inp) # Skip the header next(reader, None) try: for profile_id, color_x, color_y, brightness in reader: profiles[profile_id] = (float(color_x), float(color_y), int(brightness)) except ValueError: # ValueError if not 4 values per row # ValueError if convert to float/int failed _LOGGER.error("Error parsing light profiles from %s", profile_path) return False # pylint: disable=unused-argument def update_lights_state(now): """ Update the states of all the lights. """ for light in lights: light.update_ha_state(hass) update_lights_state(None) # Track all lights in a group group.setup_group(hass, GROUP_NAME_ALL_LIGHTS, ent_to_light.keys(), False) def handle_light_service(service): """ Hande a turn light on or off service call. """ # Get and validate data dat = service.data # Convert the entity ids to valid light ids lights = [ ent_to_light[entity_id] for entity_id in extract_entity_ids(hass, service) if entity_id in ent_to_light ] if not lights: lights = list(ent_to_light.values()) transition = util.convert(dat.get(ATTR_TRANSITION), int) if service.service == SERVICE_TURN_OFF: for light in lights: light.turn_off(transition=transition) else: # Processing extra data for turn light on request # We process the profile first so that we get the desired # behavior that extra service data attributes overwrite # profile values profile = profiles.get(dat.get(ATTR_PROFILE)) if profile: *color, bright = profile else: color, bright = None, None if ATTR_BRIGHTNESS in dat: bright = util.convert(dat.get(ATTR_BRIGHTNESS), int) if ATTR_XY_COLOR in dat: try: # xy_color should be a list containing 2 floats xy_color = dat.get(ATTR_XY_COLOR) if len(xy_color) == 2: color = [float(val) for val in xy_color] except (TypeError, ValueError): # TypeError if xy_color is not iterable # ValueError if value could not be converted to float pass if ATTR_RGB_COLOR in dat: try: # rgb_color should be a list containing 3 ints rgb_color = dat.get(ATTR_RGB_COLOR) if len(rgb_color) == 3: color = util.color_RGB_to_xy(int(rgb_color[0]), int(rgb_color[1]), int(rgb_color[2])) except (TypeError, ValueError): # TypeError if rgb_color is not iterable # ValueError if not all values can be converted to int pass for light in lights: light.turn_on(transition=transition, brightness=bright, xy_color=color) for light in lights: light.update_ha_state(hass, 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 hass.services.register(DOMAIN, SERVICE_TURN_ON, handle_light_service) hass.services.register(DOMAIN, SERVICE_TURN_OFF, handle_light_service) return True
def setup(hass, config): """ Exposes light control via statemachine and services. """ logger = logging.getLogger(__name__) if not util.validate_config(config, {DOMAIN: [ha.CONF_TYPE]}, logger): return False light_type = config[DOMAIN][ha.CONF_TYPE] if light_type == 'hue': light_init = HueLightControl else: logger.error("Found unknown light type: {}".format(light_type)) return False light_control = light_init(hass, config[DOMAIN]) ent_to_light = {} light_to_ent = {} def _update_light_state(light_id, light_state): """ Update statemachine based on the LightState passed in. """ name = light_control.get_name(light_id) or "Unknown Light" try: entity_id = light_to_ent[light_id] except KeyError: # We have not seen this light before, set it up # Create entity id logger.info("Found new light {}".format(name)) entity_id = util.ensure_unique_string( ENTITY_ID_FORMAT.format(util.slugify(name)), list(ent_to_light.keys())) ent_to_light[entity_id] = light_id light_to_ent[light_id] = entity_id state_attr = {ATTR_FRIENDLY_NAME: name} if light_state.on: state = STATE_ON if light_state.brightness: state_attr[ATTR_BRIGHTNESS] = light_state.brightness if light_state.color: state_attr[ATTR_XY_COLOR] = light_state.color else: state = STATE_OFF hass.states.set(entity_id, state, state_attr) def update_light_state(light_id): """ Update the state of specified light. """ _update_light_state(light_id, light_control.get(light_id)) # pylint: disable=unused-argument def update_lights_state(time, force_reload=False): """ Update the state of all the lights. """ # First time this method gets called, force_reload should be True if force_reload or \ datetime.now() - update_lights_state.last_updated > \ MIN_TIME_BETWEEN_SCANS: logger.info("Updating light status") update_lights_state.last_updated = datetime.now() for light_id, light_state in light_control.gets().items(): _update_light_state(light_id, light_state) # Update light state and discover lights for tracking the group update_lights_state(None, True) if len(ent_to_light) == 0: logger.error("No lights found") return False # Track all lights in a group group.setup_group( hass, GROUP_NAME_ALL_LIGHTS, light_to_ent.values(), False) # Load built-in profiles and custom profiles profile_paths = [os.path.join(os.path.dirname(__file__), LIGHT_PROFILES_FILE), hass.get_config_path(LIGHT_PROFILES_FILE)] profiles = {} for profile_path in profile_paths: if os.path.isfile(profile_path): with open(profile_path) as inp: reader = csv.reader(inp) # Skip the header next(reader, None) try: for profile_id, color_x, color_y, brightness in reader: profiles[profile_id] = (float(color_x), float(color_y), int(brightness)) except ValueError: # ValueError if not 4 values per row # ValueError if convert to float/int failed logger.error( "Error parsing light profiles from {}".format( profile_path)) return False def handle_light_service(service): """ Hande a turn light on or off service call. """ # Get and validate data dat = service.data # Convert the entity ids to valid light ids light_ids = [ent_to_light[entity_id] for entity_id in extract_entity_ids(hass, service) if entity_id in ent_to_light] if not light_ids: light_ids = list(ent_to_light.values()) transition = util.convert(dat.get(ATTR_TRANSITION), int) if service.service == SERVICE_TURN_OFF: light_control.turn_light_off(light_ids, transition) else: # Processing extra data for turn light on request # We process the profile first so that we get the desired # behavior that extra service data attributes overwrite # profile values profile = profiles.get(dat.get(ATTR_PROFILE)) if profile: *color, bright = profile else: color, bright = None, None if ATTR_BRIGHTNESS in dat: bright = util.convert(dat.get(ATTR_BRIGHTNESS), int) if ATTR_XY_COLOR in dat: try: # xy_color should be a list containing 2 floats xy_color = dat.get(ATTR_XY_COLOR) if len(xy_color) == 2: color = [float(val) for val in xy_color] except (TypeError, ValueError): # TypeError if xy_color is not iterable # ValueError if value could not be converted to float pass if ATTR_RGB_COLOR in dat: try: # rgb_color should be a list containing 3 ints rgb_color = dat.get(ATTR_RGB_COLOR) if len(rgb_color) == 3: color = util.color_RGB_to_xy(int(rgb_color[0]), int(rgb_color[1]), int(rgb_color[2])) except (TypeError, ValueError): # TypeError if rgb_color is not iterable # ValueError if not all values can be converted to int pass light_control.turn_light_on(light_ids, transition, bright, color) # Update state of lights touched. If there was only 1 light selected # then just update that light else update all if len(light_ids) == 1: update_light_state(light_ids[0]) else: update_lights_state(None, 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 hass.services.register(DOMAIN, SERVICE_TURN_ON, handle_light_service) hass.services.register(DOMAIN, SERVICE_TURN_OFF, handle_light_service) return True
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
def setup(hass, config): """ Tracks the state of the sun. """ logger = logging.getLogger(__name__) if not util.validate_config( config, {ha.DOMAIN: [ha.CONF_LATITUDE, ha.CONF_LONGITUDE]}, logger): return False try: import ephem except ImportError: logger.exception("Error while importing dependency ephem.") return False sun = ephem.Sun() # pylint: disable=no-member latitude = config[ha.DOMAIN][ha.CONF_LATITUDE] longitude = config[ha.DOMAIN][ha.CONF_LONGITUDE] # Validate latitude and longitude observer = ephem.Observer() errors = [] try: observer.lat = latitude # pylint: disable=assigning-non-slot except ValueError: errors.append("invalid value for latitude given: {}".format(latitude)) try: observer.long = longitude # pylint: disable=assigning-non-slot except ValueError: errors.append("invalid value for latitude given: {}".format(latitude)) if errors: logger.error("Error setting up: %s", ", ".join(errors)) return False def update_sun_state(now): """ Method to update the current state of the sun and set time of next setting and rising. """ utc_offset = datetime.utcnow() - datetime.now() utc_now = now + utc_offset observer = ephem.Observer() observer.lat = latitude # pylint: disable=assigning-non-slot observer.long = longitude # pylint: disable=assigning-non-slot next_rising_dt = ephem.localtime( observer.next_rising(sun, start=utc_now)) next_setting_dt = ephem.localtime( observer.next_setting(sun, start=utc_now)) if next_rising_dt > next_setting_dt: new_state = STATE_ABOVE_HORIZON next_change = next_setting_dt else: new_state = STATE_BELOW_HORIZON next_change = next_rising_dt logger.info("%s. Next change: %s", new_state, next_change.strftime("%H:%M")) state_attributes = { STATE_ATTR_NEXT_RISING: util.datetime_to_str(next_rising_dt), STATE_ATTR_NEXT_SETTING: util.datetime_to_str(next_setting_dt) } hass.states.set(ENTITY_ID, new_state, state_attributes) # +1 second so Ephem will report it has set hass.track_point_in_time(update_sun_state, next_change + timedelta(seconds=1)) update_sun_state(datetime.now()) return True