OH3 | Tips, ideas and examples requested for a "Design Pattern" for light scenes

Best OH community

I want to make light scenes in openhab 3. I never got around to this in Openhab 2.x.

I have read the topic below. Has anyone already applied this within Openhab 3?

Link to “magical-light-scenes” topic

I understand group nesting. Is this the way to go in openhab or are there better ways that make more use of the new features in openhab 3. for example reusing the groups in semantic model and or using the GA item tags in the Main UI?

Tips, ideas and examples of a “Design Pattern” for light scenes in Openhab 3 are welcome

For a very similar effect, I use metadata. I have an item for each “scene” that includes metadata that looks like this:

value: ""
config:
  Switch_PatioLight_OnOff: OFF
  Switch_FrontLights_OnOff: OFF
  Switch_BalconyLight_OnOff: OFF
  Switch_GarageLight_OnOff: OFF
  Switch_KitchenLight_Brightness: 10

Then I have a series of rules that trigger the scenes either automatically or based on some action but each rule is a basic script that simply calls a js function run-controller:

load("/openhab/conf/automation/lib/javascript/personal/automation control.js");
run_controller("Auto_EndOfNight_Control")

run_controller takes advantage of the metadata helper libraries to get the item’s metadata and loops through the configuration array sending the stored command to the listed item.

/*
This library provides functions for the automation control system.
*/

var logger = Java.type('org.slf4j.LoggerFactory').getLogger('org.openhab.rule.controllerMembership');
load("/openhab/conf/automation/lib/javascript/core/metadata.js");

'use strict';

(function(context) {
  'use strict';
  context.run_controller=function (controllerID) {
      var metaList=get_metadata(controllerID, "ActiveItems");
      if (metaList) {
        var configList=metaList['configuration'];
        for (var id in configList) {
          context.events.sendCommand(id,configList[id]);
        };
      } else {
        logger.info(['No items currently controlled by ',controllerID].join(''));
      };
  };
})(this);

The added feature here is that I don’t manually code the scene metadata. Each scene item is a string item with four possible states: ON, OFF, ADD, REMOVE. If ON, then this scene can be controlled by the automatic rules that include. If OFF, then no automatic control will occur. While it is set to ADD, and light item that changes state will have the new state recorded in the metadata and while set to REMOVE any item that changes state will be removed from the metadata list.

Here’s the custom widget that lets me set and activate these:
image

3 Likes

That’s another smart/clever use of the new features in Openhab 3.

Thanks for the example.

I still have to study him carefully to find out if that is a technique that I can apply in my situation.

Thanks for sharing.

I have several switches with 6 (physical ) buttons and I want to attach scenes such as All off, Reading, TV, relax, etc. and have the possibility to address the scenes via my voice assistant.

Thanks @JustinG , this is a great way to do it!

When you go to https://demo.openhab.org/ there are also some scene buttons. At first I thought it’s an official oh3 feature to have scenes. But it doesn’t seem like it. Wonder how those are implemented.

You can log into the demo site as an administrator (user and password demo) and take a look at exactly how it was setup.

Yes you can, however it’s just an item that changes state. There is no rule to go with it

The more I look and think about light senes and the functionality I would like to have, the more complex it becomes.

  • I have lighten with on/off functionality
  • I have lighten with dim level settings
  • I have lighten with color temperature settings
  • I have lighten with color settings

and in one room I have a mix of different lamps with different settings

As an example:

  • when I am watching TV I only want 3 lamps on at 40% brightness
  • When I am relaxing with friends I want all lights on at 50%, same color temperature and my led strip is red at 30%.
  • When I am reading or sitting at the dinner table, other settings and so on.

And you want to do as little duplicate work as possible.
If I want to add a lamp, I don’t want to have to check 6 or more rules.

I am going to search this forum again and collect all the good things and how I can combine them into a good solution.

Again, all tips & tricks and ideas are welcome and appreciated

There are not too many ways to store and retrieve arbitrary data within the framework of OH. If you want to preserve specific, complex states (that is, not just have items turn on/off) then you really only have 3 basic options:

  1. Hard code the states into different rules/scripts.
    Pro: probably clearest and easiest to keep track of each scene’s settings as they are spelled out in one rule or one script.
    Con: as you state, this means that you will have to modify every rule (however, you can also keep it to one long rule if you want to and have that rule respond to different states in one general scene item).

  2. Items with a consistent naming convention. For each light item you want added to a scene you also have an associated string item named [base item name]_[scene name] (e.g. if you have a light with an item name of livingroom_lamp then you have string items named livingroom_lamp_watchtv and livingroom_lamp_hangout). You set the state of livingroom_lamp_watchtv to “OFF” and livingroom_lamp_hangout to “50”. Then when your watchtv scene is triggered, a rule runs that checks all lights (either via a group or semantic tags) and if there’s an item with the light’s name +"_watchtv" then it sends the light item the command that is stored in the associated watchtv item state (your livingroom_lamp would get the command “OFF” for the watchtv scene).
    Pros: with the filter bar at the top of the items page you can see which scenes an item is included in and even what those scene values would be by filtering on the item name or by filtering on the scene suffix you could see the list of all items included in a scene. Plus adjusting the values of each of the item’s scene settings can be done via the UI and a custom widget as those settings are also just items.
    Con: Lots of extra items to create and maintain depending on how many scenes you want and how many basic items you want included in each scene.

  3. Metadata: either a system similar to the one I posted above with the scene specific states stored in an item for each scene (or a central scene item with different metadata namespaces), or similar to #2 above, add a scene state metadata namespace to each item you want to see controlled and have scene rules iterate through lights to find those items that have the appropriate metadata.
    Pro: Once the general mechanism is in place, this simple setup each time a new item is added just as with option #1 as you just add the metadata to that item or the appropriate scene items.
    Con: Most rule scripting required to read and manipulate metadata.

There would be other ways that include the storage of your scene settings in outside configuration files that then get read into OH, but I don’t think any such option would provide any advantage over these native OH options and would be far more work.

1 Like

Hi Justin,
thx for al the tips.
I’m going to try a mix of what you described.
I have to read you latest post again tot tomorrow

Have been playing with Hue scenes recently and was thinking it would be really nice to have some native features in OH3.x for managing the state of multiple items. Btw - Homeassistant has some concepts for scenes but nothing too fancy yet.

With the rules building UI in MainUI you pretty much have a native and simple scene setup and config as I described above. In the UI, a rule can have as many then results as you would like and each one can be a item setting in a “scene”. For example:

This pretty much what most people want or need in a basic scene control system. The only real downside is that if you want your scene setup this basic then you have one of these rules for each option for each scene, and that could get cumbersome depending on your system. Therefore, there are also more advanced ways of streamlining scenes depending on your comfort level with (in increasing complexity IMO) script rules, helper libraries, and metadata.

It doesn’t really make sense for the devs to put time into a completely separate “scene” section of the UI since that would just be duplicating what you can do with rules.

1 Like

I really like this rule usage for scenes - haven’t thought of using it that way and with this method, you still can use the UI for setting up items (e.g. colors, etc or stuff I don’t want to edit manually).

On the UI side - this is still way more clicks compared to what I would do in the Hue app for creating a scene - one UI addition that would help is a way to capture items + settings within a group in one click into a rule - that would start to make things more streamlined.

1 Like

Hi Justin,

This looks like the automation I was looking for a long time! Unfortunately I have some difficulty understanding how you actually implement this script. Would you be so kind to provide some assistance? And/or share the YAML of your widget!

Many thanks!

Sure, here’s a slightly more detailed run-down. Do note, however, that if I were to redo this system, certain parts, in particular the UI interaction, would change fairly dramatically. I haven’t gotten around to updating it though so here’s the original implementation.

Set Up

Items

The way I have it setup there is one proxy String item for each “scene”. The state of this item will indicate the current status of the scene: ON (enabled), OFF (disabled), ADD (waiting to include new item states into the scene definition), and REMOVE (waiting to remove items and their states from the scene definition). These last two are really extra. The whole thing functions with just the ON and OFF states (and could therefore be a Switch item just as well) but then you would have to manually enter all the metadata information that controls the individual items.

  • These items must have a non-semantic tag that identifies them as the group of scene controlling proxy items (In my case I used Automation Controller).
  • I also have these items in their own group for the widget although it could just as easily work the same tag as mentioned above.
  • These items use the state options in the State Description metadata to create more human readable versions of the all caps states and to allow the widget to automatically populate the control buttons for each one

All items that you might want included in scene will also have to be in one or more groups. This is for triggering the rule that will add and remove items from scenes. For example I have a gLights group that every light in the house is a member of and a gActors group for other things such as ceiling fans and speakers and so I use these groups for the rule

Helper libraries

Because this relies heavily on metadata you must be able to access the metadata of an item from your rules scripts. This is possible without the helper libraries (there are examples here on the forum) but it is much more arduous so I recommend that you install the help libraries for whichever scripting language you prefer.

Widget

The widget works by cycling through all the items that match the tag you have given your scene controllers, rendering one list item with buttons that correspond to the state description options and a second accordion list that shows the details of the items included and their states.

Widget YAML
uid: widget_automation_controllers
tags:
  - settings
  - automation control
component: oh-list-card
config:
  accordionList: true
  title: Automation controllers
slots:
  default:
    - component: oh-repeater
      config:
        accordionList: true
        sourceType: itemsInGroup
        groupItem: gAutomationControllers
        fetchMetadata: ActiveItems
        fragment: true
        for: controller
      slots:
        default:
          - component: oh-list-item
            config:
              title: =loop.controller.label.substring(24)
              badge: =items[loop.controller.name].displayState
              badgeColor: "=(items[loop.controller.name].displayState == 'On') ? 'green' : ((items[loop.controller.name].displayState == 'Off') ? 'red' : 'yellow')"
            slots:
              accordion:
                - component: oh-list
                  slots:
                    default:
                      - component: f7-segmented
                        config:
                          raised: true
                        slots:
                          default:
                            - component: oh-repeater
                              config:
                                sourceType: itemStateOptions
                                itemOptions: =loop.controller.name
                                fragment: true
                                for: option
                              slots:
                                default:
                                  - component: oh-button
                                    config:
                                      text: =loop.option.label
                                      raised: true
                                      action: command
                                      actionItem: =loop.controller.name
                                      actionCommand: =loop.option.value
                                      active: =(items[loop.controller.name].displayState==loop.option.label)
                      - component: oh-list
                        config:
                          accordionList: true
                        slots:
                          default:
                            - component: oh-list-item
                              config:
                                title: Devices
                                badge: "=(loop.controller.metadata.ActiveItems.config) ? JSON.stringify(loop.controller.metadata.ActiveItems.config).slice(1,-1).split(',').length : '0'"
                              slots:
                                accordion:
                                  - component: oh-repeater
                                    config:
                                      for: activeItem
                                      in: =JSON.stringify(loop.controller.metadata.ActiveItems.config).slice(1,-1).split(',')
                                      fragment: true
                                    slots:
                                      default:
                                        - component: oh-list-item
                                          config:
                                            title: =loop.activeItem.split(':')[0].slice(1,-1)
                                            badge: =loop.activeItem.split(':')[1].slice(1,-1)
                                            class:
                                              - margin-left

Logic

There are really only 2 different pieces of rules logic involved: 1) reading the metadata and replicating the stored states, and 2) adding and removing items and associated states.

Running scenes

You need rules that can call the scene running library function (posted above) whenever you want that scene activated, whether that’s at some automated time or as the result of a button press or a TV setting. These are basic rules that are really just a trigger and the script up above that loads the library and makes the call to run-controller with the appropriate scene controlling item name as the function parameter. Here’s an example of the full rule YAML (a rule from the mainUI rules builder) that runs at 11 pm every night but only if the controller item is set to ON:

Basic scene controller rule
triggers:
  - id: "1"
    configuration:
      time: 23:00
    type: timer.TimeOfDayTrigger
conditions:
  - inputs: {}
    id: "3"
    configuration:
      itemName: Auto_EndOfNight_Control
      state: ON
      operator: =
    type: core.ItemStateCondition
actions:
  - inputs: {}
    id: "2"
    configuration:
      type: application/javascript
      script: >-
        load("/openhab/conf/automation/lib/javascript/personal/automation control.js");

        run_controller("Auto_EndOfNight_Control")
    type: script.ScriptAction

Scene membership

The scene membership is also handled by a single rule. The information about each scene is stored in a custom metadata namespace (in this case ActiveItems). This rule runs any time one of the items in any of the basic groups changes. When an Item changes, it checks through each of the scene controlling items to see if any are in the ADD or REMOVE state. If a controller is in the add state then the ActiveItems metadata gets a new key that is the name of the item which just changed and a value that is the new state it just changed to. If the controller is currently set to REMOVE than the metadata key matching that item name is deleted from the ActiveItems namespace. So to populate your scenes you just set the scene controlling item to ADD (using the widget) and then change all of the items you want controlled to the states you want them to be in for the scene. To remove an item from a scene, you just set the controller to REMOVE and do anything that changes the state of the item you want removed. When you’re done you just reset the controller item to ON and your scene is ready to go whenever you have a rule that sends that controller item to the library function.

Scene membership script
var logger = Java.type('org.slf4j.LoggerFactory').getLogger('org.openhab.rule.controllerMembership');
load("/openhab/conf/automation/lib/javascript/core/metadata.js");

/*Loop through each controller and check state
    if state is ADD then inlcude trigger item and newState in ActiveItems metadata
    if state is REMOVE then remove trigger item key and value from ActiveItems metadata
*/

var controllerItems = ir.getItemsByTag('Automation Controller');

for( var i in controllerItems ){
  var controllerState=controllerItems[i].getState().toString();
  var controllerName=controllerItems[i].getName();
  if (controllerState == "ADD") {
    logger.info(['Adding ',event.itemName,' to ',controllerName,' as: ',event.itemState].join(''));
    var targetData={};
    targetData[event.itemName]=event.itemState.toString()
    set_metadata(controllerName,"ActiveItems",targetData, null, false)
  }
  if (controllerState == "REMOVE") {
    logger.info(['Removing ',event.itemName,' from ',controllerName].join(''));
    remove_key_value(controllerName,"ActiveItems", event.itemName)
  }
}
1 Like

Hi Justin,

Many thanks! I’ve tried to implement you solution but struggling a bit over the past days.

Where/How do I implement the membership script?

The widget only shows devices when I added the meta data manually. Also, the name of the scene is not appearing in the widget. I just seems like the reading/writing of the meta data is not working somehow.

Many thanks for your help again!

This is a rule. I have all my rules set up through the new MainUI rules builder so it looks like this:


gLights and gActors are groups that hold everything I might want to add to scenes (lights, of course, and ceiling fans, remote controls, speakers, etc. for the other). Because there are groups you can use the rule trigger member of X changed and now this rule will trigger whenever any of these items changes state. When it triggers it runs the script posted above.

The issue with your scene names is this line:

title: =loop.controller.label.substring(24)

All of my scene items have long default labels (e.g., Automation Controller - Daylight), so I don’t need the full label only the part after the hyphen. The substring() method returns a smaller string starting at the place indicated (in this case character #24). Your labels are probably all less that 24 characters long so substring is returning an empty string result. Just change that line to:

title: =loop.controller.label

and you should see your labels.

Many thanks! It took me some evening over the last days but I got it working, I needed to change the path in the the scripts but now it works excellent.

What where you planning to change on the UI? I am considering to rebuild your code to make a configurable scene button of some kind.

Another question I have and I was hoping you could help since my coding experience is a bit limited. I my house if have physical buttons I would like use to trigger a scene. My idea is to add them via the same way to the meta data of a scene and use a rule when a button is pressed it triggers the scene. Could you help me a bit in with the script I need for this rule?

Glad you got it working!

I think, at this point, there’s a way to build the UI interface that allows direct addition of items to the scene without the scene membership rule running at every item change. So the widget would change at least a little and the scene membership rule would change substantially.

There’s not much scripting required for this if I understand you correctly. If the button is just for triggering the scene then in whichever rule you have that triggers that particular scene just add that item as one of the triggers for that rule. My morning scene triggers at sunrise, but I could just as easily add a second trigger to the rule that also triggers the morning scene when I push a button on my beside remote:

I’ve finally gotten around to the update I was thinking about. You can see the whole system laid out here with the new features: