def remove_all_links(item_or_item_name): """ This function removes all Links from an Item using a ManagedItemChannelLinkProvider. Args: item_or_item_name (Item or str): the Item object or name to create the Link for Returns: Item or None: the Item that the Links were removed from or None """ try: item = validate_item(item_or_item_name) if item is None: return None channels = ItemChannelLinkRegistry.getBoundChannels(item.name) links = map(lambda channel: ItemChannelLink(item.name, channel), channels) for link in links: ManagedItemChannelLinkProvider.remove(str(link)) log.debug("Link removed: [{}]".format(link)) return item except: import traceback log.error(traceback.format_exc()) return None
def remove_all_links(item_or_item_name): """ This function removes all Links from an Item using a ManagedItemChannelLinkProvider. Args: item_or_item_name (Item or str): the Item object or name to create the Link for Returns: Item or None: the Item that the Links were removed from or None """ try: item = validate_item(item_or_item_name) if item is None: return None channels = ITEM_CHANNEL_LINK_REGISTRY.getBoundChannels(item.name) links = [ItemChannelLink(item.name, channel) for channel in channels] for link in links: MANAGED_ITEM_CHANNEL_LINK_PROVIDER.remove(str(link)) LOG.debug(u"Link removed: '{}'".format(link)) return item except: import traceback LOG.warn(traceback.format_exc()) return None
def remove_link(item_or_item_name, channel_uid_or_string): """ This function removes a Link from an Item using a ManagedItemChannelLinkProvider. Args: item_or_item_name (Item or str): the Item object or name to create the Link for channel_uid_or_string (ChannelUID or str): the ChannelUID or string representation of a ChannelUID to link the Item to Returns: Item or None: the Item that the Link was removed from or None """ try: item = validate_item(item_or_item_name) channel_uid = validate_channel_uid(channel_uid_or_string) if item is None or channel_uid is None: return None link = ItemChannelLink(item.name, channel_uid) ManagedItemChannelLinkProvider.remove(str(link)) log.debug("Link removed: [{}]".format(link)) return item except: import traceback log.error(traceback.format_exc()) return None
def add_link(item_or_item_name, channel_uid_or_string): """ This function adds a Link to an Item using a ManagedItemChannelLinkProvider. Args: item_or_item_name (Item or str): the Item object or name to create the Link for channel_uid_or_string (ChannelUID or str): the ChannelUID or string representation of a ChannelUID to link the Item to Returns: Item or None: the Item that the Link was added to or None """ try: item = validate_item(item_or_item_name) channel_uid = validate_channel_uid(channel_uid_or_string) if item is None or channel_uid is None: return None link = ItemChannelLink(item.name, channel_uid) MANAGED_ITEM_CHANNEL_LINK_PROVIDER.add(link) LOG.debug(u"Link added: '{}'".format(link)) return item except: import traceback LOG.warn(traceback.format_exc()) return None
def _gen_triggers_for_sources(config): all_items_valid = True for key in config: if isinstance(config[key], dict): all_items_valid = all_items_valid and _gen_triggers_for_sources( config[key]) elif key == META_KEY_LEVEL_SOURCE: item = validate_item(config[key]) if item is not None: if item.name not in levelTriggers: levelTriggers[item.name] = when( "Item {name} received update".format( name=item.name)) log.debug( "Added '{key}' received update trigger for '{level}'" .format(level=item.name, key=key)) else: log.error( "Failed to add '{key}' trigger for '{level}', item does not exist" .format(level=config[key], key=key)) all_items_valid = False elif key == META_KEY_MOTION_SOURCE: item = validate_item(config[key]) if item is not None: if item.name not in motionTriggers: motionTriggers[item.name] = when( "Item {name} changed".format(name=item.name)) log.debug( "Added '{key}' changed trigger for '{level}'". format(level=item.name, key=key)) else: log.error( "Failed to add '{key}' trigger for '{level}', item does not exist" .format(level=config[key], key=key)) all_items_valid = False return all_items_valid
def remove_link(item_or_item_name, channel_uid_or_string): try: item = validate_item(item_or_item_name) channel_uid = validate_channel_uid(channel_uid_or_string) if item is None or channel_uid is None: return None link = ItemChannelLink(item.name, channel_uid) ManagedItemChannelLinkProvider.remove(str(link)) log.debug("Link removed: [{}]".format(link)) return item except: import traceback log.error(traceback.format_exc()) return None
def remove_all_links(item_or_item_name): try: item = validate_item(item_or_item_name) if item is None: return None channels = ItemChannelLinkRegistry.getBoundChannels(item.name) links = map(lambda channel: ItemChannelLink(item.name, channel), channels) for link in links: ManagedItemChannelLinkProvider.remove(str(link)) log.debug("Link removed: [{}]".format(link)) return item except: import traceback log.error(traceback.format_exc()) return None
def init( rule_reinit, rule_scene_command, rule_scene_changed, rule_light_update, rule_level_source_update, rule_motion_source_changed, ): """Initialize Eos. This creates a rule with triggers for the scene item in ``configuration.eos_master_group`` and any descendants that are a ``GroupItem`` and contain a scene item to update members when they receive an update. """ def _gen_triggers_for_sources(config): all_items_valid = True for key in config: if isinstance(config[key], dict): all_items_valid = all_items_valid and _gen_triggers_for_sources( config[key]) elif key == META_KEY_LEVEL_SOURCE: item = validate_item(config[key]) if item is not None: if item.name not in levelTriggers: levelTriggers[item.name] = when( "Item {name} received update".format( name=item.name)) log.debug( "Added '{key}' received update trigger for '{level}'" .format(level=item.name, key=key)) else: log.error( "Failed to add '{key}' trigger for '{level}', item does not exist" .format(level=config[key], key=key)) all_items_valid = False elif key == META_KEY_MOTION_SOURCE: item = validate_item(config[key]) if item is not None: if item.name not in motionTriggers: motionTriggers[item.name] = when( "Item {name} changed".format(name=item.name)) log.debug( "Added '{key}' changed trigger for '{level}'". format(level=item.name, key=key)) else: log.error( "Failed to add '{key}' trigger for '{level}', item does not exist" .format(level=config[key], key=key)) all_items_valid = False return all_items_valid def _gen_triggers_for_group(group): if str(get_value(group.name, META_NAME_EOS)).lower() in META_STRING_FALSE: log.info("Found group '{group}' but it is disabled".format( name=group.name)) else: log.debug("Scanning group '{group}'".format(group=group.name)) itemScene = get_scene_item(group) if itemScene: # add scene triggers when("Item {name} received command".format( name=itemScene.name))(rule_scene_command) when("Item {name} changed".format( name=itemScene.name))(rule_scene_changed) log.debug( "Added triggers for scene item '{name}' in '{group}'". format(name=itemScene.name, group=group.name)) # gen triggers for Level and Motion sources in metadata _gen_triggers_for_sources( get_metadata(group.name, META_NAME_EOS).get("configuration", {})) # add lights triggers for light in get_light_items(group): if (str(get_value(light.name, META_NAME_EOS)).lower() in META_STRING_FALSE): log.info( "Found light '{name}' in '{group}' but it is disabled" .format(name=light.name, group=group.name)) else: _gen_triggers_for_sources( get_metadata(light.name, META_NAME_EOS).get( "configuration", {})) when("Item {name} received update".format( name=light.name))(rule_light_update) log.debug( "Added light received update trigger for '{name}' in '{group}'" .format(name=light.name, group=group.name)) # recurse into groups for group in get_group_items(group): _gen_triggers_for_group(group) else: log.warn( "Group '{group}' will be ignored because it has no scene item" .format(group=group.name)) log.info("Eos Version {} initializing...".format(eos.__version__)) config.load() if not config.master_group_name: log.error("No '{name}' specified in configuration".format( name=CONF_KEY_MASTER_GROUP)) log.error("Eos failed to initialize") return if not config.scene_item_prefix and not config.scene_item_suffix: log.error( "Must specify at least one of '{prefix}' or '{suffix}' in configuration" .format(prefix=CONF_KEY_SCENE_PREFIX, suffix=CONF_KEY_SCENE_SUFFIX)) log.error("Eos failed to initialize") return master_group_item = validate_item(config.master_group_name) if not master_group_item: log.error("Master group item '{group}' does not exist".format( group=config.master_group_name)) log.error("Eos failed to initialize") return elif not isinstance(master_group_item, itemtypesGroup): log.error("Master group item '{group}' is not a GroupItem".format( group=config.master_group_name)) log.error("Eos failed to initialize") return if not get_scene_item(master_group_item): log.error("Could not validate master scene item in '{group}'".format( group=config.master_group_name)) log.error("Eos failed to initialize") return for objRule in [ objRule for objRule in ruleRegistry.getAll() if objRule.name in [ RULE_REINIT_NAME, RULE_SCENE_COMMAND_NAME, RULE_SCENE_CHANGED_NAME, RULE_LIGHT_NAME, RULE_LEVEL_SOURCE_NAME, RULE_MOTION_SOURCE_NAME, ] ]: log.debug("Removing existing {rule} with UID '{uid}'".format( rule=objRule.name, uid=objRule.UID)) ruleRegistry.remove(objRule.UID) # try: ruleRegistry.remove(objRule.UID) # except: # log.error("Failed to delete {rule} with UID '{uid}', attempting to disable".format(rule=objRule.name, uid=objRule.UID)) # ruleEngine.setEnabled(objRule.UID, False) # if we are reinit-ing {rule}.triggers will be 'None' and cause errors if hasattr(rule_reinit, "triggers"): delattr(rule_reinit, "triggers") if hasattr(rule_scene_command, "triggers"): delattr(rule_scene_command, "triggers") if hasattr(rule_scene_changed, "triggers"): delattr(rule_scene_changed, "triggers") if hasattr(rule_light_update, "triggers"): delattr(rule_light_update, "triggers") if hasattr(rule_level_source_update, "triggers"): delattr(rule_level_source_update, "triggers") if hasattr(rule_motion_source_changed, "triggers"): delattr(rule_motion_source_changed, "triggers") # add rule to reload Eos if item exists if config.reinit_item_name: when("Item {name} received command ON".format( name=config.reinit_item_name))(rule_reinit) rule(RULE_REINIT_NAME, RULE_REINIT_DESC)(rule_reinit) if hasattr(rule_reinit, "UID"): log.debug("Created {rule} with UID '{uid}'".format( rule=RULE_REINIT_NAME, uid=rule_reinit.UID)) else: log.error("Failed to create {rule}".format(rule=RULE_REINIT_NAME)) # generate triggers for all scene, light, level source, and motion source items levelTriggers = {} motionTriggers = {} _gen_triggers_for_sources(config.global_settings) _gen_triggers_for_group(master_group_item) if hasattr(rule_light_update, "triggers"): # do not proceed if there are no lights # create scene command rule rule(RULE_SCENE_COMMAND_NAME, RULE_SCENE_COMMAND_DESC)(rule_scene_command) if hasattr(rule_scene_command, "UID"): log.debug("Created {rule} with UID '{uid}'".format( rule=RULE_SCENE_COMMAND_NAME, uid=rule_scene_command.UID)) else: log.error( "Failed to create {rule}".format(rule=RULE_SCENE_COMMAND_NAME)) log.error("Eos failed to initialize") return # create scene changed rule rule(RULE_SCENE_CHANGED_NAME, RULE_SCENE_CHANGED_DESC)(rule_scene_changed) if hasattr(rule_scene_changed, "UID"): log.debug("Created {rule} with UID '{uid}'".format( rule=RULE_SCENE_CHANGED_NAME, uid=rule_scene_changed.UID)) else: log.error( "Failed to create {rule}".format(rule=RULE_SCENE_CHANGED_NAME)) log.error("Eos failed to initialize") return # create light received update rule rule(RULE_LIGHT_NAME, RULE_LIGHT_DESC)(rule_light_update) if hasattr(rule_light_update, "UID"): log.debug("Created {rule} with UID '{uid}'".format( rule=RULE_LIGHT_NAME, uid=rule_light_update.UID)) else: log.error("Failed to create {rule}".format(rule=RULE_LIGHT_NAME)) log.error("Eos failed to initialize") return # add triggers for each Level Source item and create rule if any exist for key in levelTriggers: levelTriggers[key](rule_level_source_update) if hasattr(rule_level_source_update, "triggers"): rule(RULE_LEVEL_SOURCE_NAME, RULE_LEVEL_SOURCE_DESC)(rule_level_source_update) if hasattr(rule_level_source_update, "UID"): log.debug("Created {rule} with UID '{uid}'".format( rule=RULE_LEVEL_SOURCE_NAME, uid=rule_level_source_update.UID)) else: log.error("Failed to create {rule}".format( rule=RULE_LEVEL_SOURCE_NAME)) log.error("Eos failed to initialize") return # add triggers for each Motion Source item and create rule if any exist for key in motionTriggers: motionTriggers[key](rule_motion_source_changed) if hasattr(rule_motion_source_changed, "triggers"): rule(RULE_MOTION_SOURCE_NAME, RULE_MOTION_SOURCE_DESC)(rule_motion_source_changed) if hasattr(rule_motion_source_changed, "UID"): log.debug("Created {rule} with UID '{uid}'".format( rule=RULE_MOTION_SOURCE_NAME, uid=rule_motion_source_changed.UID)) else: log.error("Failed to create {rule}".format( rule=RULE_MOTION_SOURCE_NAME)) log.error("Eos failed to initialize") return else: log.warn("No lights found") log.info("Eos initialized") update_eos()
def update_eos(): """ Public function to update all Eos controlled lights """ update_group(validate_item(config.master_group_name))
def get_state_for_scene(item, scene): """ Returns state for scene for item. """ def constrain(value, min, max): return max if value > max else min if value < min else value # get Eos Light Type light_type = LIGHT_TYPE_MAP.get(item.type.lower(), None) if light_type is None: log.error("Couldn't get light type for '{name}'".format(name=item.name)) return str(item.state) elif config.log_trace: log.debug( "Got light type '{type}' for '{name}'".format( type=light_type, name=item.name ) ) state = None data = build_data(item) if config.log_trace: log.debug( "Got Item data for '{name}': {data}".format( name=item.name, data=data["item"] ) ) log.debug( "Got Group data for '{name}': {data}".format( name=get_item_eos_group(item).name, data=data["group"] ) ) log.debug( "Got Global data: {data}".format(name=light_type, data=data["global"]) ) # check for a scene alias setting alias_scene = get_scene_setting(item, scene, META_KEY_ALIAS_SCENE, data=data) if alias_scene is not None: log.debug( "Got alias scene '{alias}' for '{name}' for scene '{scene}', evaluating it instead".format( alias=alias_scene, name=item.name, scene=scene ) ) scene = alias_scene # check for Motion settings motion_source = validate_item( get_scene_setting(item, scene, META_KEY_MOTION_SOURCE, data=data) ) if motion_source: motion_active = get_scene_setting( item, scene, META_KEY_MOTION_ACTIVE, data=data ) motion_state = get_scene_setting(item, scene, META_KEY_MOTION_STATE, data=data) motion_scene = get_scene_setting(item, scene, META_KEY_MOTION_SCENE, data=data) if motion_active is not None and (motion_state is not None or motion_scene): log.debug( "Checking Motion trigger for '{name}' for scene '{scene}'".format( name=item.name, scene=scene ) ) if str(motion_source.state) == str(motion_active): log.debug( "Motion is active for '{name}' for scene '{scene}'".format( name=item.name, scene=scene ) ) if motion_state is not None: log.debug( "Motion trigger applying fixed state '{motion}' for '{name}' for scene '{scene}'".format( motion=motion_state, name=item.name, scene=scene ) ) state = motion_state elif motion_scene: log.debug( "Motion trigger applying scene '{motion}' for '{name}' for scene '{scene}'".format( motion=motion_scene, name=item.name, scene=scene ) ) scene = motion_scene else: log.debug( "Motion trigger is not active for '{name}' for scene '{scene}'".format( name=item.name, scene=scene ) ) elif motion_active is None: log.warn( "Motion triggers require '{key}' setting, nothing found for '{name}' for scene '{scene}'".format( key=META_KEY_MOTION_ACTIVE, name=item.name, scene=scene ) ) elif motion_state is None or not motion_scene: log.warn( "Motion triggers require '{key_state}' or {key_scene} setting, nothing found for '{name}' for scene '{scene}'".format( key_state=META_KEY_MOTION_STATE, key_scene=META_KEY_MOTION_SCENE, name=item.name, scene=scene, ) ) # get Scene Type scene_type = get_scene_type(item, scene, light_type, data=data) if scene_type is None: log.error("Couldn't get scene type for '{name}'".format(name=item.name)) return str(item.state) elif config.log_trace: log.debug( "Got scene type '{type}' for '{name}'".format( type=scene_type, name=item.name ) ) # Fixed State type if scene_type == SCENE_TYPE_FIXED and state is None: state = get_scene_setting(item, scene, META_KEY_STATE, data=data) if state is None: log.error( "Fixed State type scenes require '{key}' setting, nothing found for '{name}' for scene '{scene}'".format( key=META_KEY_STATE, name=item.name, scene=scene ) ) return str(item.state) # Threshold type elif scene_type == SCENE_TYPE_THRESHOLD and state is None: if not get_scene_setting(item, scene, META_KEY_LEVEL_SOURCE, data=data): log.error( "Threshold type scenes require '{key}' setting, nothing found for '{name}' for scene '{scene}'".format( key=META_KEY_LEVEL_SOURCE, name=item.name, scene=scene ) ) return str(item.state) level_value = resolve_type( validate_item( get_scene_setting(item, scene, META_KEY_LEVEL_SOURCE, data=data) ).state ) if isinstance(level_value, str) and level_value.lower() in ["null", "undef"]: log.warn( "Level item '{key}' for scene '{scene}' for item '{name}' has no value".format( key=get_scene_setting( item, scene, META_KEY_LEVEL_SOURCE, data=data ), scene=scene, name=item.name, ) ) return str(item.state) level_threshold = get_scene_setting( item, scene, META_KEY_LEVEL_THRESHOLD, data=data ) if level_threshold is None: log.error( "Threshold type scenes require '{key}' setting, nothing found for '{name}' for scene '{scene}'".format( key=META_KEY_LEVEL_THRESHOLD, name=item.name, scene=scene ) ) return str(item.state) state_above = get_scene_setting(item, scene, META_KEY_STATE_ABOVE, data=data) if state_above is None: log.error( "Threshold type scenes require '{key}' setting, nothing found for '{name}' for scene '{scene}'".format( key=META_KEY_STATE_ABOVE, name=item.name, scene=scene ) ) return str(item.state) state_below = get_scene_setting(item, scene, META_KEY_STATE_BELOW, data=data) if state_below is None: log.error( "Threshold type scenes require '{key}' setting, nothing found for '{name}' for scene '{scene}'".format( key=META_KEY_STATE_BELOW, name=item.name, scene=scene ) ) return str(item.state) state = state_above if level_value > level_threshold else state_below # Scaling type elif ( scene_type == SCENE_TYPE_SCALED and light_type in [LIGHT_TYPE_DIMMER, LIGHT_TYPE_COLOR] and state is None ): if not get_scene_setting(item, scene, META_KEY_LEVEL_SOURCE, data=data): log.error( "Scaling type scenes require '{key}' setting, nothing found for '{name}' for scene '{scene}'".format( key=META_KEY_LEVEL_SOURCE, name=item.name, scene=scene ) ) return str(item.state) level_value = resolve_type( validate_item( get_scene_setting(item, scene, META_KEY_LEVEL_SOURCE, data=data) ).state ) if isinstance(level_value, str) and level_value.lower() in ["null", "undef"]: log.warn( "Level item '{key}' for scene '{scene}' for item '{name}' has no value".format( key=get_scene_setting( item, scene, META_KEY_LEVEL_SOURCE, data=data ), scene=scene, name=item.name, ) ) return str(item.state) level_value = float(level_value) level_high = get_scene_setting(item, scene, META_KEY_LEVEL_HIGH, data=data) if level_high is None: log.error( "Scaling type scenes require '{key}' setting, nothing found for '{name}' for scene '{scene}'".format( key=META_KEY_LEVEL_HIGH, name=item.name, scene=scene ) ) return str(item.state) level_high = float(level_high) level_low = get_scene_setting(item, scene, META_KEY_LEVEL_LOW, data=data) if level_low is None: level_low = 0.0 log.debug( "No value for key '{key}' for scene '{scene}' for item '{name}', using default '{value}'".format( key=META_KEY_LEVEL_LOW, scene=scene, name=item.name, value=level_low ) ) level_low = float(level_low) state_high = get_scene_setting(item, scene, META_KEY_STATE_HIGH, data=data) if state_high is None: log.error( "Scaling type scenes require '{key}' setting, nothing found for '{name}' for scene '{scene}'".format( key=META_KEY_STATE_HIGH, name=item.name, scene=scene ) ) return str(item.state) state_low = get_scene_setting(item, scene, META_KEY_STATE_LOW, data=data) if state_low is None: log.error( "Scaling type scenes require '{key}' setting, nothing found for '{name}' for scene '{scene}'".format( key=META_KEY_STATE_LOW, name=item.name, scene=scene ) ) return str(item.state) state_above = ( get_scene_setting(item, scene, META_KEY_STATE_ABOVE, data=data) or state_high ) state_below = ( get_scene_setting(item, scene, META_KEY_STATE_BELOW, data=data) or state_low ) if level_value > level_high: state = state_above elif level_value < level_low: state = state_below else: scaling_factor = (level_value - level_low) / (level_high - level_low) def scale(low, high): return int(round(low + (high - low) * scaling_factor)) if isinstance(state_high, (int, float)): # Dimmer value state = scale(state_low, state_high) elif isinstance(state_high, list): # HSV list state = [scale(float(state_low[0]), float(state_high[0]))] state.append(scale(float(state_low[1]), float(state_high[1]))) state.append(scale(float(state_low[2]), float(state_high[2]))) elif state is None: log.error( "Invalid scene configuration for '{name}' scene '{scene}'".format( name=item.name, scene=scene ) ) return str(item.state) if ( light_type == LIGHT_TYPE_SWITCH and isinstance(state, (str)) and state.upper() in ["ON", "OFF"] ): state = state.upper() elif light_type == LIGHT_TYPE_DIMMER and isinstance(state, (int, float)): state = str(constrain(int(round(state)), 0, 1000000)) elif light_type == LIGHT_TYPE_COLOR and isinstance(state, (int, float, list)): if isinstance(state, (int, float)): oldState = str( "0,0,0" if isinstance(item.state, typesUnDef) else item.state ).split(",") state = ",".join( [ str(oldState[0]), str(oldState[1]), str(constrain(int(round(state)), 0, 100)), ] ) else: if state[0] > 359: state[0] -= 359 elif state[0] < 0: state[0] += 359 state[1] = constrain(state[1], 0, 100) state[2] = constrain(state[2], 0, 100) if state[2] == 0: # needed because some devices will return '0,0,0' if V=0 state = "0,0,0" else: state = ",".join([str(i) for i in state]) else: log.warn( "New state '{state}' for '{name}' scene '{scene}' is not valid for item type '{type}'".format( state=state, name=item.name, scene=scene, type=item.type ) ) return str(item.state) log.debug( "Determined {type} state '{state}' for '{name}' scene '{scene}'".format( type=scene_type, state=state, name=item.name, scene=scene ) ) return state