[hue] Added support for publishing ChannelDescriptionChangedEvents (#10718)

* Added service references to DynamicStateDescriptionProvider to support publishing ChannelDescriptionChangedEvent

Signed-off-by: Christoph Weitkamp <github@christophweitkamp.de>
This commit is contained in:
Christoph Weitkamp
2021-06-24 12:15:49 +02:00
committed by GitHub
parent 225e2ae15a
commit c5c2cab0a7
6 changed files with 43 additions and 34 deletions

View File

@@ -24,7 +24,7 @@ import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.hue.internal.handler.HueBridgeHandler; import org.openhab.binding.hue.internal.handler.HueBridgeHandler;
import org.openhab.binding.hue.internal.handler.HueGroupHandler; import org.openhab.binding.hue.internal.handler.HueGroupHandler;
import org.openhab.binding.hue.internal.handler.HueLightHandler; import org.openhab.binding.hue.internal.handler.HueLightHandler;
import org.openhab.binding.hue.internal.handler.HueStateDescriptionOptionProvider; import org.openhab.binding.hue.internal.handler.HueStateDescriptionProvider;
import org.openhab.binding.hue.internal.handler.sensors.ClipHandler; import org.openhab.binding.hue.internal.handler.sensors.ClipHandler;
import org.openhab.binding.hue.internal.handler.sensors.DimmerSwitchHandler; import org.openhab.binding.hue.internal.handler.sensors.DimmerSwitchHandler;
import org.openhab.binding.hue.internal.handler.sensors.GeofencePresenceHandler; import org.openhab.binding.hue.internal.handler.sensors.GeofencePresenceHandler;
@@ -67,11 +67,11 @@ public class HueThingHandlerFactory extends BaseThingHandlerFactory {
ClipHandler.SUPPORTED_THING_TYPES.stream(), HueGroupHandler.SUPPORTED_THING_TYPES.stream()) ClipHandler.SUPPORTED_THING_TYPES.stream(), HueGroupHandler.SUPPORTED_THING_TYPES.stream())
.flatMap(i -> i).collect(Collectors.toSet())); .flatMap(i -> i).collect(Collectors.toSet()));
private final HueStateDescriptionOptionProvider stateOptionProvider; private final HueStateDescriptionProvider stateDescriptionProvider;
@Activate @Activate
public HueThingHandlerFactory(final @Reference HueStateDescriptionOptionProvider stateOptionProvider) { public HueThingHandlerFactory(final @Reference HueStateDescriptionProvider stateDescriptionProvider) {
this.stateOptionProvider = stateOptionProvider; this.stateDescriptionProvider = stateDescriptionProvider;
} }
@Override @Override
@@ -142,9 +142,9 @@ public class HueThingHandlerFactory extends BaseThingHandlerFactory {
@Override @Override
protected @Nullable ThingHandler createHandler(Thing thing) { protected @Nullable ThingHandler createHandler(Thing thing) {
if (HueBridgeHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) { if (HueBridgeHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
return new HueBridgeHandler((Bridge) thing, stateOptionProvider); return new HueBridgeHandler((Bridge) thing, stateDescriptionProvider);
} else if (HueLightHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) { } else if (HueLightHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
return new HueLightHandler(thing, stateOptionProvider); return new HueLightHandler(thing, stateDescriptionProvider);
} else if (DimmerSwitchHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) { } else if (DimmerSwitchHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
return new DimmerSwitchHandler(thing); return new DimmerSwitchHandler(thing);
} else if (TapSwitchHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) { } else if (TapSwitchHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
@@ -160,7 +160,7 @@ public class HueThingHandlerFactory extends BaseThingHandlerFactory {
} else if (ClipHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) { } else if (ClipHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
return new ClipHandler(thing); return new ClipHandler(thing);
} else if (HueGroupHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) { } else if (HueGroupHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
return new HueGroupHandler(thing, stateOptionProvider); return new HueGroupHandler(thing, stateDescriptionProvider);
} else { } else {
return null; return null;
} }

View File

@@ -97,7 +97,7 @@ public class HueBridgeHandler extends ConfigStatusBridgeHandler implements HueCl
private static final long SCENE_POLLING_INTERVAL = TimeUnit.SECONDS.convert(10, TimeUnit.MINUTES); private static final long SCENE_POLLING_INTERVAL = TimeUnit.SECONDS.convert(10, TimeUnit.MINUTES);
private final Logger logger = LoggerFactory.getLogger(HueBridgeHandler.class); private final Logger logger = LoggerFactory.getLogger(HueBridgeHandler.class);
private final HueStateDescriptionOptionProvider stateDescriptionOptionProvider; private final HueStateDescriptionProvider stateDescriptionOptionProvider;
private final Map<String, FullLight> lastLightStates = new ConcurrentHashMap<>(); private final Map<String, FullLight> lastLightStates = new ConcurrentHashMap<>();
private final Map<String, FullSensor> lastSensorStates = new ConcurrentHashMap<>(); private final Map<String, FullSensor> lastSensorStates = new ConcurrentHashMap<>();
@@ -403,7 +403,7 @@ public class HueBridgeHandler extends ConfigStatusBridgeHandler implements HueCl
private List<String> consoleScenesList = new ArrayList<>(); private List<String> consoleScenesList = new ArrayList<>();
public HueBridgeHandler(Bridge bridge, HueStateDescriptionOptionProvider stateDescriptionOptionProvider) { public HueBridgeHandler(Bridge bridge, HueStateDescriptionProvider stateDescriptionOptionProvider) {
super(bridge); super(bridge);
this.stateDescriptionOptionProvider = stateDescriptionOptionProvider; this.stateDescriptionOptionProvider = stateDescriptionOptionProvider;
} }

View File

@@ -62,7 +62,7 @@ public class HueGroupHandler extends BaseThingHandler implements GroupStatusList
public static final String PROPERTY_MEMBERS = "members"; public static final String PROPERTY_MEMBERS = "members";
private final Logger logger = LoggerFactory.getLogger(HueGroupHandler.class); private final Logger logger = LoggerFactory.getLogger(HueGroupHandler.class);
private final HueStateDescriptionOptionProvider stateDescriptionOptionProvider; private final HueStateDescriptionProvider stateDescriptionOptionProvider;
private @NonNullByDefault({}) String groupId; private @NonNullByDefault({}) String groupId;
@@ -79,7 +79,7 @@ public class HueGroupHandler extends BaseThingHandler implements GroupStatusList
private List<String> consoleScenesList = List.of(); private List<String> consoleScenesList = List.of();
public HueGroupHandler(Thing thing, HueStateDescriptionOptionProvider stateDescriptionOptionProvider) { public HueGroupHandler(Thing thing, HueStateDescriptionProvider stateDescriptionOptionProvider) {
super(thing); super(thing);
this.stateDescriptionOptionProvider = stateDescriptionOptionProvider; this.stateDescriptionOptionProvider = stateDescriptionOptionProvider;
} }

View File

@@ -49,7 +49,7 @@ import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.thing.binding.ThingHandler; import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerService; import org.openhab.core.thing.binding.ThingHandlerService;
import org.openhab.core.types.Command; import org.openhab.core.types.Command;
import org.openhab.core.types.StateDescription; import org.openhab.core.types.StateDescriptionFragment;
import org.openhab.core.types.StateDescriptionFragmentBuilder; import org.openhab.core.types.StateDescriptionFragmentBuilder;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -83,7 +83,7 @@ public class HueLightHandler extends BaseThingHandler implements LightStatusList
private final Logger logger = LoggerFactory.getLogger(HueLightHandler.class); private final Logger logger = LoggerFactory.getLogger(HueLightHandler.class);
private final HueStateDescriptionOptionProvider stateDescriptionOptionProvider; private final HueStateDescriptionProvider stateDescriptionProvider;
private @NonNullByDefault({}) String lightId; private @NonNullByDefault({}) String lightId;
@@ -105,9 +105,9 @@ public class HueLightHandler extends BaseThingHandler implements LightStatusList
private @Nullable ScheduledFuture<?> scheduledFuture; private @Nullable ScheduledFuture<?> scheduledFuture;
public HueLightHandler(Thing hueLight, HueStateDescriptionOptionProvider stateDescriptionOptionProvider) { public HueLightHandler(Thing hueLight, HueStateDescriptionProvider stateDescriptionProvider) {
super(hueLight); super(hueLight);
this.stateDescriptionOptionProvider = stateDescriptionOptionProvider; this.stateDescriptionProvider = stateDescriptionProvider;
} }
@Override @Override
@@ -185,18 +185,14 @@ public class HueLightHandler extends BaseThingHandler implements LightStatusList
colorTemperatureCapabilties = ct; colorTemperatureCapabilties = ct;
// minimum and maximum are inverted due to mired/Kelvin conversion! // minimum and maximum are inverted due to mired/Kelvin conversion!
StateDescription stateDescription = StateDescriptionFragmentBuilder.create() StateDescriptionFragment stateDescriptionFragment = StateDescriptionFragmentBuilder.create()
.withMinimum(new BigDecimal(LightStateConverter.miredToKelvin(ct.max))) // .withMinimum(new BigDecimal(LightStateConverter.miredToKelvin(ct.max))) //
.withMaximum(new BigDecimal(LightStateConverter.miredToKelvin(ct.min))) // .withMaximum(new BigDecimal(LightStateConverter.miredToKelvin(ct.min))) //
.withStep(new BigDecimal(100)) // .withStep(new BigDecimal(100)) //
.withPattern("%.0f K") // .withPattern("%.0f K") //
.build().toStateDescription(); .build();
if (stateDescription != null) { stateDescriptionProvider.setStateDescriptionFragment(
stateDescriptionOptionProvider.setDescription( new ChannelUID(thing.getUID(), CHANNEL_COLORTEMPERATURE_ABS), stateDescriptionFragment);
new ChannelUID(thing.getUID(), CHANNEL_COLORTEMPERATURE_ABS), stateDescription);
} else {
logger.warn("Failed to create state description in thing {}", thing.getUID());
}
} }
} }
capabilitiesInitializedSuccessfully = true; capabilitiesInitializedSuccessfully = true;

View File

@@ -14,16 +14,21 @@ package org.openhab.binding.hue.internal.handler;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.events.EventPublisher;
import org.openhab.core.thing.Channel; import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.binding.BaseDynamicStateDescriptionProvider; import org.openhab.core.thing.binding.BaseDynamicStateDescriptionProvider;
import org.openhab.core.thing.events.ThingEventFactory;
import org.openhab.core.thing.i18n.ChannelTypeI18nLocalizationService; import org.openhab.core.thing.i18n.ChannelTypeI18nLocalizationService;
import org.openhab.core.thing.link.ItemChannelLinkRegistry;
import org.openhab.core.thing.type.DynamicStateDescriptionProvider; import org.openhab.core.thing.type.DynamicStateDescriptionProvider;
import org.openhab.core.types.StateDescription; import org.openhab.core.types.StateDescription;
import org.openhab.core.types.StateDescriptionFragment;
import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference; import org.osgi.service.component.annotations.Reference;
@@ -34,27 +39,36 @@ import org.osgi.service.component.annotations.Reference;
* *
* @author Hengrui Jiang - Initial contribution * @author Hengrui Jiang - Initial contribution
*/ */
@Component(service = { DynamicStateDescriptionProvider.class, HueStateDescriptionOptionProvider.class }) @Component(service = { DynamicStateDescriptionProvider.class, HueStateDescriptionProvider.class })
@NonNullByDefault @NonNullByDefault
public class HueStateDescriptionOptionProvider extends BaseDynamicStateDescriptionProvider { public class HueStateDescriptionProvider extends BaseDynamicStateDescriptionProvider {
private final Map<ChannelUID, StateDescription> descriptions = new ConcurrentHashMap<>(); private final Map<ChannelUID, StateDescriptionFragment> stateDescriptionFragments = new ConcurrentHashMap<>();
@Activate @Activate
public HueStateDescriptionOptionProvider( public HueStateDescriptionProvider(final @Reference EventPublisher eventPublisher, //
final @Reference ItemChannelLinkRegistry itemChannelLinkRegistry, //
final @Reference ChannelTypeI18nLocalizationService channelTypeI18nLocalizationService) { final @Reference ChannelTypeI18nLocalizationService channelTypeI18nLocalizationService) {
this.eventPublisher = eventPublisher;
this.itemChannelLinkRegistry = itemChannelLinkRegistry;
this.channelTypeI18nLocalizationService = channelTypeI18nLocalizationService; this.channelTypeI18nLocalizationService = channelTypeI18nLocalizationService;
} }
public void setDescription(ChannelUID channelUID, StateDescription description) { public void setStateDescriptionFragment(ChannelUID channelUID, StateDescriptionFragment stateDescriptionFragment) {
descriptions.put(channelUID, description); StateDescriptionFragment oldStateDescriptionFragment = stateDescriptionFragments.get(channelUID);
if (!stateDescriptionFragment.equals(oldStateDescriptionFragment)) {
stateDescriptionFragments.put(channelUID, stateDescriptionFragment);
postEvent(ThingEventFactory.createChannelDescriptionChangedEvent(channelUID,
itemChannelLinkRegistry != null ? itemChannelLinkRegistry.getLinkedItemNames(channelUID) : Set.of(),
stateDescriptionFragment, oldStateDescriptionFragment));
}
} }
@Override @Override
public @Nullable StateDescription getStateDescription(Channel channel, public @Nullable StateDescription getStateDescription(Channel channel,
@Nullable StateDescription originalStateDescription, @Nullable Locale locale) { @Nullable StateDescription originalStateDescription, @Nullable Locale locale) {
StateDescription stateDescription = descriptions.get(channel.getUID()); StateDescriptionFragment stateDescriptionFragment = stateDescriptionFragments.get(channel.getUID());
return stateDescription != null ? stateDescription return stateDescriptionFragment != null ? stateDescriptionFragment.toStateDescription()
: super.getStateDescription(channel, originalStateDescription, locale); : super.getStateDescription(channel, originalStateDescription, locale);
} }
} }

View File

@@ -39,7 +39,6 @@ import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus; import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingUID; import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.ThingHandlerCallback; import org.openhab.core.thing.binding.ThingHandlerCallback;
import org.openhab.core.thing.i18n.ChannelTypeI18nLocalizationService;
import org.openhab.core.types.Command; import org.openhab.core.types.Command;
import com.google.gson.Gson; import com.google.gson.Gson;
@@ -385,6 +384,7 @@ public class HueLightHandlerTest {
assertSendCommand(channel, command, currentState, expectedReply, "LCT001", "Philips"); assertSendCommand(channel, command, currentState, expectedReply, "LCT001", "Philips");
} }
@SuppressWarnings("null")
private void assertSendCommand(String channel, Command command, HueLightState currentState, String expectedReply, private void assertSendCommand(String channel, Command command, HueLightState currentState, String expectedReply,
String expectedModel, String expectedVendor) { String expectedModel, String expectedVendor) {
FullLight light = gson.fromJson(currentState.toString(), FullConfig.class).getLights().get(0); FullLight light = gson.fromJson(currentState.toString(), FullConfig.class).getLights().get(0);
@@ -400,8 +400,7 @@ public class HueLightHandlerTest {
long fadeTime = 400; long fadeTime = 400;
HueLightHandler hueLightHandler = new HueLightHandler(mockThing, HueLightHandler hueLightHandler = new HueLightHandler(mockThing, mock(HueStateDescriptionProvider.class)) {
new HueStateDescriptionOptionProvider(mock(ChannelTypeI18nLocalizationService.class))) {
@Override @Override
protected synchronized HueClient getHueClient() { protected synchronized HueClient getHueClient() {
return mockClient; return mockClient;