Automatic trigger javascript rule

I create a javascript rule for my sonos speakers. It starts with

rules.JSRule({
//---------------------------------------------------------------------------
  name: "SonesSwitchToSelected",
  description: "When room radioStation changed switch to selected",
  triggers: [triggers.GroupStateChangeTrigger("SonosStation")], 
  execute: (event) => {

This trigger is fired when everything is in rest. This means that the radio can start during the night.
The items ar e as shown below

Group     SonosStation
Number    RadioStationBadkamer             "Radio"                      <network>    (SonosStation)
Number    RadioStationHuiskamer            "Radio"                      <network>    (SonosStation)
Number    RadioStationBadkamer             "Radio"                      <network>    (SonosStation)

How is this possible and how can I find the root cause?

To be honest, I have chance to understand your problem. Could you provide more information?

Please note that you added an item with the same id twice

I copied the wrong line. The last item line should be

Number    RadioStationKeuken               "Radio"                      <network>    (SonosStation)

Some additional info. I use the 3 items to select a radio station from Sitemap with a pull down. When I do this for one of the 3 items the javascript rule is fired and the requested radiostation based on the number is selected. That is working fine. The problem is that sometimes the rule is fired without I change one of the 3 items.

When the rule is fired could it be that someone changes the radio station directly at the device or via sonos app?

That really cannot be. Based on the code you have shown, there is no way for this rule to trigger without one of those items changing state. Since those items are not connected to any channel that could cause a state change, then something else is causing the item to change.

  1. Are those items involved in any other rule that might be running at different times?
  2. Do any of those items have metadata (e.g., expire metadata) added via the UI?
  3. Is your sitemap open to the internet (there have been examples in the past of harmless interference from outside individuals if an OH system is found unsecured on the internet)?

You don’t show the whole rule. Are there timers or delays in the rule that may be causing it to complete its run some time long after it has been triggered?

The first thing to look at is are those RadioStation Items changing? You can find that out by looking at events.log. Based on timing you can examine your openhab.log and see what else is going on around that time.

Beyond that, we need answers to what @JustinG asked to guess any more than this.

Yes it changed but not from the sitemap. I was sleeping

2025-01-29 03:11:11.047 [INFO ] [openhab.event.ItemCommandEvent      ] - Item 'RadioStationBadkamer' received command 1
2025-01-29 03:11:11.047 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'RadioStationBadkamer' changed from 7 to 1

This is matching with the login written from the javascript rule

2025-01-29 03:11:13.049 [INFO ] [automation.script.file.javartrial.js] - 2: Geselecteerde waarde voor radioString:  NPO Radio 2

But it also changed just before

2025-01-29 03:11:02.355 [INFO ] [openhab.event.ItemCommandEvent      ] - Item 'RadioStationBadkamer' received command 7
2025-01-29 03:11:02.356 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'RadioStationBadkamer' changed from 1 to 7

I search fro this item and found it as expected in the sitemap

 Selection item=RadioStationBadkamer mappings=[0="Uit", 1="Radio 2", 2="Qmusic", 3="Radio 10", 4="Slam", 5="Radio 3", 6="Sky", 7="Extern"]

and in the javascript file. See full rule below.

//---------------------------------------------------------------------------
rules.JSRule({
//---------------------------------------------------------------------------
  name: "SonesSwitchToSelected",
  description: "When room radioStation changed switch to selected",
  triggers: [triggers.GroupStateChangeTrigger("SonosStation")], 
  execute: (event) => {
    let newStateNumber = event.newState;
    let previousStateNumber = event.newState.toString();
//    var radioString = "geen waarde"
    var now = time.ZonedDateTime.now();
//    console.log("1: Geselecteerde waarde voor radioString na radio voor timer 1: ", radioString);
    switch (newStateNumber)  {
      case "0" : radioString = "uit"; break;        
      case "1" : radioString = items["Radio2"].state.toString(); break;
      case "2" : radioString = items["Qmusic"].state.toString(); break;
      case "3" : radioString = items["Radio10"].state.toString(); break;
      case "4" : radioString = items["Slam"].state.toString(); break;
      case "5" : radioString = items["Radio3"].state.toString(); break;
      case "6" : radioString = items["SkyRadio"].state.toString(); break;
      case "7" : radioString = "extern"; break;
      default: radioString = "undefined"; break;
    } 
    var myTimer = actions.ScriptExecution.createTimer(now.plusSeconds(2), function() {
      console.log("2: Geselecteerde waarde voor radioString: ", radioString);
      if (radioString != "extern") {
        switch (event.itemName) {
         case "RadioStationBadkamer":   if (radioString == "uit") items["SonosPlay1BadkamerControl"].sendCommand("PAUSE"); else
                                                                  items["SonosPlay1BadkamerFavorite"].sendCommand(radioString); break;
          case "RadioStationKeuken":    if (radioString == "uit") items["SonosPlay1KeukenControl"].sendCommand("PAUSE"); else 
                                                                  items["SonosPlay1KeukenFavorite"].sendCommand(radioString); break;
          case "RadioStationHuiskamer": if (radioString == "uit") items["SonosPlay1HuiskamerControl"].sendCommand("PAUSE"); else 
                                                                  items["SonosPlaybarHuiskamerFavorite"].sendCommand(radioString); break;
          default:
            console.log("Geen geldig item gevonden voor: ", event.itemName);
        }
      } 
    });
  }
});

OK, so we know from this that there isn’t anything wrong with this Rule. Something is commanding one or more members of the Group which results in a change which triggers the rule.

Commands can come from the following places:

  1. UIs including but not limited to MainUI, BasicUI, and phone apps
  2. Rules
  3. Bindings
  4. Other parts of the API (REST API, karaf console, etc.)
  5. Access to these through the cloud connector
  6. Access to these through a reverse proxy or by exposing OH directly to the internet
  7. Someone on your LAN is messing with you

The posted rule does not appear to command these Items so we can eliminate this one rule as the source. But there could be other rules or ways that this Item gets commanded. And if you have a rule that generates the names of the Items to command it won’t necessarily show up in a search.

You’ll have to systematically eliminate each of those six possible sources for commands to the Item. Assuming the Item definitions posted are complete (i.e. no member of SonosStation is lined to a Channel and there are no Items not listed that are a member of SonosStation), 3 can be eliminated.

6 Should be easy to eliminate as a possibility. If you can’t tell from your own networking configuration, go to https://whatsmyip.org and then search for that address in https://shodan.io. That will show you what can be seen by the internet from your IP address and if OH is exposed it usually can identify that.

5 you can look at what devices are attached (under your email will be a menu named “devices”. Look for anything unexpected there.

1 and 2 will take analysis and possible systematically disabling/removing access until the behavior stops.

4 and 7 will be the hardest to find and may require setting up wireshark to capture the network traffic to the OH machine and see where the commands are coming from.

Completely off-topic, but your rule could be made significantly simpler.

If you use a String Item instead of a Number Item you can map to the Strings you need in the first place and eliminate the switch.

Selection item=RadioStationBadkamer mappings=[uit="Uit", Radio2="Radio 2", Qmusic="Qmusic", Radio10="Radio 10", Slam="Slam", Radio3="Radio 3", SkyRadio="Sky", extern="Extern"]

Then your rule could become (with a few more improvements).

//---------------------------------------------------------------------------
rules.JSRule({
//---------------------------------------------------------------------------
  name: "SonesSwitchToSelected",
  description: "When room radioStation changed switch to selected",
  triggers: [triggers.GroupStateChangeTrigger("SonosStation")], 
  execute: (event) => {
    // If the change was to an Item name, use it's state, otherwise keep extern or uit
    let radioString = (items[event.newState] !== null) ? items[event.newState].state : event.newState;

    // what if the Timer already exists?
    var myTimer = actions.ScriptExecution.createTimer(time.toZDT("PT2S"), function() {
      console.log("2: Geselecteerde waarde voor radioString: ", radioString);

      // if the radioString is uid the command is always PAUSE, otherwise it's the radioString
      let command = (radioString == "uit") ? "PAUSE" : radioString;

      // If the radioString is uit the associated control Item is commanded, otherwise the favorite Item is commanded
      let itemName = "SonosPlay1" + event.itemName.repalce("RadioStation", "") + (radioString == "uit") ? "Control" : "Favorite";
      if(event.item.includes("HuiskamerFavorite")) itemName.replace("1", "bar"); // can be eliminated if consistent naming scheme is used

      // command the Item or log if we can't handle the if the change isn't to extern
      if(radioString != "extern") {
        if(items[itemName] == null ) {
          console.log("Geen geldig item gevonden voor: ", event.itemName);
        }
        else {
          items[itemName].sendCommand(command);
        }
      }
    });
  }
});

Thanks rlkoshak. Is much better. After I find the problem I will use your code and look at other locations for improvements.

Thanks for all the suggestions.