Esempio n. 1
0
def eos_rule_level_source_update(event):
    """Eos Level Source received update Rule"""
    log.debug("{rule} triggered by '{name}' with state '{state}'".format(
        rule=RULE_LEVEL_SOURCE_NAME,
        name=event.itemName,
        state=event.itemState))
    update_eos()
Esempio n. 2
0
def eos_rule_motion_source_changed(event):
    """Eos Motion Source changed Rule"""
    log.debug("{rule} triggered by '{name}' with state '{state}'".format(
        rule=RULE_MOTION_SOURCE_NAME,
        name=event.itemName,
        state=event.itemState))
    update_eos()
Esempio n. 3
0
def _get_conf_value(name, valid_types=None, default=None):
    """Gets ``name`` from configuration.

    Returns ``default`` if not present or not one of types in ``valid_types``
    """
    # importing here so we can reload each time and catch any updates the user may have made
    try:
        import configuration

        reload(configuration)
    except:
        return default

    if hasattr(configuration, name):
        value = getattr(configuration, name)
        if valid_types is None or isinstance(value, valid_types):
            log.debug("Got '{name}': '{value}' from configuration".format(
                name=name, value=value))
            return value
        else:
            log.error(
                "Configuration value for '{name}' is type '{type}', must be one of {valid_types}"
                .format(name=name, type=type(value), valid_types=valid_types))
            return default
    else:
        log.debug(
            "No value for '{name}' specified in configuration, using default '{value}'"
            .format(name=name, value=default))
        return default
Esempio n. 4
0
def update_group(target, only_if_scene_parent=False, scene=None, parent_scene=None):
    if str(get_value(target.name, META_NAME_EOS)).lower() in META_STRING_FALSE:
        if config.log_trace:
            log.debug(
                "Skipping update for group '{name}' as it is disabled".format(
                    name=target.name
                )
            )
        return
    elif config.log_trace:
        log.debug("Processing update for group '{name}'".format(name=target.name))

    scene = scene or str(get_scene_item(target).state).lower()
    if scene == SCENE_PARENT:
        scene = parent_scene or scene
    elif only_if_scene_parent:
        return

    for light_item in get_light_items(target):
        try:
            update_light(light_item, scene=scene)
        except:
            continue

    for group_item in get_group_items(target):
        update_group(group_item, only_if_scene_parent, parent_scene=scene)
Esempio n. 5
0
def eos_rule_scene_command(event):
    """Eos Scene received command Rule"""
    log.debug("{rule} triggered by '{name}' with scene '{scene}'".format(
        rule=RULE_SCENE_COMMAND_NAME,
        name=event.itemName,
        scene=event.itemCommand))
    update_scene(itemRegistry.get(event.itemName),
                 scene=str(event.itemCommand).lower())
Esempio n. 6
0
def eos_rule_scene_changed(event):
    """Eos Scene changed Rule"""
    log.debug("{rule} triggered by '{name}' with scene '{scene}'".format(
        rule=RULE_SCENE_CHANGED_NAME,
        name=event.itemName,
        scene=event.itemState))
    update_group(
        get_item_eos_group(itemRegistry.get(event.itemName)),
        scene=str(event.itemState).lower(),
    )
Esempio n. 7
0
def update_scene(item, scene=None):
    """
    Updates all lights and subgroups, and propagates scene change to children
    with ``follow_parent`` set to ``True``.
    """
    scene = scene or str(item.state).lower()
    log.info(
        "Changing '{group}' scene to '{scene}'".format(
            group=get_item_eos_group(item).name, scene=item.state
        )
    )

    for light_item in get_light_items(get_item_eos_group(item)):
        try:
            update_light(light_item, scene=scene)
        except:
            continue

    for group_item in get_group_items(get_item_eos_group(item)):
        if (
            str(get_value(group_item.name, META_NAME_EOS)).lower()
            not in META_STRING_FALSE
        ):
            # set children to "parent" scene unless following is turned off
            if resolve_type(
                get_metadata(group_item.name, META_NAME_EOS)
                .get("configuration", {})
                .get(META_KEY_FOLLOW_PARENT, True)
            ):
                log.debug(
                    "Commanding '{group}' scene to 'parent'".format(
                        group=group_item.name
                    )
                )
                sendCommand(get_scene_item(group_item).name, SCENE_PARENT)
            else:
                update_group(group_item, True)
Esempio n. 8
0
 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
Esempio n. 9
0
 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))
Esempio n. 10
0
def eos_rule_light_update(event):
    """Eos Light received update Rule"""
    log.debug("{rule} triggered by '{name}' with state '{state}'".format(
        rule=RULE_LIGHT_NAME, name=event.itemName, state=event.itemState))
    update_light(itemRegistry.get(event.itemName))
Esempio n. 11
0
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()
Esempio n. 12
0
def update_light(item, scene=None):
    """
    Sends commands to lights based on scene.
    """
    if str(get_value(item.name, META_NAME_EOS)).lower() in META_STRING_FALSE:
        if config.log_trace:
            log.debug(
                "Skipping update for light '{name}' as it is disabled".format(
                    name=item.name
                )
            )
        return
    elif config.log_trace:
        log.debug("Processing update for light '{name}'".format(name=item.name))

    scene = scene if scene and scene != SCENE_PARENT else get_scene_for_item(item)
    if config.log_trace:
        log.debug(
            "Got scene '{scene}' for item '{name}'".format(scene=scene, name=item.name)
        )

    if scene != SCENE_MANUAL:
        newState = get_state_for_scene(item, scene)
        if sendCommandCheckFirst(item.name, newState, floatPrecision=3):
            log.debug(
                "Light '{name}' scene is '{scene}', sent command '{command}'".format(
                    name=item.name, scene=scene, command=newState
                )
            )
        else:
            log.debug(
                "Light '{name}' scene is '{scene}', state is already '{command}'".format(
                    name=item.name, scene=scene, command=newState
                )
            )
    else:
        log.debug(
            "Light '{name}' scene is '{scene}', no action taken".format(
                name=item.name, scene=scene
            )
        )
Esempio n. 13
0
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