# Scene activations

{% hint style="warning" %}
Be aware: In order to use scene activations, basic knowledge of javascript is required.
{% endhint %}

{% hint style="info" %}
This page was updated July 2021 reflecting the change from using *Logic* to [Better Logic](https://homey.app/en-us/app/net.i-dev.betterlogic/Better-Logic/). Not all pages reflect this update, and there might be examples using logic variables.
{% endhint %}

I have one scene activation logic for each scene variable. A scene variable is a text value representing the different [available scenes](/lighting/scenes.md) for a room or area.

The scene activation is responsible for translating a set of input variables (aggregated state) into one of the available scenes for a room.

### A very basic example: bedroom

Bedroom has a scene variable `scene-bedroom`, a text string that always contains one of the [defined scenes](/lighting/scenes.md) for that room. In a simple setup a room can have `off`, `normal`, `dimmed`.

The logic to decide the scene, may be `bright`, `presence-bedroom` and `timeperiod`. Both bright and presence-bedroom is boolean, indicating whether there is [bright daylight](/aggregated-state/dark-or-bright.md) and whether there is [someone present](/aggregated-state/room-and-floor-presence-v2.md) in the bedroom (motion and door sensors). The [timeperiod variable](/aggregated-state/time-table.md) is a string that can contain one of a few values indicating which part of the day.

A scene activation consists of a homeyscript and a flow that triggers the homeyscript. I use homeyscript because it allows more advanced logic than what is available in the flow editor (which is far too limited).

The flow *Scene activation bedroom*, triggers when one of aggregated state variables that are involved changes.

![Example of a scene activation flow](/files/-MexeIdnzxMVbmEBUKtD)

### The scene activation flow

Use the better logic "On of these variables changed" card, as seen in the illustration above.

The card takes a comma-separated list of better logic variables – the aggregated state variables that is needed for this flow.

### The scene activation **homeyscript**

The scene activation homeyscript will run when one of the aggregated state variables changes. The script can be split into three sections:

* Fetching the aggregated state variables
* Logic calculating the output scene depending on the aggregated state.
* Setting the output scene variable

#### Fetching the aggregated state variables

The first section with helper functions is the same for all scene activation homeyscript, and can be copied as part of a boiler plate template.

```javascript
// Helper functions to be able to fetch variables from the better logic app
let betterlogic = await Homey.apps.getApp({id: "net.i-dev.betterlogic"})
let getVar = async (name) => {
    let x = await betterlogic.apiGet(name)
    if (!x)  throw new Error("Could not find variable [" + name + "]")
    return x.value
}

// Fetch the aggregated state variables that is needed to do the logic.
let timeperiod = await getVar('timeperiod')
let presence = await getVar('presence-bedroom')
let bright = await getVar('bright')
```

In the example above we fetch three better logic variables into local javascript variables available to perform the logic.

{% hint style="info" %}
Notice that there is a slight delay in fetching each of the logic variables of a few milliseconds.
{% endhint %}

Output the variables to the console for debugging is useful:

```javascript
console.log("Timeperiod " + timeperiod)
console.log("Bedroom presence " + presence)
console.log("Bright daylight " + bright)
```

#### Scene logic

We use the aggregated state variables fetched above to decide which scene to activate for this room. It must always end up with one of the [defined scenes](/lighting/scenes.md) for that room.

```javascript
// timeperiod values: morning, day, evening, late, night
let scene = "off"
if (presence) {
  if (bright && timperiod !== 'morning')  {
    scene = "normal"
  } else if (timeperiod !== "night") {
    scene = "dimmed"
  } else {
    scene = "off"
  }
}
```

#### Setting the scene variable

Notice the trailing slash needed to be added to the name of the better logic scene variable.

```javascript
console.log("Setting scene to [" + scene + "]")
betterlogic.apiPut("scene-bedroom/" + scene)
return true
```

#### Complete example

The complete homeyscript as described above:

{% code title="sa-bedroom.js" %}

```javascript
// Helper functions to be able to fetch variables from the better logic app
let betterlogic = await Homey.apps.getApp({id: "net.i-dev.betterlogic"})
let getVar = async (name) => {
    let x = await betterlogic.apiGet(name)
    if (!x)  throw new Error("Could not find variable [" + name + "]")
    return x.value
}

// Fetch the aggregated state variables that is needed to do the logic.
let timeperiod = await getVar('timeperiod')
let presence = await getVar('presence-bedroom')
let bright = await getVar('bright')

// timeperiod values: morning, day, evening, late, night
let scene = "off"
if (presence) {
  if (bright && timperiod !== 'morning')  {
    scene = "normal"
  } else if (timeperiod !== "night") {
    scene = "dimmed"
  } else {
    scene = "off"
  }
}

console.log("Setting scene to [" + scene + "]")
betterlogic.apiPut("scene-bedroom/" + scene)
return true
```

{% endcode %}

### Testing the scene activation

Use the better logic app settings and manually edit each of the aggregate states, and verify that the scene variable are updated correctly, and also verify how the lighting, music or temperature changes accordingly to the scene.

One common mistake is the have typos in the "On of these variables changed"-card.

### Alternative way of fetching variables

If you run into issues with delays adding up when fetching a large number of variables, you may fetch data in parallell like this:

```javascript
let [
    home,
    timeperiod,
    presence1,
    presence0,
    dark
] = await Promise.all([getVar('home'), getVar('timeperiod'), 
    getVar('presence-1etg'), getVar('presence-0etg'), 
    getVar('dark')]
)
```

### More examples

#### Living room lights

Here is the homescript for kjellerstua:

{% code title="sa-0etg.js" %}

```javascript
let sceneid = "off"
if (presence) {
    if (tv)  {
        sceneid = "tv"
    } else if (timeperiod === 'day') {
        sceneid = "normal"
    } else {
        sceneid = "dim"
    }
    
} 
```

{% endcode %}

#### Outdoor lights

Example for outdoor lighting.

{% code title="sa-outdoor.js" %}

```javascript
let sceneid = "off"
if (dark) {
    if (timeperiod === 'morning' && presence) {
        sceneid = 'normal'
    } else if (timeperiod === 'day' && presence) {
        sceneid = 'boost'
    } else if (timeperiod === 'day' && home) {
        sceneid = 'normal'
    } else if (timeperiod === 'evening' && presence) {
        sceneid = 'boost'
    } else if (timeperiod === 'evening') {
        sceneid = 'normal'
    } else if (timeperiod === 'late' && presence) {
        sceneid = 'dim'
    }
}
```

{% endcode %}

#### 2nd floor lights

{% code title="sa-floor2.js" %}

```javascript
let sceneid = "off"

if (inputScene === "off") {
    sceneid = "night"
} else if (timeperiod === 'night' && presence) {
    sceneid = "nightp"
} else if (timeperiod === 'night') {
    sceneid = "night"
} else if (!home) {
    sceneid = "away"
} else if (!presence) {
    sceneid = "night"
} else if (inputScene === "flood") {
    sceneid = "flood"
} else if (inputScene === "middag") {
    sceneid = "middag"
} else if (bright) {
    sceneid = "bright"
} else if (timeperiod === "morning") {
    sceneid = (inputScene === "dim") ? "dim" : "soft"
} else if (timeperiod === "evening") {
    sceneid = (inputScene === "dim") ? "dim" : "soft"
} else if (timeperiod === "late") {
    sceneid = "dim"
} else {
    sceneid = (inputScene === "dim") ? "soft" : "normal"
}

```

{% endcode %}

Here is another older example for the second floor:

{% code title="sa-floor2-old.js" %}

```javascript
let sceneid = "off"
if (timeperiod === 'night' && presence) {
    sceneid = "nightp"
} else if (timeperiod === 'night') {
    sceneid = "night"
} else if (!home) {
    sceneid = "away"
} else if (!presence) {
    sceneid = "night"
} else if (bright) {
    sceneid = "bright"
} else if (timeperiod === "morning") {
    sceneid = "soft"
} else if (timeperiod === "evening") {
    sceneid = "soft"
} else if (timeperiod === "late") {
    sceneid = "dim"
} else {
    sceneid = "normal"
}
```

{% endcode %}

#### Children's room lights

Example from a childrens room. `inputScene` is a variable controlled by a person using a set of buttons for choosing modus.

```javascript
let sceneid = null
if (inputScene === 'flood') {
    sceneid = "flood"
} else if (!presence) {
    sceneid = "off"
} else if (scene === 'night') {
    sceneid = 'night'
} else if (scene === 'off') {
    sceneid = 'off'
} else {

    if (timeperiod === 'day') {
        sceneid = "normal"
    } else if (timeperiod === 'morning') {
//        sceneid = "morning"
    } else if (timeperiod === 'evening') {
        if (leggetid) {
            sceneid = "off"
        } else {
            sceneid = "leselys"
        }
    } else if (timeperiod === 'late') {
        sceneid = "night"
    } else if (timeperiod === 'night') {
        sceneid = "night"
    }
}
if (inputScene !== null) {
    console.log("Set new scene " + sceneid)
    betterlogic.apiPut("scene-1linus/" + sceneid)
} else {
    console.log("No change. Keep current scene")
}

```

#### Music 2nd floor lights

Here is an example controlling a music scene, including a variable for music volume:

```javascript
let d = new Date()
let m = d.getMonth()+1
let day = d.getDate()
let christmas = (m === 12) && day > 10

// console.log("Is christmas", christmas)
// Timeperiods: morning, day, evening, late, night

let music = "off"
let volume = 12

if (presenceAndreas && !presenceVigdis) {
    volume = 16
}

if (!presence) {
    music = "off"
} else if (!home) {
    music = "off"
} else if (timeperiod === 'night') {
    music = "off"
} else if (timeperiod === 'day' && !presenceAndreas && !presenceVigdis) {
    music = "nrksuper"
} else if (timeperiod === 'day' && presenceAndreas && !presenceVigdis) {
    music = "p4"
} else {

} 

await setTagValue("music-2etg", {type: 'string', title:'Music 2etg'}, music)
await setTagValue("music-2etg-volume", {type: 'number', title:'Music 2etg volume'}, volume/100)
```

#### Music bathroom

Here is another logic for music, controlling music on the bathroom:

```javascript
let d = new Date()
let m = d.getMonth()+1
let day = d.getDate()
let christmas = (m === 12) && day > 10

// console.log("Is christmas", christmas)
// Timeperiods: morning, day, evening, late, night

let music = "off"
let volume = 0.08

if (presenceAndreasBad) {
    volume = 0.10
}
console.log("Christmas " + christmas)

if (!presence) {
    music = "off"
} else if (timeperiod === 'night') {
    music = "off"
} else {

    music = "chill"

    if (inputMusic !== 'auto') {
        music = inputMusic

        if(inputMusic === 'rock' && timeperiod !== 'late' && timeperiod !== 'evening') {
            volume += 0.05
        }

    } else if ( timeperiod === 'morning') {
        music = "spa"
    } else if (christmas) {
        music = "jul"
    } else if (timeperiod === 'day') {
        music = "pop"
    }

}
if (timeperiod !== 'late') {
  volume -= 0.03
} 
if (timeperiod !== 'evening') {
  volume -= 0.01
}

```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://homey.solweb.no/lighting/scene-activations.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
