Enhance existing StateDescription in code

I have an existing channel-type. At run time, I want to read config from the AirConditioner and set the min and max setpoint values on the State. An AirTouch can control multiple AC units. I therefore can’t assume all channels will have the same min/max values and presume I need to create new instances of the state.

I define my channel-type in XML like this…

	<channel-type id="airconditioner-zone-setpoint">
		<item-type>Number:Temperature</item-type>
		<label>Zone Setpoint Temperature</label>
		<description>The temperature setpoint for the Zone.</description>
		<category>Temperature</category>
		<tags>
			<tag>Temperature</tag>
			<tag>Setpoint</tag>
		</tags>
		<state step="1" pattern="%d °C" readOnly="false"/>
	</channel-type>

I have found that I should be using a StateDescriptionFragmentBuilder to enhance the existing state and build a new one. I can then simply override min and max on the State.

How do I find the existing State I created in XML?

  StateDescription existing =  ??? // some magic here to get existing StateDescription
  StateDescriptionFragmentBuilder.create(existing)
      .withMaximum(max)
      .withMinimum(min)
      .build();`

Also, what do I need to do after this to get the StateDescription? Do I need to register it with a service? Or just pass it into the channel creation?

If someone could please point me at a working example binding I would be very greatful.

I have a working solution. I’m not sure it’s the “OpenHAB way”, but it is working for me.

  1. Define ChannelTypes in thing-types.xml. For most of my types this is good enough.
  2. Created a DynamicStateDescriptionProvider. This has two caches in it to store values.
  3. For the few types that require customisation, I create a StateDescriptionFragment in my Handler and store these in the cache in my DynamicStateDescriptionProvider.
  4. When DynamicStateDescriptionProvider is asked about a StateDescription, I check if I have a StateDescriptionFragment for that channel.
  5. If I have a StateDescriptionFragment in my cache, I merge the fragment with StateDescriptionand cache the merge result for next time.

Here is my DynamicStateDescriptionProvider method.

    /**
     * If the Channel is one from our binding and we have a StateDescriptionFragment in our cache
     * merge the StateDescriptionFragment with the passed in StateDescription.
     * Store this merged value for next time it is requested.
     */
    @Override
    public @Nullable StateDescription getStateDescription(Channel channel,
            @Nullable StateDescription originalStateDescription, @Nullable Locale locale) {
        if (AirTouchBindingConstants.BINDING_ID.equals(channel.getUID().getBindingId())) {
            logger.trace("state requested for channel {}: Existing description: {}", channel.getUID(),
                    originalStateDescription);
            if (this.stateDescriptionCache.containsKey(channel)) {
                logger.trace("Returning cached StateDescription: {}", this.stateDescriptionCache.get(channel));
                return this.stateDescriptionCache.get(channel);
            } else if (originalStateDescription != null && this.stateDescriptionFragmentCache.containsKey(channel)) {
                logger.trace("Fragment cache contains channel. Attempting to build fragment for {} from {} ",
                        channel.getUID(), this.stateDescriptionFragmentCache.get(channel));

                StateDescriptionFragmentBuilder fragmentBuilder = StateDescriptionFragmentBuilder
                        .create(originalStateDescription);
                StateDescriptionFragment cachedFragment = this.stateDescriptionFragmentCache.get(channel);

                BigDecimal min = cachedFragment.getMinimum();
                if (min != null) {
                    fragmentBuilder.withMinimum(min);
                }
                BigDecimal max = cachedFragment.getMaximum();
                if (max != null) {
                    fragmentBuilder.withMaximum(max);
                }

                // TODO: Handle FanSpeed and Mode String options

                StateDescription fragment = fragmentBuilder.build().toStateDescription();
                if (fragment != null) {
                    logger.trace("Fragment created for {} as {} ", channel.getUID(), fragment);
                    this.stateDescriptionCache.put(channel, fragment);
                    return this.stateDescriptionCache.get(channel);
                }
            }
        }
        return null;
    }