Setting the value of an item group that uses an aggregation function

  • Platform information:
    • Hardware: Raspberry Pi Model 3b
    • OS: Openhabian
    • Java Runtime Environment: openjdk 11.0.18 2023-01-17
    • openHAB version: 3.4.1

I have two Sonoff IFAN02 devices flashed with tasmota. I have them connected to OpenHAB through mqtt. Definitions are as follows:

UID: mqtt:topic:xxxx:xxxx
label: Living Room Fan
thingTypeUID: mqtt:topic
configuration: {}
bridgeUID: mqtt:broker:xxxx
location: Living Room Fan
channels:
  - id: fan_speed
    channelTypeUID: mqtt:string
    label: Fan Speed
    description: ""
    configuration:
      allowedStates: 0,1,2,3,+,-
      commandTopic: cmnd/living_room_fan/FanSpeed
      stateTopic: tele/living_room_fan/STATE
      transformationPattern: JSONPATH:$.FanSpeed
      off: 0
      on: 1
  - id: fan_light
    channelTypeUID: mqtt:switch
    label: Fan Light
    description: ""
    configuration:
      commandTopic: cmnd/living_room_fan/POWER1
      stateTopic: tele/living_room_fan/STATE
      transformationPattern: JSONPATH:$.POWER1
      off: OFF
      on: ON



UID: mqtt:topic:yyyy:yyyy
label: Dining Room Fan
thingTypeUID: mqtt:topic
configuration: {}
bridgeUID: mqtt:broker:yyyy
location: Dining Room Fan
channels:
  - id: fan_speed
    channelTypeUID: mqtt:string
    label: Fan Speed
    description: ""
    configuration:
      allowedStates: 0,1,2,3,+,-
      commandTopic: cmnd/dining_room_fan/FanSpeed
      stateTopic: tele/dining_room_fan/STATE
      transformationPattern: JSONPATH:$.FanSpeed
      off: 0
      on: 1
  - id: fan_light
    channelTypeUID: mqtt:switch
    label: Fan Light
    description: ""
    configuration:
      commandTopic: cmnd/dining_room_fan/POWER1
      stateTopic: tele/dining_room_fan/STATE
      transformationPattern: JSONPATH:$.POWER1
      off: OFF
      on: ON

The fan channel has 4 states 0,1,2 and 3
These work as expected. If i change the states, the fans change speed accordingly, as expected.

My intention is to have a group that contains both fan channels. I would like this group to show an aggregation of the values of the 2 fans (MAX, to be exact), and additionally, I would like to use the group to control the fans (For example, if I want to turn all fans off, I send 0 to the group, if I want to set all fans to level 1, I send 1 to the group, etc etc)

My first idea was to create a group of base type string (The fan channels are of type string) and use the MAX function to aggregate the values of the fans. Sadly, this does not work, the result of the aggregation turns up to be a date (?!?!?!?!?)

Then I turned to creating a group of base type number, and use MAX as and aggregation again. When it comes to show the aggregation of the two fan channels, this works perfectly. When both fan channels are 0 (Off), then the group value is 0 and I can interpret this as “Everything is off”. When one or both fan channels are non-zero, I see the highest value between the fan channels as group aggregation, and this is my expected result also.

The problem comes when i try to control the fans through the group. I can set the group value as 0, 1,2 or 3 using SendCmd, and i can see in the logs that an attempt is made to change the values of the group members:

02:47:23.321 [INFO ] [openhab.event.ItemCommandEvent       ] - Item 'main_room_all_fans' received command 1
02:47:23.343 [INFO ] [openhab.event.ItemCommandEvent       ] - Item 'dining_room_fan_FanSpeed' received command 1
02:47:23.349 [INFO ] [openhab.event.ItemCommandEvent       ] - Item 'living_room_fan_FanSpeed' received command 1

Nothing happens though. The fans do not change state, and the group does not change state either.
I guess i have the following questions:

-Am i supposed to be able to change the value of a group that has an aggregation defined?
-Should I expect the group members to change values too?
-If i am, can someone help me figure out what am i doing wrong?
-Could this have to do with the fact that the group is of basetype integer, and the members are using string types? If that is the case, how can i solve this problem, given that using MAX on basetype string does not appear to work?

Here is a screenshot of the group configuration:

The command i am using to try to change the value of the group is:

sendCmd('main_room_all_fans', '1')

or

sendCmd('main_room_all_fans', '0')

(I did try with or without the quotes surrounding the value i am assigning. Tried no quotes, single quotes and double quotes)

Thanks in advance for any help or pointers you could provide

For now, I ended up solving this using 2 groups. One for monitoring (The group with basetype Number, and a MAX aggregation) and a regular string group with no aggregation, that I use to control the devices. If anyone knows a better way to handle this scenario, please let me know.

Thanks!

A number is not a string. A (dirty) workaround would be a simple rule:

rule "send command to group members"
when
    Item main_room_all_fans received command
then
    main_room_all_fans.members.forEach[i|
        i.sendCommand(receivedCommand.toString)
    ]
end

Of course you can do this not only in DSL but all other rules as well.

Thanks @Udo_Hartmann,
I am trying to not use rules for this specific case, and to keep everything self contained in objects.

I understand that strings are not numbers, however, what i would have expected as a behaviour is:

-Some kind of error in the logs, specifically because string are not numbers. In fact, I have seen cases of log messages where OpenHAB specifically states it has ignored a value sent to an item because types do not match. However, the logs are clean, and if one read the logs, everething appears to run smoothly.

-A way to convert those numbers to strings, either automatically or manually. I thought openhab did that in the background, and I understand it would not be able to convert an 1 to a ‘A’, but a 1 to a ‘1’ appears to be more straightforward.

Thanks again

This is telling. I assume you’ve not turned off autoupdate on these Items? I’m not certain if the MQTT binding suppresses that when both the command and state topics are configured. When autoupdate is suppressed, the Item’s won’t update until the fans report that they’ve changed state on the state topic.

Are you monitoring the MQTT topics to see if the command is being published or not?

But based on these logs and your description, the Group is behaving as expected. The problem is somewhere else.

Please show the number channel configs for the Thing. Note that MAX will only work with numbers so the String approach is a non-starter.

Yes

Yes, if they are updated to a different state. Note that a command does not guarantee a state change.

Absolutely. The Group’s base type and the members of the Group’s type must match. Doing anything else results in undefined behavior.

Either abandon the idea of using MAX on a Group and use a proxy Item and a Rule, or change your Items and Thing Channels to be Numbers.

Which rules language is this?

Then everything needs to be Numbers. As @Udo_Hartmann said, a number is not a string.

It is common to have a mix of different types of Items be members of a Group. And in some cases it can even make sense to use an aggregation function and send commands to such a Group. Therefore it’s not considered an error if you send a command to a Group that isn’t supported by all it’s members, so there’s no error logged.

There is a way, using rules.

But you can’t do this using Groups. When you send the command to the Group, the command is parsed (if it’s a String) and converted to the specific type supported by the Group’s base type, in this case DecimalType. DecimalType is not a valid command to send to a String Item so, given that this is not considered an error case, the command is ignored.

@rlkoshak , thanks for the help. after a bit of pain, i converted all my fans ro numbers, and everything appears to be working without the need for 2 groups now.

While i understand the “a Number is not a string” arguments, sometimes the scenarios where the types these can or cannot be parsed properly between items and channel is still a bit confusing to me.

Anyways, the main objective was achieved, so thanks again!

This is because conversion table between openHAB types is not consistent. You would expect that (almost) every cast could be reversed making below line true:

new DecimalType(1.0).as(StringType.class).as(DecimalType.class) equals new DecimalType(1.0)

Sadly, its not a case. Each type has its own logic to cast itself to another type, which means that other type may miss cast to source type.

Look at as methods for types to find which combinations are supported:

Some types such as String, DateTime, Raw simply do not have casting logic (even if DateTime cast to Decimal could make it ie. unix timestamp :wink: ).

That is helpful @Udo_Hartmann , thanks for the info!

I think you meant to tag @splatch .

The only think I can add is this would be a great place for some PRs for OH 4 to make the getStateAs() and .as() methods a bit smarter and flexible. I could also see the Group command processing logic being more flexible by using the event bus’s command action with the String representation of the state instead of the other representations so that it’s up to the receiving Item how to interpret the command. I think the original attempt to make this work above would have worked if that were the case.

At a minimum it would be a start if StringType’s as attempted to parse it’s state to the target type. The logic wouldn’t be too ugly and it would add a lot of flexibility.

It would also be nice to see some non-OH types in the mix. For example, if QuantityType, DecimalType, and PercentType supported as(Number).

1 Like

Correct @rlkoshak , i mean @splatch . Sorry for the confusion!

And of course this should be null aware (or something similar, so no more need to check before casting to a specific type if it’s even possible).

It’s kind of annoying to always write down several lines only to get rid of nullPointerExceptions.

I’m not sure how feasible that would be. What should MyItem.getStateAs(DecimalType) return if the state is UnDefType? Anything I can think of would still require you to write those lines of code to handle the NULL/UNDEF states.

Yes, sure, but no Exception when doing the .getStateAs().

1 Like