Skip to content

Commit

Permalink
Added support for publishing ChannelDescriptionChangedEvents (#10900)
Browse files Browse the repository at this point in the history
Signed-off-by: Christoph Weitkamp <github@christophweitkamp.de>
  • Loading branch information
cweitkamp authored Jun 23, 2021
1 parent 089a78f commit 225e2ae
Show file tree
Hide file tree
Showing 8 changed files with 106 additions and 130 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.deconz.internal;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.BaseDynamicCommandDescriptionProvider;
import org.openhab.core.thing.type.DynamicCommandDescriptionProvider;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Dynamic channel command description provider.
* Overrides the command description for the controls, which receive its configuration in the runtime.
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
@Component(service = { DynamicCommandDescriptionProvider.class, DeconzDynamicCommandDescriptionProvider.class })
public class DeconzDynamicCommandDescriptionProvider extends BaseDynamicCommandDescriptionProvider {
private final Logger logger = LoggerFactory.getLogger(DeconzDynamicCommandDescriptionProvider.class);

/**
* remove all descriptions for a given thing
*
* @param thingUID the thing's UID
*/
public void removeDescriptionsForThing(ThingUID thingUID) {
logger.trace("removing state description for thing {}", thingUID);
channelOptionsMap.entrySet().removeIf(entry -> entry.getKey().getThingUID().equals(thingUID));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,19 @@

import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.BaseDynamicStateDescriptionProvider;
import org.openhab.core.thing.events.ThingEventFactory;
import org.openhab.core.thing.type.DynamicStateDescriptionProvider;
import org.openhab.core.types.StateDescription;
import org.openhab.core.types.StateDescriptionFragment;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -34,24 +38,30 @@
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
@Component(service = { DynamicStateDescriptionProvider.class, StateDescriptionProvider.class })
public class StateDescriptionProvider implements DynamicStateDescriptionProvider {
@Component(service = { DynamicStateDescriptionProvider.class, DeconzDynamicStateDescriptionProvider.class })
public class DeconzDynamicStateDescriptionProvider extends BaseDynamicStateDescriptionProvider {
private final Logger logger = LoggerFactory.getLogger(DeconzDynamicStateDescriptionProvider.class);

private final Map<ChannelUID, StateDescription> descriptions = new ConcurrentHashMap<>();
private final Logger logger = LoggerFactory.getLogger(StateDescriptionProvider.class);
private final Map<ChannelUID, StateDescriptionFragment> stateDescriptionFragments = new ConcurrentHashMap<>();

/**
* Set a state description for a channel. This description will be used when preparing the channel state by
* the framework for presentation. A previous description, if existed, will be replaced.
*
* @param channelUID
* channel UID
* @param description
* @param stateDescriptionFragment
* state description for the channel
*/
public void setDescription(ChannelUID channelUID, StateDescription description) {
logger.trace("adding state description for channel {}", channelUID);
descriptions.put(channelUID, description);
public void setDescriptionFragment(ChannelUID channelUID, StateDescriptionFragment stateDescriptionFragment) {
StateDescriptionFragment oldStateDescriptionFragment = stateDescriptionFragments.get(channelUID);
if (!stateDescriptionFragment.equals(oldStateDescriptionFragment)) {
logger.trace("adding state description for channel {}", channelUID);
stateDescriptionFragments.put(channelUID, stateDescriptionFragment);
postEvent(ThingEventFactory.createChannelDescriptionChangedEvent(channelUID,
itemChannelLinkRegistry != null ? itemChannelLinkRegistry.getLinkedItemNames(channelUID) : Set.of(),
stateDescriptionFragment, oldStateDescriptionFragment));
}
}

/**
Expand All @@ -61,17 +71,18 @@ public void setDescription(ChannelUID channelUID, StateDescription description)
*/
public void removeDescriptionsForThing(ThingUID thingUID) {
logger.trace("removing state description for thing {}", thingUID);
descriptions.entrySet().removeIf(entry -> entry.getKey().getThingUID().equals(thingUID));
stateDescriptionFragments.entrySet().removeIf(entry -> entry.getKey().getThingUID().equals(thingUID));
}

@Override
public @Nullable StateDescription getStateDescription(Channel channel,
@Nullable StateDescription originalStateDescription, @Nullable Locale locale) {
if (descriptions.containsKey(channel.getUID())) {
StateDescriptionFragment stateDescriptionFragment = stateDescriptionFragments.get(channel.getUID());
if (stateDescriptionFragment != null) {
logger.trace("returning new stateDescription for {}", channel.getUID());
return descriptions.get(channel.getUID());
return stateDescriptionFragment.toStateDescription();
} else {
return null;
return super.getStateDescription(channel, originalStateDescription, locale);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,14 @@ public class DeconzHandlerFactory extends BaseThingHandlerFactory {
private final Gson gson;
private final WebSocketFactory webSocketFactory;
private final HttpClientFactory httpClientFactory;
private final StateDescriptionProvider stateDescriptionProvider;
private final CommandDescriptionProvider commandDescriptionProvider;
private final DeconzDynamicStateDescriptionProvider stateDescriptionProvider;
private final DeconzDynamicCommandDescriptionProvider commandDescriptionProvider;

@Activate
public DeconzHandlerFactory(final @Reference WebSocketFactory webSocketFactory,
final @Reference HttpClientFactory httpClientFactory,
final @Reference StateDescriptionProvider stateDescriptionProvider,
final @Reference CommandDescriptionProvider commandDescriptionProvider) {
final @Reference DeconzDynamicStateDescriptionProvider stateDescriptionProvider,
final @Reference DeconzDynamicCommandDescriptionProvider commandDescriptionProvider) {
this.webSocketFactory = webSocketFactory;
this.httpClientFactory = httpClientFactory;
this.stateDescriptionProvider = stateDescriptionProvider;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
package org.openhab.binding.deconz.internal.dto;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.types.CommandOption;

/**
* The {@link Scene} is send by the websocket connection as well as the Rest API.
Expand All @@ -24,4 +25,13 @@
public class Scene {
public String id = "";
public String name = "";

public CommandOption toCommandOption() {
return new CommandOption(name, name);
}

@Override
public String toString() {
return "Scene{" + "id='" + id + '\'' + ", name='" + name + '\'' + '}';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,13 @@
import java.util.stream.Collectors;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.deconz.internal.CommandDescriptionProvider;
import org.openhab.binding.deconz.internal.DeconzDynamicCommandDescriptionProvider;
import org.openhab.binding.deconz.internal.Util;
import org.openhab.binding.deconz.internal.dto.DeconzBaseMessage;
import org.openhab.binding.deconz.internal.dto.GroupAction;
import org.openhab.binding.deconz.internal.dto.GroupMessage;
import org.openhab.binding.deconz.internal.dto.GroupState;
import org.openhab.binding.deconz.internal.dto.Scene;
import org.openhab.binding.deconz.internal.types.ResourceType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.HSBType;
Expand All @@ -36,8 +37,6 @@
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.types.Command;
import org.openhab.core.types.CommandDescriptionBuilder;
import org.openhab.core.types.CommandOption;
import org.openhab.core.types.RefreshType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -56,12 +55,13 @@
public class GroupThingHandler extends DeconzBaseThingHandler {
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPE_UIDS = Set.of(THING_TYPE_LIGHTGROUP);
private final Logger logger = LoggerFactory.getLogger(GroupThingHandler.class);
private final CommandDescriptionProvider commandDescriptionProvider;
private final DeconzDynamicCommandDescriptionProvider commandDescriptionProvider;

private Map<String, String> scenes = Map.of();
private GroupState groupStateCache = new GroupState();

public GroupThingHandler(Thing thing, Gson gson, CommandDescriptionProvider commandDescriptionProvider) {
public GroupThingHandler(Thing thing, Gson gson,
DeconzDynamicCommandDescriptionProvider commandDescriptionProvider) {
super(thing, gson, ResourceType.GROUPS);
this.commandDescriptionProvider = commandDescriptionProvider;
}
Expand Down Expand Up @@ -142,10 +142,8 @@ protected void processStateResponse(DeconzBaseMessage stateResponse) {
GroupMessage groupMessage = (GroupMessage) stateResponse;
scenes = groupMessage.scenes.stream().collect(Collectors.toMap(scene -> scene.name, scene -> scene.id));
ChannelUID channelUID = new ChannelUID(thing.getUID(), CHANNEL_SCENE);
commandDescriptionProvider.setDescription(channelUID,
CommandDescriptionBuilder.create().withCommandOptions(groupMessage.scenes.stream()
.map(scene -> new CommandOption(scene.name, scene.name)).collect(Collectors.toList()))
.build());
commandDescriptionProvider.setCommandOptions(channelUID,
groupMessage.scenes.stream().map(Scene::toCommandOption).collect(Collectors.toList()));

}
messageReceived(config.id, stateResponse);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.deconz.internal.CommandDescriptionProvider;
import org.openhab.binding.deconz.internal.StateDescriptionProvider;
import org.openhab.binding.deconz.internal.DeconzDynamicCommandDescriptionProvider;
import org.openhab.binding.deconz.internal.DeconzDynamicStateDescriptionProvider;
import org.openhab.binding.deconz.internal.Util;
import org.openhab.binding.deconz.internal.dto.DeconzBaseMessage;
import org.openhab.binding.deconz.internal.dto.LightMessage;
Expand All @@ -49,10 +49,9 @@
import org.openhab.core.thing.binding.builder.ChannelBuilder;
import org.openhab.core.thing.binding.builder.ThingBuilder;
import org.openhab.core.types.Command;
import org.openhab.core.types.CommandDescriptionBuilder;
import org.openhab.core.types.CommandOption;
import org.openhab.core.types.RefreshType;
import org.openhab.core.types.StateDescription;
import org.openhab.core.types.StateDescriptionFragment;
import org.openhab.core.types.StateDescriptionFragmentBuilder;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
Expand Down Expand Up @@ -85,8 +84,8 @@ public class LightThingHandler extends DeconzBaseThingHandler {

private final Logger logger = LoggerFactory.getLogger(LightThingHandler.class);

private final StateDescriptionProvider stateDescriptionProvider;
private final CommandDescriptionProvider commandDescriptionProvider;
private final DeconzDynamicStateDescriptionProvider stateDescriptionProvider;
private final DeconzDynamicCommandDescriptionProvider commandDescriptionProvider;

private long lastCommandExpireTimestamp = 0;
private boolean needsPropertyUpdate = false;
Expand All @@ -104,8 +103,8 @@ public class LightThingHandler extends DeconzBaseThingHandler {
private int ctMax = ZCL_CT_MAX;
private int ctMin = ZCL_CT_MIN;

public LightThingHandler(Thing thing, Gson gson, StateDescriptionProvider stateDescriptionProvider,
CommandDescriptionProvider commandDescriptionProvider) {
public LightThingHandler(Thing thing, Gson gson, DeconzDynamicStateDescriptionProvider stateDescriptionProvider,
DeconzDynamicCommandDescriptionProvider commandDescriptionProvider) {
super(thing, gson, ResourceType.LIGHTS);
this.stateDescriptionProvider = stateDescriptionProvider;
this.commandDescriptionProvider = commandDescriptionProvider;
Expand All @@ -123,15 +122,11 @@ public void initialize() {
ctMin = ctMinString == null ? ZCL_CT_MIN : Integer.parseInt(ctMinString);

// minimum and maximum are inverted due to mired/kelvin conversion!
StateDescription stateDescription = StateDescriptionFragmentBuilder.create()
StateDescriptionFragment stateDescriptionFragment = StateDescriptionFragmentBuilder.create()
.withMinimum(new BigDecimal(miredToKelvin(ctMax)))
.withMaximum(new BigDecimal(miredToKelvin(ctMin))).build().toStateDescription();
if (stateDescription != null) {
stateDescriptionProvider.setDescription(new ChannelUID(thing.getUID(), CHANNEL_COLOR_TEMPERATURE),
stateDescription);
} else {
logger.warn("Failed to create state description in thing {}", thing.getUID());
}
.withMaximum(new BigDecimal(miredToKelvin(ctMin))).build();
stateDescriptionProvider.setDescriptionFragment(
new ChannelUID(thing.getUID(), CHANNEL_COLOR_TEMPERATURE), stateDescriptionFragment);
} catch (NumberFormatException e) {
needsPropertyUpdate = true;
}
Expand Down Expand Up @@ -370,20 +365,16 @@ private void checkAndUpdateEffectChannels(LightMessage lightMessage) {
List<String> options = List.of("none", "steady", "snow", "rainbow", "snake", "tinkle", "fireworks",
"flag", "waves", "updown", "vintage", "fading", "collide", "strobe", "sparkles", "carnival",
"glow");
commandDescriptionProvider.setDescription(effectChannelUID,
CommandDescriptionBuilder.create().withCommandOptions(toCommandOptionList(options)).build());
commandDescriptionProvider.setCommandOptions(effectChannelUID, toCommandOptionList(options));
break;
case TINT_MUELLER:
options = List.of("none", "colorloop", "sunset", "party", "worklight", "campfire", "romance",
"nightlight");
commandDescriptionProvider.setDescription(effectChannelUID,
CommandDescriptionBuilder.create().withCommandOptions(toCommandOptionList(options)).build());
commandDescriptionProvider.setCommandOptions(effectChannelUID, toCommandOptionList(options));
break;
default:
options = List.of("none", "colorloop");
commandDescriptionProvider.setDescription(effectChannelUID,
CommandDescriptionBuilder.create().withCommandOptions(toCommandOptionList(options)).build());

commandDescriptionProvider.setCommandOptions(effectChannelUID, toCommandOptionList(options));
}
}

Expand Down
Loading

0 comments on commit 225e2ae

Please sign in to comment.