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.
Define ChannelTypes in thing-types.xml. For most of my types this is good enough.
Created a DynamicStateDescriptionProvider. This has two caches in it to store values.
For the few types that require customisation, I create a StateDescriptionFragment in my Handler and store these in the cache in my DynamicStateDescriptionProvider.
When DynamicStateDescriptionProvider is asked about a StateDescription, I check if I have a StateDescriptionFragment for that channel.
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;
}