Xtend Scripts vs JSR223?

OK so your first requirement: “change the colour of any lights that are on when a scene changes” is pretty simple, assuming that all your Color Lights are in gColor.

gColor.members.filter[ f | f.getStateAs(OnOffType) == ON ].forEach[ f | f.sendCommand(<new color>) ]

where <new color> is the color to change the lights to. Filter so we have a List containing all the lights that are ON then send that light the new color.

You’re second requirement though is a bit different but not too bad. If we assume that all your Color proxy Items are in the group gProxy, and you have Rules that keep the proxy and real Item in sync (which should be the case for a proxy Item) then you need to filter on and loop through the gProxy instead.

gProxy.members.filter[ f | f instanceof ColorItem && f.getStateAs(OnOffType) == ON ].forEach[ f | f.sendCommand(<new color>) ]

If your proxy is acting as a true proxy then when the light is ON, the proxy will be ON and vise versa so we don’t really even need to search across multiple Groups.

So this solves your particular case, but what if there are other cases where you may want to filter and find Items like your original question. We can do this a number of ways using your existing Group structure. BTW, the following are implementations of Design Pattern: Associated Items.

    gColor,members.filter[ f | f.getStateAs(OnOffType) == ON ].forEach[ f |
        val proxy = gProxy.findFirst[ p | p.name == f.name.replace("Light", "Proxy") ]
        proxy.sendCommand(<new color>)
    ]

Filter to get a List of those lights that are ON and for each of those find the associated proxy Item based on name and send the new color to the proxy.

This assumes a one to one. If you had lots of proxy Items associated with this one light (not sure why one would have that but let’s just assume it for now):

    gColor,members.filter[ f | f.getStateAs(OnOffType) == ON ].forEach[ f |
        val proxies = gProxy.members.findFirst[ p | p.name.contians(f.name.replace("Light", "") ]
        proxies.forEach[ p | p.sendCommand(<new color>) ]
    ]

So for this we get the List of lights that are ON, and then get a list of proxies that have a matching name (assumes the naming scheme in the Items above) and send the new color to those proxies.

Or in another approach you can use the Groups, though this is a little more challenging because we don’t really have a hint as to what Group the member of gColor is also member of is that we care about. If we can assume that the members of gColor are only ever a member of one other Group then we can do something like:

    gColor,members.filter[ f | f.getStateAs(OnOffType) == ON ].forEach[ f |
        val relatedGroup = f.getGroupNames.findFirst[ gn | gn != "gColor" ]
        val proxies = gProxy.members.filter[ p | p.getGroupNames.contains(relatedGroup) ]
        proxies.forEach[ p | p.sendCommand(<new color>) ]
    ]

Filter to get a List of lights that are ON and for each of those get the name of the first Group it is a member of that isn’t “gColor”. Then filter gProxy for all Items that are also a member of the just found Group. Finally, for each proxy send the new color to the proxy.

If the lights can be a member of more than two Groups, then it gets a bit more complicated because we don’t have anything to tell us which of the two or more additional Groups that are not gColor we care about. You can use the name trick in this case again in some cases.

    gColor,members.filter[ f | f.getStateAs(OnOffType) == ON ].forEach[ f |
        val relatedGroup = f.getGroupNames.findFirst[ gn | gn.contains("room")  ]
        val proxies = gProxy.members.filter[ p | p.getGroupNames.contains(relatedGroup) ]
        proxies.forEach[ p | p.sendCommand(<new color>) ]
    ]

Which gives us the first Group name that contains “room” in the name. Of course this assumes that only one Group will have “room” in its name.

When Xtend and the Rules DSL started to click for me was when I realized that it really likes to work with Lists. All of the powerful operations (filter, forEach, findFirst, map, reduce, et. al.) are List operations. So if you can convert your problem into a List operation the language will help you solve it rather than feel like its fighting against you. So you will find that half of the DPs revolve around representing your problem as a List, usually using Groups, for which the language provides the best tools.