Added 'ChannelDescriptionChangedEvent' (#1505)

Signed-off-by: Christoph Weitkamp <github@christophweitkamp.de>
This commit is contained in:
Christoph Weitkamp
2021-06-21 13:03:37 +02:00
committed by GitHub
parent 1f2beea19e
commit 556d3498c1
19 changed files with 1150 additions and 96 deletions
@@ -17,12 +17,14 @@ import static org.openhab.core.magic.binding.MagicBindingConstants.*;
import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.magic.binding.internal.MagicDynamicCommandDescriptionProvider;
import org.openhab.core.magic.binding.internal.MagicDynamicStateDescriptionProvider;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.types.Command;
import org.openhab.core.types.CommandOption;
import org.openhab.core.types.StateOption;
/**
@@ -39,23 +41,26 @@ public class MagicDynamicStateDescriptionThingHandler extends BaseThingHandler {
private static final String SYSTEM_COMMAND_SUSPEND = "Suspend";
private static final String SYSTEM_COMMAND_QUIT = "Quit";
private final MagicDynamicCommandDescriptionProvider commandDescriptionProvider;
private final MagicDynamicStateDescriptionProvider stateDescriptionProvider;
public MagicDynamicStateDescriptionThingHandler(Thing thing,
MagicDynamicCommandDescriptionProvider commandDescriptionProvider,
MagicDynamicStateDescriptionProvider stateDescriptionProvider) {
super(thing);
this.commandDescriptionProvider = commandDescriptionProvider;
this.stateDescriptionProvider = stateDescriptionProvider;
}
@Override
public void initialize() {
ChannelUID systemCommandChannelUID = new ChannelUID(getThing().getUID(), CHANNEL_SYSTEM_COMMAND);
stateDescriptionProvider.setStateOptions(systemCommandChannelUID,
List.of(new StateOption(SYSTEM_COMMAND_HIBERNATE, SYSTEM_COMMAND_HIBERNATE),
new StateOption(SYSTEM_COMMAND_REBOOT, SYSTEM_COMMAND_REBOOT),
new StateOption(SYSTEM_COMMAND_SHUTDOWN, SYSTEM_COMMAND_SHUTDOWN),
new StateOption(SYSTEM_COMMAND_SUSPEND, SYSTEM_COMMAND_SUSPEND),
new StateOption(SYSTEM_COMMAND_QUIT, SYSTEM_COMMAND_QUIT)));
commandDescriptionProvider.setCommandOptions(systemCommandChannelUID,
List.of(new CommandOption(SYSTEM_COMMAND_HIBERNATE, SYSTEM_COMMAND_HIBERNATE),
new CommandOption(SYSTEM_COMMAND_REBOOT, SYSTEM_COMMAND_REBOOT),
new CommandOption(SYSTEM_COMMAND_SHUTDOWN, SYSTEM_COMMAND_SHUTDOWN),
new CommandOption(SYSTEM_COMMAND_SUSPEND, SYSTEM_COMMAND_SUSPEND),
new CommandOption(SYSTEM_COMMAND_QUIT, SYSTEM_COMMAND_QUIT)));
ChannelUID signalStrengthChannelUID = new ChannelUID(getThing().getUID(), CHANNEL_SIGNAL_STRENGTH);
stateDescriptionProvider.setStateOptions(signalStrengthChannelUID, List.of(new StateOption("1", "Unusable"),
@@ -0,0 +1,42 @@
/**
* 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.core.magic.binding.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.events.EventPublisher;
import org.openhab.core.thing.binding.BaseDynamicCommandDescriptionProvider;
import org.openhab.core.thing.i18n.ChannelTypeI18nLocalizationService;
import org.openhab.core.thing.link.ItemChannelLinkRegistry;
import org.openhab.core.thing.type.DynamicCommandDescriptionProvider;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
/**
* Dynamic provider of command options.
*
* @author Christoph Weitkamp - Initial contribution
*/
@Component(service = { DynamicCommandDescriptionProvider.class, MagicDynamicCommandDescriptionProvider.class })
@NonNullByDefault
public class MagicDynamicCommandDescriptionProvider extends BaseDynamicCommandDescriptionProvider {
@Activate
public MagicDynamicCommandDescriptionProvider(final @Reference EventPublisher eventPublisher, //
final @Reference ChannelTypeI18nLocalizationService channelTypeI18nLocalizationService, //
final @Reference ItemChannelLinkRegistry itemChannelLinkRegistry) {
this.eventPublisher = eventPublisher;
this.channelTypeI18nLocalizationService = channelTypeI18nLocalizationService;
this.itemChannelLinkRegistry = itemChannelLinkRegistry;
}
}
@@ -13,9 +13,12 @@
package org.openhab.core.magic.binding.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.events.EventPublisher;
import org.openhab.core.thing.binding.BaseDynamicStateDescriptionProvider;
import org.openhab.core.thing.i18n.ChannelTypeI18nLocalizationService;
import org.openhab.core.thing.link.ItemChannelLinkRegistry;
import org.openhab.core.thing.type.DynamicStateDescriptionProvider;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
@@ -28,14 +31,12 @@ import org.osgi.service.component.annotations.Reference;
@NonNullByDefault
public class MagicDynamicStateDescriptionProvider extends BaseDynamicStateDescriptionProvider {
@Reference
protected void setChannelTypeI18nLocalizationService(
final ChannelTypeI18nLocalizationService channelTypeI18nLocalizationService) {
@Activate
public MagicDynamicStateDescriptionProvider(final @Reference EventPublisher eventPublisher, //
final @Reference ChannelTypeI18nLocalizationService channelTypeI18nLocalizationService, //
final @Reference ItemChannelLinkRegistry itemChannelLinkRegistry) {
this.eventPublisher = eventPublisher;
this.channelTypeI18nLocalizationService = channelTypeI18nLocalizationService;
}
protected void unsetChannelTypeI18nLocalizationService(
final ChannelTypeI18nLocalizationService channelTypeI18nLocalizationService) {
this.channelTypeI18nLocalizationService = null;
this.itemChannelLinkRegistry = itemChannelLinkRegistry;
}
}
@@ -63,10 +63,13 @@ public class MagicHandlerFactory extends BaseThingHandlerFactory {
THING_TYPE_CHATTY_THING, THING_TYPE_ROLLERSHUTTER, THING_TYPE_PLAYER, THING_TYPE_IMAGE,
THING_TYPE_ACTION_MODULE, THING_TYPE_DYNAMIC_STATE_DESCRIPTION, THING_TYPE_ONLINE_OFFLINE);
private final MagicDynamicCommandDescriptionProvider commandDescriptionProvider;
private final MagicDynamicStateDescriptionProvider stateDescriptionProvider;
@Activate
public MagicHandlerFactory(final @Reference MagicDynamicStateDescriptionProvider stateDescriptionProvider) {
public MagicHandlerFactory(final @Reference MagicDynamicCommandDescriptionProvider commandDescriptionProvider, //
final @Reference MagicDynamicStateDescriptionProvider stateDescriptionProvider) {
this.commandDescriptionProvider = commandDescriptionProvider;
this.stateDescriptionProvider = stateDescriptionProvider;
}
@@ -128,7 +131,8 @@ public class MagicHandlerFactory extends BaseThingHandlerFactory {
return new MagicActionModuleThingHandler(thing);
}
if (THING_TYPE_DYNAMIC_STATE_DESCRIPTION.equals(thingTypeUID)) {
return new MagicDynamicStateDescriptionThingHandler(thing, stateDescriptionProvider);
return new MagicDynamicStateDescriptionThingHandler(thing, commandDescriptionProvider,
stateDescriptionProvider);
}
if (THING_TYPE_ONLINE_OFFLINE.equals(thingTypeUID)) {
return new MagicOnlineOfflineHandler(thing);
@@ -38,7 +38,8 @@ public class MagicHandlerFactoryTest {
@BeforeEach
public void setup() {
factory = new MagicHandlerFactory(mock(MagicDynamicStateDescriptionProvider.class));
factory = new MagicHandlerFactory(mock(MagicDynamicCommandDescriptionProvider.class),
mock(MagicDynamicStateDescriptionProvider.class));
}
@Test
@@ -0,0 +1,78 @@
/**
* 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.core.thing.binding;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.events.Event;
import org.openhab.core.events.EventPublisher;
import org.openhab.core.thing.events.ChannelDescriptionChangedEvent;
import org.openhab.core.thing.i18n.ChannelTypeI18nLocalizationService;
import org.openhab.core.thing.link.ItemChannelLinkRegistry;
import org.osgi.framework.BundleContext;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Deactivate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link AbstractDynamicDescriptionProvider} provides a base implementation for dynamic description providers.
* <p>
* It holds a reference to the {@link ChannelTypeI18nLocalizationService} to provide localized descriptions. Therefore
* the inheriting class has to request a reference for the {@link ChannelTypeI18nLocalizationService} on its own.
* <p>
* It posts {@link ChannelDescriptionChangedEvent}s through the openHAB events bus about a changed dynamic description.
* Therefore the subclass has to request references for the {@link EventPublisher} and
* {@link ItemChannelLinkRegistry}.
*
* @author Christoph Weitkamp - Initial contribution
*/
@NonNullByDefault
public abstract class AbstractDynamicDescriptionProvider {
private final Logger logger = LoggerFactory.getLogger(getClass());
protected @NonNullByDefault({}) BundleContext bundleContext;
protected @Nullable EventPublisher eventPublisher;
protected @Nullable ChannelTypeI18nLocalizationService channelTypeI18nLocalizationService;
protected @Nullable ItemChannelLinkRegistry itemChannelLinkRegistry;
/**
* This method can be used in a subclass in order to post events through the openHAB events bus. A common use case
* is to notify event subscribers about a changed dynamic description.
*
* @param event the {@link Event}
*/
protected void postEvent(Event event) {
if (eventPublisher != null) {
try {
eventPublisher.post(event);
} catch (RuntimeException e) {
logger.error("Cannot post '{}' event: {}", event.getType(), e.getMessage(), e);
}
} else {
logger.debug("Cannot post event as EventPublisher is missing");
}
}
@Activate
protected void activate(ComponentContext componentContext) {
bundleContext = componentContext.getBundleContext();
}
@Deactivate
public void deactivate() {
bundleContext = null;
}
}
@@ -15,12 +15,14 @@ package org.openhab.core.thing.binding;
import java.util.List;
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.events.ThingEventFactory;
import org.openhab.core.thing.i18n.ChannelTypeI18nLocalizationService;
import org.openhab.core.thing.type.ChannelType;
import org.openhab.core.thing.type.ChannelTypeUID;
@@ -28,37 +30,40 @@ import org.openhab.core.thing.type.DynamicCommandDescriptionProvider;
import org.openhab.core.types.CommandDescription;
import org.openhab.core.types.CommandDescriptionBuilder;
import org.openhab.core.types.CommandOption;
import org.osgi.framework.BundleContext;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Deactivate;
/**
* The {@link BaseDynamicCommandDescriptionProvider} provides a base implementation for the
* {@link DynamicCommandDescriptionProvider}.
* <p>
* It provides localized command options. Therefore the inheriting class has to request the reference for the
* {@link ChannelTypeI18nLocalizationService} on its own.
* It provides localized dynamic {@link CommandOption}s. Therefore the inheriting class has to request a reference for
* the {@link ChannelTypeI18nLocalizationService} on its own.
*
* @author Christoph Weitkamp - Initial contribution
* @author Christoph Weitkamp - Added ChannelStateDescriptionChangedEvent
*/
@NonNullByDefault
public abstract class BaseDynamicCommandDescriptionProvider implements DynamicCommandDescriptionProvider {
private @NonNullByDefault({}) BundleContext bundleContext;
protected @NonNullByDefault({}) ChannelTypeI18nLocalizationService channelTypeI18nLocalizationService;
public abstract class BaseDynamicCommandDescriptionProvider extends AbstractDynamicDescriptionProvider
implements DynamicCommandDescriptionProvider {
protected final Map<ChannelUID, List<CommandOption>> channelOptionsMap = new ConcurrentHashMap<>();
/**
* For a given channel UID, set a {@link List} of {@link CommandOption}s that should be used for the channel,
* For a given {@link ChannelUID}, set a {@link List} of {@link CommandOption}s that should be used for the channel,
* instead of the one defined statically in the {@link ChannelType}.
*
* @param channelUID the channel UID of the channel
* @param channelUID the {@link ChannelUID} of the channel
* @param options a {@link List} of {@link CommandOption}s
*/
public void setCommandOptions(ChannelUID channelUID, List<CommandOption> options) {
channelOptionsMap.put(channelUID, options);
List<CommandOption> oldOptions = channelOptionsMap.get(channelUID);
if (!options.equals(oldOptions)) {
channelOptionsMap.put(channelUID, options);
postEvent(ThingEventFactory.createChannelDescriptionCommandOptionsChangedEvent(channelUID,
itemChannelLinkRegistry != null ? itemChannelLinkRegistry.getLinkedItemNames(channelUID) : Set.of(),
options, oldOptions));
}
}
@Override
@@ -92,14 +97,10 @@ public abstract class BaseDynamicCommandDescriptionProvider implements DynamicCo
return options;
}
@Activate
protected void activate(ComponentContext componentContext) {
bundleContext = componentContext.getBundleContext();
}
@Override
@Deactivate
public void deactivate() {
channelOptionsMap.clear();
bundleContext = null;
super.deactivate();
}
}
@@ -15,12 +15,14 @@ package org.openhab.core.thing.binding;
import java.util.List;
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.events.ThingEventFactory;
import org.openhab.core.thing.i18n.ChannelTypeI18nLocalizationService;
import org.openhab.core.thing.type.ChannelType;
import org.openhab.core.thing.type.ChannelTypeUID;
@@ -28,50 +30,58 @@ import org.openhab.core.thing.type.DynamicStateDescriptionProvider;
import org.openhab.core.types.StateDescription;
import org.openhab.core.types.StateDescriptionFragmentBuilder;
import org.openhab.core.types.StateOption;
import org.osgi.framework.BundleContext;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Deactivate;
/**
* The {@link BaseDynamicStateDescriptionProvider} provides a base implementation for the
* {@link DynamicStateDescriptionProvider}.
* <p>
* It provides localized state patterns and dynamic state options while leaving other state description fields as
* original. Therefore the inheriting class has to request the reference for the
* It provides localized patterns and dynamic {@link StateOption}s while leaving other {@link StateDescription} fields
* as original. Therefore the inheriting class has to request a reference for the
* {@link ChannelTypeI18nLocalizationService} on its own.
*
* @author Christoph Weitkamp - Initial contribution
* @author Christoph Weitkamp - Added ChannelStateDescriptionChangedEvent
*/
@NonNullByDefault
public abstract class BaseDynamicStateDescriptionProvider implements DynamicStateDescriptionProvider {
private @NonNullByDefault({}) BundleContext bundleContext;
protected @NonNullByDefault({}) ChannelTypeI18nLocalizationService channelTypeI18nLocalizationService;
public abstract class BaseDynamicStateDescriptionProvider extends AbstractDynamicDescriptionProvider
implements DynamicStateDescriptionProvider {
protected final Map<ChannelUID, String> channelPatternMap = new ConcurrentHashMap<>();
protected final Map<ChannelUID, List<StateOption>> channelOptionsMap = new ConcurrentHashMap<>();
/**
* For a given channel UID, set a pattern that should be used for the channel, instead of the one defined statically
* in the {@link ChannelType}.
* For a given {@link ChannelUID}, set a pattern that should be used for the channel, instead of the one defined
* statically in the {@link ChannelType}.
*
* @param channelUID the channel UID of the channel
* @param channelUID the {@link ChannelUID} of the channel
* @param pattern a pattern
*/
public void setStatePattern(ChannelUID channelUID, String pattern) {
channelPatternMap.put(channelUID, pattern);
String oldPattern = channelPatternMap.get(channelUID);
if (!pattern.equals(oldPattern)) {
channelPatternMap.put(channelUID, pattern);
postEvent(ThingEventFactory.createChannelDescriptionPatternChangedEvent(channelUID,
itemChannelLinkRegistry != null ? itemChannelLinkRegistry.getLinkedItemNames(channelUID) : Set.of(),
pattern, oldPattern));
}
}
/**
* For a given channel UID, set a {@link List} of {@link StateOption}s that should be used for the channel, instead
* of the one defined statically in the {@link ChannelType}.
* For a given {@link ChannelUID}, set a {@link List} of {@link StateOption}s that should be used for the channel,
* instead of the one defined statically in the {@link ChannelType}.
*
* @param channelUID the channel UID of the channel
* @param channelUID the {@link ChannelUID} of the channel
* @param options a {@link List} of {@link StateOption}s
*/
public void setStateOptions(ChannelUID channelUID, List<StateOption> options) {
channelOptionsMap.put(channelUID, options);
List<StateOption> oldOptions = channelOptionsMap.get(channelUID);
if (!options.equals(oldOptions)) {
channelOptionsMap.put(channelUID, options);
postEvent(ThingEventFactory.createChannelDescriptionStateOptionsChangedEvent(channelUID,
itemChannelLinkRegistry != null ? itemChannelLinkRegistry.getLinkedItemNames(channelUID) : Set.of(),
options, oldOptions));
}
}
@Override
@@ -139,14 +149,11 @@ public abstract class BaseDynamicStateDescriptionProvider implements DynamicStat
return options;
}
@Activate
protected void activate(ComponentContext componentContext) {
bundleContext = componentContext.getBundleContext();
}
@Override
@Deactivate
public void deactivate() {
channelPatternMap.clear();
channelOptionsMap.clear();
bundleContext = null;
super.deactivate();
}
}
@@ -34,8 +34,8 @@ public abstract class AbstractThingRegistryEvent extends AbstractEvent {
*
* @param topic the topic
* @param payload the payload
* @param source the source, can be null
* @param thing the thing
* @param source the source
* @param thing the thing data transfer object
*/
protected AbstractThingRegistryEvent(String topic, String payload, @Nullable String source, ThingDTO thing) {
super(topic, payload, source);
@@ -43,9 +43,9 @@ public abstract class AbstractThingRegistryEvent extends AbstractEvent {
}
/**
* Gets the thing.
* Gets the thing data transfer object.
*
* @return the thing
* @return the thing data transfer object
*/
public ThingDTO getThing() {
return thing;
@@ -0,0 +1,149 @@
/**
* 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.core.thing.events;
import java.util.Set;
import java.util.stream.Collectors;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.events.AbstractEvent;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.types.CommandDescription;
import org.openhab.core.types.StateDescription;
/**
* {@link ChannelDescriptionChangedEvent}s will be delivered through the openHAB event bus if the
* {@link CommandDescription} or {@link StateDescription} of a channel has changed. Instances must be created with the
* {@link ThingEventFactory}.
*
* @author Christoph Weitkamp - Initial contribution
*/
@NonNullByDefault
public class ChannelDescriptionChangedEvent extends AbstractEvent {
public enum CommonChannelDescriptionField {
ALL,
COMMAND_OPTIONS,
PATTERN,
STATE_OPTIONS
};
/**
* The channel description changed event type.
*/
public static final String TYPE = ChannelDescriptionChangedEvent.class.getSimpleName();
/**
* The changed field.
*/
private CommonChannelDescriptionField field;
/**
* The channel which triggered the event.
*/
private final ChannelUID channelUID;
/**
* A {@link Set} of linked item names.
*/
private final Set<String> linkedItemNames;
/**
* The new value (represented as JSON string).
*/
private final String value;
/**
* The old value (represented as JSON string).
*/
private final @Nullable String oldValue;
/**
* Creates a new instance.
*
* @param topic the topic
* @param payload the payload
* @param field the changed field
* @param channelUID the {@link ChannelUID}
* @param linkedItemNames a {@link Set} of linked item names
* @param value the new value (represented as JSON string)
* @param oldValue the old value represented as JSON string)
*/
protected ChannelDescriptionChangedEvent(String topic, String payload, CommonChannelDescriptionField field,
ChannelUID channelUID, Set<String> linkedItemNames, String value, @Nullable String oldValue) {
super(topic, payload, null);
this.field = field;
this.channelUID = channelUID;
this.linkedItemNames = linkedItemNames;
this.value = value;
this.oldValue = oldValue;
}
@Override
public String getType() {
return TYPE;
}
/**
* Gets the changed field.
*
* @return the changed field
*/
public CommonChannelDescriptionField getField() {
return field;
}
/**
* Gets the {@link ChannelUID}.
*
* @return the {@link ChannelUID}
*/
public ChannelUID getChannelUID() {
return channelUID;
}
/**
* Gets the linked item names.
*
* @return a {@link Set} of linked item names
*/
public Set<String> getLinkedItemNames() {
return linkedItemNames;
}
/**
* Gets the new value (represented as JSON string).
*
* @return the new value.
*/
public String getValue() {
return value;
}
/**
* Gets the old value (represented as JSON string).
*
* @return the old value.
*/
public @Nullable String getOldValue() {
return oldValue;
}
@Override
public String toString() {
return String.format(
"Description for field '%s' of channel '%s' changed from '%s' to '%s' for linked items: %s", field,
channelUID, oldValue, value, linkedItemNames.stream().collect(Collectors.joining(",", "[", "]")));
}
}
@@ -58,6 +58,11 @@ public class ChannelTriggeredEvent extends AbstractEvent {
this.channel = channel;
}
@Override
public String getType() {
return TYPE;
}
/**
* Returns the event.
*
@@ -74,13 +79,8 @@ public class ChannelTriggeredEvent extends AbstractEvent {
return channel;
}
@Override
public String getType() {
return TYPE;
}
@Override
public String toString() {
return channel + " triggered " + event;
return String.format("%s triggered %s", channel, event);
}
}
@@ -27,6 +27,9 @@ import org.openhab.core.thing.ThingStatusInfo;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.dto.ThingDTO;
import org.openhab.core.thing.dto.ThingDTOMapper;
import org.openhab.core.thing.events.ChannelDescriptionChangedEvent.CommonChannelDescriptionField;
import org.openhab.core.types.CommandOption;
import org.openhab.core.types.StateOption;
import org.osgi.service.component.annotations.Component;
/**
@@ -37,6 +40,7 @@ import org.osgi.service.component.annotations.Component;
*
* @author Stefan Bußweiler - Initial contribution
* @author Dennis Nobel - Added status changed event
* @author Christoph Weitkamp - Added ChannelDescriptionChangedEvent
*/
@Component(immediate = true, service = EventFactory.class)
@NonNullByDefault
@@ -51,6 +55,8 @@ public class ThingEventFactory extends AbstractEventFactory {
static final String THING_UPDATED_EVENT_TOPIC = "openhab/things/{thingUID}/updated";
static final String CHANNEL_DESCRIPTION_CHANGED_TOPIC = "openhab/channels/{channelUID}/descriptionchanged";
static final String CHANNEL_TRIGGERED_EVENT_TOPIC = "openhab/channels/{channelUID}/triggered";
/**
@@ -58,7 +64,8 @@ public class ThingEventFactory extends AbstractEventFactory {
*/
public ThingEventFactory() {
super(Set.of(ThingStatusInfoEvent.TYPE, ThingStatusInfoChangedEvent.TYPE, ThingAddedEvent.TYPE,
ThingRemovedEvent.TYPE, ThingUpdatedEvent.TYPE, ChannelTriggeredEvent.TYPE));
ThingRemovedEvent.TYPE, ThingUpdatedEvent.TYPE, ChannelDescriptionChangedEvent.TYPE,
ChannelTriggeredEvent.TYPE));
}
@Override
@@ -74,12 +81,184 @@ public class ThingEventFactory extends AbstractEventFactory {
return createRemovedEvent(topic, payload);
} else if (ThingUpdatedEvent.TYPE.equals(eventType)) {
return createUpdatedEvent(topic, payload);
} else if (ChannelDescriptionChangedEvent.TYPE.equals(eventType)) {
return createChannelDescriptionChangedEvent(topic, payload);
} else if (ChannelTriggeredEvent.TYPE.equals(eventType)) {
return createTriggerEvent(topic, payload, source);
}
throw new IllegalArgumentException("The event type '" + eventType + "' is not supported by this factory.");
}
public static class ChannelDescriptionChangedEventPayloadBean {
public @NonNullByDefault({}) CommonChannelDescriptionField field;
public @NonNullByDefault({}) String channelUID;
public Set<String> linkedItemNames = Set.of();
public @NonNullByDefault({}) String value;
public @Nullable String oldValue;
/**
* Default constructor for deserialization e.g. by Gson.
*/
protected ChannelDescriptionChangedEventPayloadBean() {
}
public ChannelDescriptionChangedEventPayloadBean(CommonChannelDescriptionField field, String channelUID,
Set<String> linkedItemNames, String value, @Nullable String oldValue) {
this.field = field;
this.channelUID = channelUID;
this.linkedItemNames = linkedItemNames;
this.value = value;
this.oldValue = oldValue;
}
}
public static interface CommonChannelDescriptionFieldPayloadBean {
}
public static class ChannelDescriptionPatternPayloadBean implements CommonChannelDescriptionFieldPayloadBean {
public @NonNullByDefault({}) String pattern;
/**
* Default constructor for deserialization e.g. by Gson.
*/
protected ChannelDescriptionPatternPayloadBean() {
}
public ChannelDescriptionPatternPayloadBean(String pattern) {
this.pattern = pattern;
}
}
public static class ChannelDescriptionStateOptionsPayloadBean implements CommonChannelDescriptionFieldPayloadBean {
public @NonNullByDefault({}) List<StateOption> options;
/**
* Default constructor for deserialization e.g. by Gson.
*/
protected ChannelDescriptionStateOptionsPayloadBean() {
}
public ChannelDescriptionStateOptionsPayloadBean(List<StateOption> options) {
this.options = options;
}
}
public static class ChannelDescriptionCommandOptionsPayloadBean
implements CommonChannelDescriptionFieldPayloadBean {
public @NonNullByDefault({}) List<CommandOption> options;
/**
* Default constructor for deserialization e.g. by Gson.
*/
protected ChannelDescriptionCommandOptionsPayloadBean() {
}
public ChannelDescriptionCommandOptionsPayloadBean(List<CommandOption> options) {
this.options = options;
}
}
/**
* Creates a {@link ChannelDescriptionChangedEvent} for a changed pattern. New and optional old value will be
* serialized to a JSON string from the {@link ChannelDescriptionPatternPayloadBean} object.
*
* @param channelUID the {@link ChannelUID}
* @param linkedItemNames a {@link Set} of linked item names
* @param pattern the new pattern
* @param oldPattern the old pattern
* @return Created {@link ChannelDescriptionChangedEvent}
*/
public static ChannelDescriptionChangedEvent createChannelDescriptionPatternChangedEvent(ChannelUID channelUID,
Set<String> linkedItemNames, String pattern, @Nullable String oldPattern) {
checkNotNull(linkedItemNames, "linkedItemNames");
checkNotNull(channelUID, "channelUID");
checkNotNull(pattern, "pattern");
String patternPayload = serializePayload(new ChannelDescriptionPatternPayloadBean(pattern));
String oldPatternPayload = oldPattern != null
? serializePayload(new ChannelDescriptionPatternPayloadBean(oldPattern))
: null;
ChannelDescriptionChangedEventPayloadBean bean = new ChannelDescriptionChangedEventPayloadBean(
CommonChannelDescriptionField.PATTERN, channelUID.getAsString(), linkedItemNames, patternPayload,
oldPatternPayload);
String payload = serializePayload(bean);
String topic = buildTopic(CHANNEL_DESCRIPTION_CHANGED_TOPIC, channelUID);
return new ChannelDescriptionChangedEvent(topic, payload, CommonChannelDescriptionField.PATTERN, channelUID,
linkedItemNames, patternPayload, oldPatternPayload);
}
/**
* Creates a {@link ChannelDescriptionChangedEvent} for changed {@link StateOption}s. New and optional old value
* will be serialized to a JSON string from the {@link ChannelDescriptionStateOptionsPayloadBean} object.
*
* @param channelUID the {@link ChannelUID}
* @param linkedItemNames a {@link Set} of linked item names
* @param options the new {@link StateOption}s
* @param oldOptions the old {@link StateOption}s
* @return Created {@link ChannelDescriptionChangedEvent}
*/
public static ChannelDescriptionChangedEvent createChannelDescriptionStateOptionsChangedEvent(ChannelUID channelUID,
Set<String> linkedItemNames, List<StateOption> options, @Nullable List<StateOption> oldOptions) {
checkNotNull(linkedItemNames, "linkedItemNames");
checkNotNull(channelUID, "channelUID");
checkNotNull(options, "options");
String stateOptionsPayload = serializePayload(new ChannelDescriptionStateOptionsPayloadBean(options));
String oldStateOptionsPayload = oldOptions != null
? serializePayload(new ChannelDescriptionStateOptionsPayloadBean(oldOptions))
: null;
ChannelDescriptionChangedEventPayloadBean bean = new ChannelDescriptionChangedEventPayloadBean(
CommonChannelDescriptionField.STATE_OPTIONS, channelUID.getAsString(), linkedItemNames,
stateOptionsPayload, oldStateOptionsPayload);
String payload = serializePayload(bean);
String topic = buildTopic(CHANNEL_DESCRIPTION_CHANGED_TOPIC, channelUID);
return new ChannelDescriptionChangedEvent(topic, payload, CommonChannelDescriptionField.STATE_OPTIONS,
channelUID, linkedItemNames, stateOptionsPayload, oldStateOptionsPayload);
}
/**
* Creates a {@link ChannelDescriptionChangedEvent} for change {@link CommandOption}s. New and optional old value
* will be serialized to a JSON string from the {@link ChannelDescriptionCommandOptionsPayloadBean} object.
*
* @param channelUID the {@link ChannelUID}
* @param linkedItemNames a {@link Set} of linked item names
* @param options the new {@link CommandOption}s
* @param oldOptions the old {@link CommandOption}s
* @return Created {@link ChannelDescriptionChangedEvent}
*/
public static ChannelDescriptionChangedEvent createChannelDescriptionCommandOptionsChangedEvent(
ChannelUID channelUID, Set<String> linkedItemNames, List<CommandOption> options,
@Nullable List<CommandOption> oldOptions) {
checkNotNull(linkedItemNames, "linkedItemNames");
checkNotNull(channelUID, "channelUID");
checkNotNull(options, "options");
String commandOptionsPayload = serializePayload(new ChannelDescriptionCommandOptionsPayloadBean(options));
String oldCommandOptionsPayload = oldOptions != null
? serializePayload(new ChannelDescriptionCommandOptionsPayloadBean(oldOptions))
: null;
ChannelDescriptionChangedEventPayloadBean bean = new ChannelDescriptionChangedEventPayloadBean(
CommonChannelDescriptionField.COMMAND_OPTIONS, channelUID.getAsString(), linkedItemNames,
commandOptionsPayload, oldCommandOptionsPayload);
String payload = serializePayload(bean);
String topic = buildTopic(CHANNEL_DESCRIPTION_CHANGED_TOPIC, channelUID);
return new ChannelDescriptionChangedEvent(topic, payload, CommonChannelDescriptionField.COMMAND_OPTIONS,
channelUID, linkedItemNames, commandOptionsPayload, oldCommandOptionsPayload);
}
private ChannelDescriptionChangedEvent createChannelDescriptionChangedEvent(String topic, String payload) {
String[] topicElements = getTopicElements(topic);
if (topicElements.length != 4) {
throw new IllegalArgumentException(
"ChannelDescriptionChangedEvent creation failed, invalid topic: " + topic);
}
ChannelUID channelUID = new ChannelUID(topicElements[2]);
ChannelDescriptionChangedEventPayloadBean bean = deserializePayload(payload,
ChannelDescriptionChangedEventPayloadBean.class);
return new ChannelDescriptionChangedEvent(topic, payload, bean.field, channelUID, bean.linkedItemNames,
bean.value, bean.oldValue);
}
/**
* This is a java bean that is used to serialize/deserialize trigger event payload.
*/
@@ -108,11 +287,11 @@ public class ThingEventFactory extends AbstractEventFactory {
}
/**
* Creates a channel triggered event.
* Creates a {@link ChannelTriggeredEvent}
*
* @param event The event
* @param channelUID The channel UID
* @return the created channel triggered event
* @param event the event
* @param channel the {@link ChannelUID}
* @return Created {@link ChannelTriggeredEvent}
*/
public static ChannelTriggeredEvent createTriggerEvent(String event, ChannelUID channelUID) {
checkNotNull(channelUID, "channelUID");
@@ -218,7 +397,7 @@ public class ThingEventFactory extends AbstractEventFactory {
* @throws IllegalArgumentException if thing is null
*/
public static ThingAddedEvent createAddedEvent(Thing thing) {
assertValidArgument(thing);
assertValidThing(thing);
String topic = buildTopic(THING_ADDED_EVENT_TOPIC, thing.getUID());
ThingDTO thingDTO = map(thing);
String payload = serializePayload(thingDTO);
@@ -233,7 +412,7 @@ public class ThingEventFactory extends AbstractEventFactory {
* @throws IllegalArgumentException if thing is null
*/
public static ThingRemovedEvent createRemovedEvent(Thing thing) {
assertValidArgument(thing);
assertValidThing(thing);
String topic = buildTopic(THING_REMOVED_EVENT_TOPIC, thing.getUID());
ThingDTO thingDTO = map(thing);
String payload = serializePayload(thingDTO);
@@ -249,8 +428,8 @@ public class ThingEventFactory extends AbstractEventFactory {
* @throws IllegalArgumentException if thing or oldThing is null
*/
public static ThingUpdatedEvent createUpdateEvent(Thing thing, Thing oldThing) {
assertValidArgument(thing);
assertValidArgument(oldThing);
assertValidThing(thing);
assertValidThing(oldThing);
String topic = buildTopic(THING_UPDATED_EVENT_TOPIC, thing.getUID());
ThingDTO thingDTO = map(thing);
ThingDTO oldThingDTO = map(oldThing);
@@ -261,7 +440,7 @@ public class ThingEventFactory extends AbstractEventFactory {
return new ThingUpdatedEvent(topic, payload, thingDTO, oldThingDTO);
}
private static void assertValidArgument(Thing thing) {
private static void assertValidThing(Thing thing) {
checkNotNull(thing, "thing");
checkNotNull(thing.getUID(), "thingUID of the thing");
}
@@ -0,0 +1,94 @@
/**
* 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.core.thing.binding;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import java.util.List;
import java.util.Set;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
import org.openhab.core.events.Event;
import org.openhab.core.events.EventPublisher;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.events.ChannelDescriptionChangedEvent;
import org.openhab.core.thing.events.ChannelDescriptionChangedEvent.CommonChannelDescriptionField;
import org.openhab.core.thing.link.ItemChannelLinkRegistry;
import org.openhab.core.types.CommandOption;
import org.osgi.framework.BundleContext;
/**
* Tests for {@link BaseDynamicCommandDescriptionProvider}.
*
* @author Christoph Weitkamp - Initial contribution
*/
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.WARN)
class BaseDynamicCommandDescriptionProviderTest {
private static final ThingTypeUID THING_TYPE_UID = new ThingTypeUID("binding:type");
private static final ThingUID THING_UID = new ThingUID(THING_TYPE_UID, "id");
private static final ChannelUID CHANNEL_UID = new ChannelUID(THING_UID, "channel");
@Mock
EventPublisher mockEventPublisher;
@Mock
ItemChannelLinkRegistry mockItemChannelLinkRegistry;
class TestDynamicCommandDescriptionProvider extends BaseDynamicCommandDescriptionProvider {
public TestDynamicCommandDescriptionProvider() {
this.bundleContext = mock(BundleContext.class);
this.eventPublisher = mockEventPublisher;
this.itemChannelLinkRegistry = mockItemChannelLinkRegistry;
}
};
private TestDynamicCommandDescriptionProvider subject;
@BeforeEach
public void setup() {
when(mockItemChannelLinkRegistry.getLinkedItemNames(CHANNEL_UID)).thenReturn(Set.of("item1", "item2"));
subject = new TestDynamicCommandDescriptionProvider();
}
@Test
public void setCommandOptionsPublishesEvent() {
subject.setCommandOptions(CHANNEL_UID, List.of(new CommandOption("reboot", "Reboot")));
ArgumentCaptor<Event> capture = ArgumentCaptor.forClass(Event.class);
verify(mockEventPublisher, times(1)).post(capture.capture());
Event event = capture.getValue();
assertTrue(event instanceof ChannelDescriptionChangedEvent);
ChannelDescriptionChangedEvent cdce = (ChannelDescriptionChangedEvent) event;
assertEquals(CommonChannelDescriptionField.COMMAND_OPTIONS, cdce.getField());
// check the event is not published again
subject.setCommandOptions(CHANNEL_UID, List.of(new CommandOption("reboot", "Reboot")));
verify(mockEventPublisher, times(1)).post(capture.capture());
}
}
@@ -0,0 +1,112 @@
/**
* 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.core.thing.binding;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import java.util.List;
import java.util.Set;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
import org.openhab.core.events.Event;
import org.openhab.core.events.EventPublisher;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.events.ChannelDescriptionChangedEvent;
import org.openhab.core.thing.events.ChannelDescriptionChangedEvent.CommonChannelDescriptionField;
import org.openhab.core.thing.link.ItemChannelLinkRegistry;
import org.openhab.core.types.StateOption;
import org.osgi.framework.BundleContext;
/**
* Tests for {@link BaseDynamicStateDescriptionProvider}.
*
* @author Christoph Weitkamp - Initial contribution
*/
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.WARN)
class BaseDynamicStateDescriptionProviderTest {
private static final ThingTypeUID THING_TYPE_UID = new ThingTypeUID("binding:type");
private static final ThingUID THING_UID = new ThingUID(THING_TYPE_UID, "id");
private static final ChannelUID CHANNEL_UID = new ChannelUID(THING_UID, "channel");
@Mock
EventPublisher mockEventPublisher;
@Mock
ItemChannelLinkRegistry mockItemChannelLinkRegistry;
class TestBaseDynamicStateDescriptionProvider extends BaseDynamicStateDescriptionProvider {
public TestBaseDynamicStateDescriptionProvider() {
this.bundleContext = mock(BundleContext.class);
this.eventPublisher = mockEventPublisher;
this.itemChannelLinkRegistry = mockItemChannelLinkRegistry;
}
};
private BaseDynamicStateDescriptionProvider subject;
@BeforeEach
public void setup() {
when(mockItemChannelLinkRegistry.getLinkedItemNames(CHANNEL_UID)).thenReturn(Set.of("item1", "item2"));
subject = new TestBaseDynamicStateDescriptionProvider();
}
@Test
public void setStatePatternPublishesEvent() {
subject.setStatePattern(CHANNEL_UID, "%s");
ArgumentCaptor<Event> capture = ArgumentCaptor.forClass(Event.class);
verify(mockEventPublisher, times(1)).post(capture.capture());
Event event = capture.getValue();
assertTrue(event instanceof ChannelDescriptionChangedEvent);
ChannelDescriptionChangedEvent cdce = (ChannelDescriptionChangedEvent) event;
assertEquals(CommonChannelDescriptionField.PATTERN, cdce.getField());
// check the event is not published again
subject.setStatePattern(CHANNEL_UID, "%s");
verify(mockEventPublisher, times(1)).post(capture.capture());
}
@Test
public void setStateOptionsPublishesEvent() {
subject.setStateOptions(CHANNEL_UID, List.of(new StateOption("offline", "Offline")));
ArgumentCaptor<Event> capture = ArgumentCaptor.forClass(Event.class);
verify(mockEventPublisher, times(1)).post(capture.capture());
Event event = capture.getValue();
assertTrue(event instanceof ChannelDescriptionChangedEvent);
ChannelDescriptionChangedEvent cdce = (ChannelDescriptionChangedEvent) event;
assertEquals(CommonChannelDescriptionField.STATE_OPTIONS, cdce.getField());
// check the event is not published again
subject.setStateOptions(CHANNEL_UID, List.of(new StateOption("offline", "Offline")));
verify(mockEventPublisher, times(1)).post(capture.capture());
}
}
@@ -14,10 +14,14 @@ package org.openhab.core.thing.events;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.BeforeEach;
import java.util.List;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test;
import org.openhab.core.events.Event;
import org.openhab.core.thing.ChannelUID;
@@ -31,7 +35,12 @@ import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.builder.ThingBuilder;
import org.openhab.core.thing.binding.builder.ThingStatusInfoBuilder;
import org.openhab.core.thing.dto.ThingDTOMapper;
import org.openhab.core.thing.events.ChannelDescriptionChangedEvent.CommonChannelDescriptionField;
import org.openhab.core.thing.events.ThingEventFactory.ChannelDescriptionChangedEventPayloadBean;
import org.openhab.core.thing.events.ThingEventFactory.ChannelDescriptionPatternPayloadBean;
import org.openhab.core.thing.events.ThingEventFactory.ChannelDescriptionStateOptionsPayloadBean;
import org.openhab.core.thing.events.ThingEventFactory.TriggerEventPayloadBean;
import org.openhab.core.types.StateOption;
import com.google.gson.Gson;
@@ -39,12 +48,18 @@ import com.google.gson.Gson;
* {@link ThingEventFactoryTests} tests the {@link ThingEventFactory}.
*
* @author Stefan Bußweiler - Initial contribution
* @author Christoph Weitkamp - Added ChannelStateDescriptionChangedEvent
*/
@NonNullByDefault
public class ThingEventFactoryTest {
private static final Gson JSONCONVERTER = new Gson();
private static final ThingStatusInfo THING_STATUS_INFO = ThingStatusInfoBuilder
.create(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR).withDescription("Some description")
.build();
private final ThingEventFactory factory = new ThingEventFactory();
private static final ThingTypeUID THING_TYPE_UID = new ThingTypeUID("binding:type");
private static final ThingUID THING_UID = new ThingUID(THING_TYPE_UID, "id");
private static final Thing THING = ThingBuilder.create(THING_TYPE_UID, THING_UID).build();
@@ -54,10 +69,35 @@ public class ThingEventFactoryTest {
private static final String THING_ADDED_EVENT_TOPIC = ThingEventFactory.THING_ADDED_EVENT_TOPIC
.replace("{thingUID}", THING_UID.getAsString());
private static final String THING_STATUS_EVENT_PAYLOAD = new Gson().toJson(THING_STATUS_INFO);
private static final String THING_ADDED_EVENT_PAYLOAD = new Gson().toJson(ThingDTOMapper.map(THING));
private static final String THING_STATUS_EVENT_PAYLOAD = JSONCONVERTER.toJson(THING_STATUS_INFO);
private static final String THING_ADDED_EVENT_PAYLOAD = JSONCONVERTER.toJson(ThingDTOMapper.map(THING));
private static final ChannelUID CHANNEL_UID = new ChannelUID(THING_UID, "channel");
private static final String CHANNEL_DESCRIPTION_CHANGED_EVENT_TOPIC = ThingEventFactory.CHANNEL_DESCRIPTION_CHANGED_TOPIC
.replace("{channelUID}", CHANNEL_UID.getAsString());
private static final String CHANNEL_DESCRIPTION_PATTERN_PAYLOAD = JSONCONVERTER
.toJson(new ChannelDescriptionPatternPayloadBean("%s"));
private static final String CHANNEL_DESCRIPTION_OLD_PATTERN_PAYLOAD = JSONCONVERTER
.toJson(new ChannelDescriptionPatternPayloadBean("%unit%"));
private static final String CHANNEL_DESCRIPTION_CHANGED_EVENT_PAYLOAD_ONLY_NEW_VALUE = JSONCONVERTER
.toJson(new ChannelDescriptionChangedEventPayloadBean(CommonChannelDescriptionField.PATTERN,
CHANNEL_UID.getAsString(), Set.of("item1", "item2"), CHANNEL_DESCRIPTION_PATTERN_PAYLOAD, null));
private static final String CHANNEL_DESCRIPTION_CHANGED_EVENT_PAYLOAD_NEW_AND_OLD_VALUE = JSONCONVERTER
.toJson(new ChannelDescriptionChangedEventPayloadBean(CommonChannelDescriptionField.PATTERN,
CHANNEL_UID.getAsString(), Set.of("item1", "item2"), CHANNEL_DESCRIPTION_PATTERN_PAYLOAD,
CHANNEL_DESCRIPTION_OLD_PATTERN_PAYLOAD));
private static final String CHANNEL_DESCRIPTION_STATE_OPTIONS_PAYLOAD = JSONCONVERTER
.toJson(new ChannelDescriptionStateOptionsPayloadBean(List.of(new StateOption("offline", "Offline"))));
private static final String CHANNEL_DESCRIPTION_OLD_STATE_OPTIONS_PAYLOAD = JSONCONVERTER
.toJson(new ChannelDescriptionStateOptionsPayloadBean(List.of(new StateOption("online", "Online"))));
private static final String CHANNEL_DESCRIPTION_CHANGED_EVENT_PAYLOAD_ONLY_NEW_OPTIONS = JSONCONVERTER
.toJson(new ChannelDescriptionChangedEventPayloadBean(CommonChannelDescriptionField.STATE_OPTIONS,
CHANNEL_UID.getAsString(), Set.of("item1", "item2"), CHANNEL_DESCRIPTION_STATE_OPTIONS_PAYLOAD,
null));
private static final String CHANNEL_DESCRIPTION_CHANGED_EVENT_PAYLOAD_NEW_AND_OLD_OPTIONS = JSONCONVERTER
.toJson(new ChannelDescriptionChangedEventPayloadBean(CommonChannelDescriptionField.STATE_OPTIONS,
CHANNEL_UID.getAsString(), Set.of("item1", "item2"), CHANNEL_DESCRIPTION_STATE_OPTIONS_PAYLOAD,
CHANNEL_DESCRIPTION_OLD_STATE_OPTIONS_PAYLOAD));
private static final String CHANNEL_TRIGGERED_EVENT_TOPIC = ThingEventFactory.CHANNEL_TRIGGERED_EVENT_TOPIC
.replace("{channelUID}", CHANNEL_UID.getAsString());
private static final String CHANNEL_TRIGGERED_PRESSED_EVENT_PAYLOAD = new Gson()
@@ -65,23 +105,17 @@ public class ThingEventFactoryTest {
private static final String CHANNEL_TRIGGERED_EMPTY_EVENT_PAYLOAD = new Gson()
.toJson(new TriggerEventPayloadBean("", CHANNEL_UID.getAsString()));
private ThingEventFactory eventFactory;
@BeforeEach
public void setUp() {
eventFactory = new ThingEventFactory();
}
@Test
public void testSupportedEventTypes() {
assertThat(eventFactory.getSupportedEventTypes(),
assertThat(factory.getSupportedEventTypes(),
containsInAnyOrder(ThingStatusInfoEvent.TYPE, ThingStatusInfoChangedEvent.TYPE, ThingAddedEvent.TYPE,
ThingRemovedEvent.TYPE, ThingUpdatedEvent.TYPE, ChannelTriggeredEvent.TYPE));
ThingRemovedEvent.TYPE, ThingUpdatedEvent.TYPE, ChannelDescriptionChangedEvent.TYPE,
ChannelTriggeredEvent.TYPE));
}
@Test
public void testCreateEventThingStatusInfoEvent() throws Exception {
Event event = eventFactory.createEvent(ThingStatusInfoEvent.TYPE, THING_STATUS_EVENT_TOPIC,
Event event = factory.createEvent(ThingStatusInfoEvent.TYPE, THING_STATUS_EVENT_TOPIC,
THING_STATUS_EVENT_PAYLOAD, null);
assertThat(event, is(instanceOf(ThingStatusInfoEvent.class)));
@@ -106,7 +140,7 @@ public class ThingEventFactoryTest {
@Test
public void testCreateEventThingAddedEvent() throws Exception {
Event event = eventFactory.createEvent(ThingAddedEvent.TYPE, THING_ADDED_EVENT_TOPIC, THING_ADDED_EVENT_PAYLOAD,
Event event = factory.createEvent(ThingAddedEvent.TYPE, THING_ADDED_EVENT_TOPIC, THING_ADDED_EVENT_PAYLOAD,
null);
assertThat(event, is(instanceOf(ThingAddedEvent.class)));
@@ -129,6 +163,158 @@ public class ThingEventFactoryTest {
assertEquals(THING_UID.getAsString(), event.getThing().UID);
}
@Test
public void testCreateChannelDescriptionChangedEventOnlyNewValue() {
ChannelDescriptionChangedEvent event = ThingEventFactory
.createChannelDescriptionPatternChangedEvent(CHANNEL_UID, Set.of("item1", "item2"), "%s", null);
assertEquals(ChannelDescriptionChangedEvent.TYPE, event.getType());
assertEquals(CHANNEL_DESCRIPTION_CHANGED_EVENT_TOPIC, event.getTopic());
assertEquals(CHANNEL_DESCRIPTION_CHANGED_EVENT_PAYLOAD_ONLY_NEW_VALUE, event.getPayload());
assertEquals(CommonChannelDescriptionField.PATTERN, event.getField());
assertEquals(CHANNEL_UID, event.getChannelUID());
assertThat(event.getLinkedItemNames(), hasSize(2));
assertEquals(CHANNEL_DESCRIPTION_PATTERN_PAYLOAD, event.getValue());
assertNull(event.getOldValue());
}
@Test
public void testCreateChannelDescriptionChangedEventNewAndOldValue() {
ChannelDescriptionChangedEvent event = ThingEventFactory
.createChannelDescriptionPatternChangedEvent(CHANNEL_UID, Set.of("item1", "item2"), "%s", "%unit%");
assertEquals(ChannelDescriptionChangedEvent.TYPE, event.getType());
assertEquals(CHANNEL_DESCRIPTION_CHANGED_EVENT_TOPIC, event.getTopic());
assertEquals(CHANNEL_DESCRIPTION_CHANGED_EVENT_PAYLOAD_NEW_AND_OLD_VALUE, event.getPayload());
assertEquals(CommonChannelDescriptionField.PATTERN, event.getField());
assertEquals(CHANNEL_UID, event.getChannelUID());
assertThat(event.getLinkedItemNames(), hasSize(2));
assertEquals(CHANNEL_DESCRIPTION_PATTERN_PAYLOAD, event.getValue());
assertEquals(CHANNEL_DESCRIPTION_OLD_PATTERN_PAYLOAD, event.getOldValue());
}
@Test
public void testCreateEventChannelDescriptionChangedEventOnlyNewValue() throws Exception {
Event event = factory.createEvent(ChannelDescriptionChangedEvent.TYPE, CHANNEL_DESCRIPTION_CHANGED_EVENT_TOPIC,
CHANNEL_DESCRIPTION_CHANGED_EVENT_PAYLOAD_ONLY_NEW_VALUE, null);
assertThat(event, is(instanceOf(ChannelDescriptionChangedEvent.class)));
ChannelDescriptionChangedEvent triggeredEvent = (ChannelDescriptionChangedEvent) event;
assertEquals(ChannelDescriptionChangedEvent.TYPE, triggeredEvent.getType());
assertEquals(CHANNEL_DESCRIPTION_CHANGED_EVENT_TOPIC, triggeredEvent.getTopic());
assertEquals(CHANNEL_DESCRIPTION_CHANGED_EVENT_PAYLOAD_ONLY_NEW_VALUE, triggeredEvent.getPayload());
assertEquals(CommonChannelDescriptionField.PATTERN, triggeredEvent.getField());
assertEquals(CHANNEL_UID, triggeredEvent.getChannelUID());
assertThat(triggeredEvent.getLinkedItemNames(), hasSize(2));
assertEquals(CHANNEL_DESCRIPTION_PATTERN_PAYLOAD, triggeredEvent.getValue());
assertNull(triggeredEvent.getOldValue());
}
@Test
public void testCreateEventChannelDescriptionChangedEventNewAndOldValue() throws Exception {
Event event = factory.createEvent(ChannelDescriptionChangedEvent.TYPE, CHANNEL_DESCRIPTION_CHANGED_EVENT_TOPIC,
CHANNEL_DESCRIPTION_CHANGED_EVENT_PAYLOAD_NEW_AND_OLD_VALUE, null);
assertThat(event, is(instanceOf(ChannelDescriptionChangedEvent.class)));
ChannelDescriptionChangedEvent triggeredEvent = (ChannelDescriptionChangedEvent) event;
assertEquals(ChannelDescriptionChangedEvent.TYPE, triggeredEvent.getType());
assertEquals(CHANNEL_DESCRIPTION_CHANGED_EVENT_TOPIC, triggeredEvent.getTopic());
assertEquals(CHANNEL_DESCRIPTION_CHANGED_EVENT_PAYLOAD_NEW_AND_OLD_VALUE, triggeredEvent.getPayload());
assertEquals(CommonChannelDescriptionField.PATTERN, triggeredEvent.getField());
assertEquals(CHANNEL_UID, triggeredEvent.getChannelUID());
assertThat(triggeredEvent.getLinkedItemNames(), hasSize(2));
assertEquals(CHANNEL_DESCRIPTION_PATTERN_PAYLOAD, triggeredEvent.getValue());
assertEquals(CHANNEL_DESCRIPTION_OLD_PATTERN_PAYLOAD, triggeredEvent.getOldValue());
}
@SuppressWarnings("null")
@Test
public void testCreateChannelDescriptionChangedEventOnlyNewOptions() {
Set<String> itemNames = Set.of("item1", "item2");
List<StateOption> options = List.of(new StateOption("offline", "Offline"));
ChannelDescriptionChangedEvent event = ThingEventFactory
.createChannelDescriptionStateOptionsChangedEvent(CHANNEL_UID, itemNames, options, null);
assertEquals(ChannelDescriptionChangedEvent.TYPE, event.getType());
assertEquals(CHANNEL_DESCRIPTION_CHANGED_EVENT_TOPIC, event.getTopic());
assertEquals(CHANNEL_DESCRIPTION_CHANGED_EVENT_PAYLOAD_ONLY_NEW_OPTIONS, event.getPayload());
assertEquals(CommonChannelDescriptionField.STATE_OPTIONS, event.getField());
assertEquals(CHANNEL_UID, event.getChannelUID());
assertEquals(itemNames, event.getLinkedItemNames());
assertEquals(CHANNEL_DESCRIPTION_STATE_OPTIONS_PAYLOAD, event.getValue());
assertEquals(options,
JSONCONVERTER.fromJson(event.getValue(), ChannelDescriptionStateOptionsPayloadBean.class).options);
assertNull(event.getOldValue());
}
@SuppressWarnings("null")
@Test
public void testCreateEventChannelDescriptionChangedEventOnlyNewOptions() throws Exception {
List<StateOption> options = List.of(new StateOption("offline", "Offline"));
Event event = factory.createEvent(ChannelDescriptionChangedEvent.TYPE, CHANNEL_DESCRIPTION_CHANGED_EVENT_TOPIC,
CHANNEL_DESCRIPTION_CHANGED_EVENT_PAYLOAD_ONLY_NEW_OPTIONS, null);
assertThat(event, is(instanceOf(ChannelDescriptionChangedEvent.class)));
ChannelDescriptionChangedEvent triggeredEvent = (ChannelDescriptionChangedEvent) event;
assertEquals(ChannelDescriptionChangedEvent.TYPE, triggeredEvent.getType());
assertEquals(CHANNEL_DESCRIPTION_CHANGED_EVENT_TOPIC, triggeredEvent.getTopic());
assertEquals(CHANNEL_DESCRIPTION_CHANGED_EVENT_PAYLOAD_ONLY_NEW_OPTIONS, triggeredEvent.getPayload());
assertEquals(CommonChannelDescriptionField.STATE_OPTIONS, triggeredEvent.getField());
assertEquals(CHANNEL_UID, triggeredEvent.getChannelUID());
assertEquals(Set.of("item1", "item2"), triggeredEvent.getLinkedItemNames());
assertEquals(CHANNEL_DESCRIPTION_STATE_OPTIONS_PAYLOAD, triggeredEvent.getValue());
assertEquals(options, JSONCONVERTER.fromJson(triggeredEvent.getValue(),
ChannelDescriptionStateOptionsPayloadBean.class).options);
assertNull(triggeredEvent.getOldValue());
}
@SuppressWarnings("null")
@Test
public void testCreateChannelDescriptionChangedEventOldAndNewOptions() {
Set<String> itemNames = Set.of("item1", "item2");
List<StateOption> options = List.of(new StateOption("offline", "Offline"));
List<StateOption> oldOptions = List.of(new StateOption("online", "Online"));
ChannelDescriptionChangedEvent event = ThingEventFactory
.createChannelDescriptionStateOptionsChangedEvent(CHANNEL_UID, itemNames, options, oldOptions);
assertEquals(ChannelDescriptionChangedEvent.TYPE, event.getType());
assertEquals(CHANNEL_DESCRIPTION_CHANGED_EVENT_TOPIC, event.getTopic());
assertEquals(CHANNEL_DESCRIPTION_CHANGED_EVENT_PAYLOAD_NEW_AND_OLD_OPTIONS, event.getPayload());
assertEquals(CommonChannelDescriptionField.STATE_OPTIONS, event.getField());
assertEquals(CHANNEL_UID, event.getChannelUID());
assertEquals(itemNames, event.getLinkedItemNames());
assertEquals(CHANNEL_DESCRIPTION_STATE_OPTIONS_PAYLOAD, event.getValue());
assertEquals(options,
JSONCONVERTER.fromJson(event.getValue(), ChannelDescriptionStateOptionsPayloadBean.class).options);
assertEquals(CHANNEL_DESCRIPTION_OLD_STATE_OPTIONS_PAYLOAD, event.getOldValue());
assertEquals(oldOptions,
JSONCONVERTER.fromJson(event.getOldValue(), ChannelDescriptionStateOptionsPayloadBean.class).options);
}
@SuppressWarnings("null")
@Test
public void testCreateEventChannelDescriptionChangedEventOldAndNewOptions() throws Exception {
List<StateOption> options = List.of(new StateOption("offline", "Offline"));
List<StateOption> oldOptions = List.of(new StateOption("online", "Online"));
Event event = factory.createEvent(ChannelDescriptionChangedEvent.TYPE, CHANNEL_DESCRIPTION_CHANGED_EVENT_TOPIC,
CHANNEL_DESCRIPTION_CHANGED_EVENT_PAYLOAD_NEW_AND_OLD_OPTIONS, null);
assertThat(event, is(instanceOf(ChannelDescriptionChangedEvent.class)));
ChannelDescriptionChangedEvent triggeredEvent = (ChannelDescriptionChangedEvent) event;
assertEquals(ChannelDescriptionChangedEvent.TYPE, triggeredEvent.getType());
assertEquals(CHANNEL_DESCRIPTION_CHANGED_EVENT_TOPIC, triggeredEvent.getTopic());
assertEquals(CHANNEL_DESCRIPTION_CHANGED_EVENT_PAYLOAD_NEW_AND_OLD_OPTIONS, triggeredEvent.getPayload());
assertEquals(CommonChannelDescriptionField.STATE_OPTIONS, triggeredEvent.getField());
assertEquals(CHANNEL_UID, triggeredEvent.getChannelUID());
assertEquals(Set.of("item1", "item2"), triggeredEvent.getLinkedItemNames());
assertEquals(CHANNEL_DESCRIPTION_STATE_OPTIONS_PAYLOAD, triggeredEvent.getValue());
assertEquals(options, JSONCONVERTER.fromJson(triggeredEvent.getValue(),
ChannelDescriptionStateOptionsPayloadBean.class).options);
assertEquals(CHANNEL_DESCRIPTION_OLD_STATE_OPTIONS_PAYLOAD, triggeredEvent.getOldValue());
assertEquals(oldOptions, JSONCONVERTER.fromJson(triggeredEvent.getOldValue(),
ChannelDescriptionStateOptionsPayloadBean.class).options);
}
@Test
public void testCreateTriggerPressedEvent() {
ChannelTriggeredEvent event = ThingEventFactory.createTriggerEvent(CommonTriggerEvents.PRESSED, CHANNEL_UID);
@@ -138,11 +324,12 @@ public class ThingEventFactoryTest {
assertEquals(CHANNEL_TRIGGERED_PRESSED_EVENT_PAYLOAD, event.getPayload());
assertNotNull(event.getEvent());
assertEquals(CommonTriggerEvents.PRESSED, event.getEvent());
assertEquals(CHANNEL_UID, event.getChannel());
}
@Test
public void testCreateEventChannelTriggeredPressedEvent() throws Exception {
Event event = eventFactory.createEvent(ChannelTriggeredEvent.TYPE, CHANNEL_TRIGGERED_EVENT_TOPIC,
Event event = factory.createEvent(ChannelTriggeredEvent.TYPE, CHANNEL_TRIGGERED_EVENT_TOPIC,
CHANNEL_TRIGGERED_PRESSED_EVENT_PAYLOAD, null);
assertThat(event, is(instanceOf(ChannelTriggeredEvent.class)));
@@ -152,6 +339,7 @@ public class ThingEventFactoryTest {
assertEquals(CHANNEL_TRIGGERED_PRESSED_EVENT_PAYLOAD, triggeredEvent.getPayload());
assertNotNull(triggeredEvent.getEvent());
assertEquals(CommonTriggerEvents.PRESSED, triggeredEvent.getEvent());
assertEquals(CHANNEL_UID, triggeredEvent.getChannel());
}
@Test
@@ -163,11 +351,12 @@ public class ThingEventFactoryTest {
assertEquals(CHANNEL_TRIGGERED_EMPTY_EVENT_PAYLOAD, event.getPayload());
assertNotNull(event.getEvent());
assertEquals("", event.getEvent());
assertEquals(CHANNEL_UID, event.getChannel());
}
@Test
public void testCreateEventChannelTriggeredEmptyEvent() throws Exception {
Event event = eventFactory.createEvent(ChannelTriggeredEvent.TYPE, CHANNEL_TRIGGERED_EVENT_TOPIC,
Event event = factory.createEvent(ChannelTriggeredEvent.TYPE, CHANNEL_TRIGGERED_EVENT_TOPIC,
CHANNEL_TRIGGERED_EMPTY_EVENT_PAYLOAD, null);
assertThat(event, is(instanceOf(ChannelTriggeredEvent.class)));
@@ -177,5 +366,6 @@ public class ThingEventFactoryTest {
assertEquals(CHANNEL_TRIGGERED_EMPTY_EVENT_PAYLOAD, triggeredEvent.getPayload());
assertNotNull(triggeredEvent.getEvent());
assertEquals("", triggeredEvent.getEvent());
assertEquals(CHANNEL_UID, triggeredEvent.getChannel());
}
}
@@ -0,0 +1,90 @@
/**
* 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.core.thing.link.events;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.Test;
import org.openhab.core.events.Event;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.link.ItemChannelLink;
import org.openhab.core.thing.link.dto.ItemChannelLinkDTO;
import com.google.gson.Gson;
/**
* {@link LinkEventFactoryTests} tests the {@link LinkEventFactory}.
*
* @author Christoph Weitkamp - Initial contribution
*/
@NonNullByDefault
public class LinkEventFactoryTest {
private static final Gson JSONCONVERTER = new Gson();
private final LinkEventFactory factory = new LinkEventFactory();
private static final ItemChannelLink LINK = new ItemChannelLink("item", new ChannelUID("a:b:c:d"));
private static final ItemChannelLinkDTO LINK_DTO = new ItemChannelLinkDTO(LINK.getItemName(),
LINK.getLinkedUID().toString(), LINK.getConfiguration().getProperties());
private static final String LINK_EVENT_PAYLOAD = JSONCONVERTER.toJson(LINK_DTO);
private static final String LINK_ADDED_EVENT_TOPIC = LinkEventFactory.LINK_ADDED_EVENT_TOPIC.replace("{linkID}",
LINK.getItemName() + "-" + LINK.getLinkedUID().toString());
private static final String LINK_REMOVED_EVENT_TOPIC = LinkEventFactory.LINK_REMOVED_EVENT_TOPIC.replace("{linkID}",
LINK.getItemName() + "-" + LINK.getLinkedUID().toString());
@Test
public void testCreateItemChannelLinkAddedEvent() {
ItemChannelLinkAddedEvent event = LinkEventFactory.createItemChannelLinkAddedEvent(LINK);
assertEquals(ItemChannelLinkAddedEvent.TYPE, event.getType());
assertEquals(LINK_ADDED_EVENT_TOPIC, event.getTopic());
assertEquals(LINK_EVENT_PAYLOAD, event.getPayload());
}
@Test
public void testCreateEventItemChannelLinkAddedEvent() throws Exception {
Event event = factory.createEvent(ItemChannelLinkAddedEvent.TYPE, LINK_ADDED_EVENT_TOPIC, LINK_EVENT_PAYLOAD,
null);
assertThat(event, is(instanceOf(ItemChannelLinkAddedEvent.class)));
ItemChannelLinkAddedEvent triggeredEvent = (ItemChannelLinkAddedEvent) event;
assertEquals(ItemChannelLinkAddedEvent.TYPE, triggeredEvent.getType());
assertEquals(LINK_ADDED_EVENT_TOPIC, triggeredEvent.getTopic());
assertEquals(LINK_EVENT_PAYLOAD, triggeredEvent.getPayload());
}
@Test
public void testCreateItemChannelLinkRemovedEvent() {
ItemChannelLinkRemovedEvent event = LinkEventFactory.createItemChannelLinkRemovedEvent(LINK);
assertEquals(ItemChannelLinkRemovedEvent.TYPE, event.getType());
assertEquals(LINK_REMOVED_EVENT_TOPIC, event.getTopic());
assertEquals(LINK_EVENT_PAYLOAD, event.getPayload());
}
@Test
public void testCreateEventItemChannelLinkRemovedEvent() throws Exception {
Event event = factory.createEvent(ItemChannelLinkRemovedEvent.TYPE, LINK_REMOVED_EVENT_TOPIC,
LINK_EVENT_PAYLOAD, null);
assertThat(event, is(instanceOf(ItemChannelLinkRemovedEvent.class)));
ItemChannelLinkRemovedEvent triggeredEvent = (ItemChannelLinkRemovedEvent) event;
assertEquals(ItemChannelLinkRemovedEvent.TYPE, triggeredEvent.getType());
assertEquals(LINK_REMOVED_EVENT_TOPIC, triggeredEvent.getTopic());
assertEquals(LINK_EVENT_PAYLOAD, triggeredEvent.getPayload());
}
}
@@ -89,6 +89,6 @@ public class CommandOption {
@Override
public String toString() {
return "CommandOption [command=" + command + ", label=" + label + "]";
return String.format("CommandOption [command=%s, label=%s]", command, label);
}
}
@@ -81,6 +81,6 @@ public final class StateOption {
@Override
public String toString() {
return "StateOption [value=" + value + ", label=" + label + "]";
return String.format("StateOption [value=%s, label=%s]", value, label);
}
}
@@ -0,0 +1,101 @@
/**
* 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.core.thing.events;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.events.Event;
import org.openhab.core.events.EventFilter;
import org.openhab.core.events.EventSubscriber;
import org.openhab.core.test.java.JavaOSGiTest;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingRegistry;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.ThingFactory;
import org.openhab.core.thing.type.ThingType;
import org.openhab.core.thing.type.ThingTypeBuilder;
/**
* Event Tests for {@link ThingRegistry}.
*
* @author Christoph Weitkamp - Initial contribution
*/
@NonNullByDefault
public class ThingEventOSGiTest extends JavaOSGiTest {
class ThingEventSubscriber implements EventSubscriber {
private @Nullable Event lastReceivedEvent;
@Override
public Set<String> getSubscribedEventTypes() {
return Set.of(ThingAddedEvent.TYPE, ThingUpdatedEvent.TYPE, ThingRemovedEvent.TYPE);
}
@Override
public @Nullable EventFilter getEventFilter() {
return null;
}
@Override
public void receive(Event event) {
lastReceivedEvent = event;
}
public @Nullable Event getLastReceivedEvent() {
return lastReceivedEvent;
}
}
@BeforeEach
public void setup() {
registerVolatileStorageService();
}
@Test
public void assertThingEventsAreSent() {
ThingRegistry thingRegistry = getService(ThingRegistry.class);
ThingEventSubscriber eventSubscriber = new ThingEventSubscriber();
registerService(eventSubscriber);
ThingType thingType = ThingTypeBuilder.instance("bindingId", "thingTypeId", "label").build();
ThingUID thingUID = new ThingUID(thingType.getUID(), "thingId");
Configuration configuration = new Configuration();
Thing thing = ThingFactory.createThing(thingType, thingUID, configuration);
thingRegistry.add(thing);
waitFor(() -> eventSubscriber.getLastReceivedEvent() != null);
waitForAssert(() -> assertThat(eventSubscriber.getLastReceivedEvent().getType(), is(ThingAddedEvent.TYPE)));
assertThat(eventSubscriber.getLastReceivedEvent().getTopic(),
is(ThingEventFactory.THING_ADDED_EVENT_TOPIC.replace("{thingUID}", thingUID.getAsString())));
thingRegistry.update(thing);
waitForAssert(() -> assertThat(eventSubscriber.getLastReceivedEvent().getType(), is(ThingUpdatedEvent.TYPE)));
assertThat(eventSubscriber.getLastReceivedEvent().getTopic(),
is(ThingEventFactory.THING_UPDATED_EVENT_TOPIC.replace("{thingUID}", thingUID.getAsString())));
thingRegistry.forceRemove(thing.getUID());
waitForAssert(() -> assertThat(eventSubscriber.getLastReceivedEvent().getType(), is(ThingRemovedEvent.TYPE)));
assertThat(eventSubscriber.getLastReceivedEvent().getTopic(),
is(ThingEventFactory.THING_REMOVED_EVENT_TOPIC.replace("{thingUID}", thingUID.getAsString())));
}
}