def test_setup_group(self): """ Test setup_group method. """ # Try to setup a group with mixed groupable states self.hass.states.set('device_tracker.Paulus', STATE_HOME) self.assertTrue( group.setup_group(self.hass, 'person_and_light', ['light.Bowl', 'device_tracker.Paulus'])) self.assertEqual( STATE_ON, self.hass.states.get( group.ENTITY_ID_FORMAT.format('person_and_light')).state) # Try to setup a group with a non existing state self.assertNotIn('non.existing', self.hass.states.entity_ids()) self.assertTrue( group.setup_group(self.hass, 'light_and_nothing', ['light.Bowl', 'non.existing'])) self.assertEqual( STATE_ON, self.hass.states.get( group.ENTITY_ID_FORMAT.format('light_and_nothing')).state) # Try to setup a group with non groupable states self.hass.states.set('cast.living_room', "Plex") self.hass.states.set('cast.bedroom', "Netflix") self.assertFalse( group.setup_group(self.hass, 'chromecasts', ['cast.living_room', 'cast.bedroom'])) # Try to setup an empty group self.assertFalse(group.setup_group(self.hass, 'nothing', []))
def test_setup_group(self): """ Test setup_group method. """ # Try to setup a group with mixed groupable states self.hass.states.set('device_tracker.Paulus', STATE_HOME) self.assertTrue(group.setup_group( self.hass, 'person_and_light', ['light.Bowl', 'device_tracker.Paulus'])) self.assertEqual( STATE_ON, self.hass.states.get( group.ENTITY_ID_FORMAT.format('person_and_light')).state) # Try to setup a group with a non existing state self.assertNotIn('non.existing', self.hass.states.entity_ids()) self.assertTrue(group.setup_group( self.hass, 'light_and_nothing', ['light.Bowl', 'non.existing'])) self.assertEqual( STATE_ON, self.hass.states.get( group.ENTITY_ID_FORMAT.format('light_and_nothing')).state) # Try to setup a group with non groupable states self.hass.states.set('cast.living_room', "Plex") self.hass.states.set('cast.bedroom', "Netflix") self.assertFalse( group.setup_group( self.hass, 'chromecasts', ['cast.living_room', 'cast.bedroom'])) # Try to setup an empty group self.assertFalse(group.setup_group(self.hass, 'nothing', []))
def __init__(self, hass, device_scanner): self.states = hass.states self.device_scanner = device_scanner self.error_scanning = TIME_SPAN_FOR_ERROR_IN_SCANNING self.lock = threading.Lock() self.path_known_devices_file = hass.get_config_path(KNOWN_DEVICES_FILE) # Dictionary to keep track of known devices and devices we track self.known_devices = {} # Did we encounter an invalid known devices file self.invalid_known_devices_file = False self._read_known_devices_file() # Wrap it in a func instead of lambda so it can be identified in # the bus by its __name__ attribute. def update_device_state(time): # pylint: disable=unused-argument """ Triggers update of the device states. """ self.update_devices() hass.track_time_change(update_device_state) hass.services.register(DOMAIN, SERVICE_DEVICE_TRACKER_RELOAD, lambda service: self._read_known_devices_file()) self.update_devices() group.setup_group(hass, GROUP_NAME_ALL_DEVICES, self.device_entity_ids, False)
def __init__(self, hass, device_scanner): self.states = hass.states self.device_scanner = device_scanner self.error_scanning = TIME_SPAN_FOR_ERROR_IN_SCANNING self.lock = threading.Lock() self.path_known_devices_file = hass.get_config_path(KNOWN_DEVICES_FILE) # Dictionary to keep track of known devices and devices we track self.known_devices = {} # Did we encounter an invalid known devices file self.invalid_known_devices_file = False self._read_known_devices_file() # Wrap it in a func instead of lambda so it can be identified in # the bus by its __name__ attribute. def update_device_state(time): # pylint: disable=unused-argument """ Triggers update of the device states. """ self.update_devices() hass.track_time_change(update_device_state) hass.services.register(DOMAIN, SERVICE_DEVICE_TRACKER_RELOAD, lambda service: self._read_known_devices_file()) self.update_devices() group.setup_group( hass, GROUP_NAME_ALL_DEVICES, self.device_entity_ids, False)
def test_setup_group_with_mixed_groupable_states(self): """ Try to setup a group with mixed groupable states """ self.hass.states.set('device_tracker.Paulus', STATE_HOME) group.setup_group(self.hass, 'person_and_light', ['light.Bowl', 'device_tracker.Paulus']) self.assertEqual( STATE_ON, self.hass.states.get( group.ENTITY_ID_FORMAT.format('person_and_light')).state)
def test_setup_group_with_mixed_groupable_states(self): """ Try to setup a group with mixed groupable states """ self.hass.states.set('device_tracker.Paulus', STATE_HOME) group.setup_group( self.hass, 'person_and_light', ['light.Bowl', 'device_tracker.Paulus']) self.assertEqual( STATE_ON, self.hass.states.get( group.ENTITY_ID_FORMAT.format('person_and_light')).state)
def reload_known_devices_service(service): """ Reload known devices file. """ group.remove_group(self.hass, GROUP_NAME_ALL_DEVICES) self._read_known_devices_file() self.update_devices(datetime.now()) if self.tracked: group.setup_group(self.hass, GROUP_NAME_ALL_DEVICES, self.device_entity_ids, False)
def reload_known_devices_service(service): """ Reload known devices file. """ group.remove_group(self.hass, GROUP_NAME_ALL_DEVICES) self._read_known_devices_file() self.update_devices(datetime.now()) if self.tracked: group.setup_group( self.hass, GROUP_NAME_ALL_DEVICES, self.device_entity_ids, False)
def setUp(self): # pylint: disable=invalid-name """ Init needed objects. """ self.hass = ha.HomeAssistant() self.hass.states.set('light.Bowl', STATE_ON) self.hass.states.set('light.Ceiling', STATE_OFF) self.hass.states.set('switch.AC', STATE_OFF) group.setup_group(self.hass, 'init_group', ['light.Bowl', 'light.Ceiling'], False) group.setup_group(self.hass, 'mixed_group', ['light.Bowl', 'switch.AC'], False) self.group_name = group.ENTITY_ID_FORMAT.format('init_group') self.mixed_group_name = group.ENTITY_ID_FORMAT.format('mixed_group')
def test_setup_and_monitor_group(self): """ Test setup_group method. """ # Test if group setup in our init mode is ok self.assertIn(self.group_name, self.hass.states.entity_ids()) group_state = self.hass.states.get(self.group_name) self.assertEqual(STATE_ON, group_state.state) self.assertTrue(group_state.attributes[group.ATTR_AUTO]) # Turn the Bowl off and see if group turns off self.hass.states.set('light.Bowl', STATE_OFF) self.hass._pool.block_till_done() group_state = self.hass.states.get(self.group_name) self.assertEqual(STATE_OFF, group_state.state) # Turn the Ceiling on and see if group turns on self.hass.states.set('light.Ceiling', STATE_ON) self.hass._pool.block_till_done() group_state = self.hass.states.get(self.group_name) self.assertEqual(STATE_ON, group_state.state) # Try to setup a group with mixed groupable states self.hass.states.set('device_tracker.Paulus', STATE_HOME) self.assertFalse(group.setup_group( self.hass, 'person_and_light', ['light.Bowl', 'device_tracker.Paulus'])) # Try to setup a group with a non existing state self.assertNotIn('non.existing', self.hass.states.entity_ids()) self.assertFalse(group.setup_group( self.hass, 'light_and_nothing', ['light.Bowl', 'non.existing'])) # Try to setup a group with non groupable states self.hass.states.set('cast.living_room', "Plex") self.hass.states.set('cast.bedroom', "Netflix") self.assertFalse( group.setup_group( self.hass, 'chromecasts', ['cast.living_room', 'cast.bedroom'])) # Try to setup an empty group self.assertFalse(group.setup_group(self.hass, 'nothing', []))
def test_setup_group_with_a_non_existing_state(self): """ Try to setup a group with a non existing state """ grp = group.setup_group( self.hass, 'light_and_nothing', ['light.Bowl', 'non.existing']) self.assertEqual(STATE_ON, grp.state)
def test_setup_and_monitor_group(self): """ Test setup_group method. """ # Test if group setup in our init mode is ok self.assertIn(self.group_name, self.hass.states.entity_ids) group_state = self.hass.states.get(self.group_name) self.assertEqual(comps.STATE_ON, group_state.state) self.assertTrue(group_state.attributes[group.ATTR_AUTO]) # Turn the Bowl off and see if group turns off self.hass.states.set('light.Bowl', comps.STATE_OFF) self.hass._pool.block_till_done() group_state = self.hass.states.get(self.group_name) self.assertEqual(comps.STATE_OFF, group_state.state) # Turn the Ceiling on and see if group turns on self.hass.states.set('light.Ceiling', comps.STATE_ON) self.hass._pool.block_till_done() group_state = self.hass.states.get(self.group_name) self.assertEqual(comps.STATE_ON, group_state.state) # Try to setup a group with mixed groupable states self.hass.states.set('device_tracker.Paulus', comps.STATE_HOME) self.assertFalse( group.setup_group(self.hass, 'person_and_light', ['light.Bowl', 'device_tracker.Paulus'])) # Try to setup a group with a non existing state self.assertNotIn('non.existing', self.hass.states.entity_ids) self.assertFalse( group.setup_group(self.hass, 'light_and_nothing', ['light.Bowl', 'non.existing'])) # Try to setup a group with non groupable states self.hass.states.set('cast.living_room', "Plex") self.hass.states.set('cast.bedroom', "Netflix") self.assertFalse( group.setup_group(self.hass, 'chromecasts', ['cast.living_room', 'cast.bedroom'])) # Try to setup an empty group self.assertFalse(group.setup_group(self.hass, 'nothing', []))
def test_setup_group_with_non_groupable_states(self): self.hass.states.set('cast.living_room', "Plex") self.hass.states.set('cast.bedroom', "Netflix") grp = group.setup_group(self.hass, 'chromecasts', ['cast.living_room', 'cast.bedroom']) self.assertEqual(STATE_UNKNOWN, grp.state)
def test_setup_group_with_non_groupable_states(self): self.hass.states.set('cast.living_room', "Plex") self.hass.states.set('cast.bedroom', "Netflix") grp = group.setup_group( self.hass, 'chromecasts', ['cast.living_room', 'cast.bedroom']) self.assertEqual(STATE_UNKNOWN, grp.state)
def setup_group(self): """ Initializes group for all tracked devices. """ entity_ids = (dev.entity_id for dev in self.devices.values() if dev.track) self.group = group.setup_group( self.hass, GROUP_NAME_ALL_DEVICES, entity_ids, False)
def setup(hass, config): """ Track states and offer events for switches. """ logger = logging.getLogger(__name__) switches = platform_devices_from_config(config, DOMAIN, hass, logger) if not switches: 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)), ent_to_switch.keys()) switch.entity_id = entity_id ent_to_switch[entity_id] = switch # pylint: disable=unused-argument @util.Throttle(MIN_TIME_BETWEEN_SCANS) def update_states(now): """ Update states of all switches. """ logger.info("Updating switch states") for switch in switches: switch.update_ha_state(hass) update_states(None) 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 switches 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): """ Exposes light control via statemachine and services. """ # 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 lights = platform_devices_from_config(config, DOMAIN, hass, _LOGGER) if not lights: 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)), 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: *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 test_setup_empty_group(self): """ Try to setup an empty group. """ grp = group.setup_group(self.hass, 'nothing', []) self.assertEqual(STATE_UNKNOWN, grp.state)
def setup(hass, config): """ Track states and offer events for switches. """ logger = logging.getLogger(__name__) switches = platform_devices_from_config(config, DOMAIN, hass, logger) if not switches: 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)), ent_to_switch.keys()) switch.entity_id = entity_id ent_to_switch[entity_id] = switch # pylint: disable=unused-argument @util.Throttle(MIN_TIME_BETWEEN_SCANS) def update_states(now): """ Update states of all switches. """ logger.info("Updating switch states") for switch in switches: switch.update_ha_state(hass) update_states(None) 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 switches 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): """ 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): """ 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] if light_type == 'hue': light_init = get_hue_lights else: _LOGGER.error("Unknown light type specified: %s", light_type) return False lights = light_init(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. """ 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): """ 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): """ Exposes light control via statemachine and services. """ # 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 lights = platform_devices_from_config(config, DOMAIN, hass, _LOGGER) if not lights: 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)), 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: *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 if ATTR_FLASH in dat: if dat[ATTR_FLASH] == FLASH_SHORT: params[ATTR_FLASH] = FLASH_SHORT elif dat[ATTR_FLASH] == FLASH_LONG: params[ATTR_FLASH] = FLASH_LONG 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): """ 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): """ Track states and offer events for WeMo switches. """ logger = logging.getLogger(__name__) try: 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