mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
[remoteopenhab] Listen to the new ChannelDescriptionChangedEvent events to update dynamic state/command options (#10885)
Also handle dynamic command options Signed-off-by: Laurent Garnier <lg.hc@free.fr>
This commit is contained in:
parent
58c7928b23
commit
34e26f806d
@ -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.binding.remoteopenhab.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 Laurent Garnier - Initial contribution
|
||||
*/
|
||||
@Component(service = { DynamicCommandDescriptionProvider.class, RemoteopenhabCommandDescriptionOptionProvider.class })
|
||||
@NonNullByDefault
|
||||
public class RemoteopenhabCommandDescriptionOptionProvider extends BaseDynamicCommandDescriptionProvider {
|
||||
|
||||
@Activate
|
||||
public RemoteopenhabCommandDescriptionOptionProvider(final @Reference EventPublisher eventPublisher, //
|
||||
final @Reference ItemChannelLinkRegistry itemChannelLinkRegistry, //
|
||||
final @Reference ChannelTypeI18nLocalizationService channelTypeI18nLocalizationService) {
|
||||
this.eventPublisher = eventPublisher;
|
||||
this.itemChannelLinkRegistry = itemChannelLinkRegistry;
|
||||
this.channelTypeI18nLocalizationService = channelTypeI18nLocalizationService;
|
||||
}
|
||||
}
|
@ -75,6 +75,7 @@ public class RemoteopenhabHandlerFactory extends BaseThingHandlerFactory {
|
||||
private final SseEventSourceFactory eventSourceFactory;
|
||||
private final RemoteopenhabChannelTypeProvider channelTypeProvider;
|
||||
private final RemoteopenhabStateDescriptionOptionProvider stateDescriptionProvider;
|
||||
private final RemoteopenhabCommandDescriptionOptionProvider commandDescriptionProvider;
|
||||
private final Gson jsonParser;
|
||||
|
||||
private HttpClient httpClientTrustingCert;
|
||||
@ -83,13 +84,15 @@ public class RemoteopenhabHandlerFactory extends BaseThingHandlerFactory {
|
||||
public RemoteopenhabHandlerFactory(final @Reference HttpClientFactory httpClientFactory,
|
||||
final @Reference ClientBuilder clientBuilder, final @Reference SseEventSourceFactory eventSourceFactory,
|
||||
final @Reference RemoteopenhabChannelTypeProvider channelTypeProvider,
|
||||
final @Reference RemoteopenhabStateDescriptionOptionProvider stateDescriptionProvider) {
|
||||
final @Reference RemoteopenhabStateDescriptionOptionProvider stateDescriptionProvider,
|
||||
final @Reference RemoteopenhabCommandDescriptionOptionProvider commandDescriptionProvider) {
|
||||
this.httpClient = httpClientFactory.getCommonHttpClient();
|
||||
this.httpClientTrustingCert = httpClientFactory.createHttpClient(RemoteopenhabBindingConstants.BINDING_ID);
|
||||
this.clientBuilder = clientBuilder;
|
||||
this.eventSourceFactory = eventSourceFactory;
|
||||
this.channelTypeProvider = channelTypeProvider;
|
||||
this.stateDescriptionProvider = stateDescriptionProvider;
|
||||
this.commandDescriptionProvider = commandDescriptionProvider;
|
||||
this.jsonParser = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.IDENTITY).create();
|
||||
|
||||
try {
|
||||
@ -196,7 +199,8 @@ public class RemoteopenhabHandlerFactory extends BaseThingHandlerFactory {
|
||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||
if (thingTypeUID.equals(RemoteopenhabBindingConstants.BRIDGE_TYPE_SERVER)) {
|
||||
return new RemoteopenhabBridgeHandler((Bridge) thing, httpClient, httpClientTrustingCert, clientBuilder,
|
||||
eventSourceFactory, channelTypeProvider, stateDescriptionProvider, jsonParser);
|
||||
eventSourceFactory, channelTypeProvider, stateDescriptionProvider, commandDescriptionProvider,
|
||||
jsonParser);
|
||||
} else if (RemoteopenhabBindingConstants.SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID)) {
|
||||
return new RemoteopenhabThingHandler(thing);
|
||||
}
|
||||
|
@ -0,0 +1,33 @@
|
||||
/**
|
||||
* 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.remoteopenhab.internal.data;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Payload from ChannelDescriptionChangedEvent events received through the SSE connection.
|
||||
*
|
||||
* @author Laurent Garnier - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class RemoteopenhabChannelDescriptionChangedEvent {
|
||||
|
||||
public String field = "";
|
||||
public String channelUID = "";
|
||||
public Set<String> linkedItemNames = Set.of();
|
||||
public String value = "";
|
||||
public @Nullable String oldValue = "";
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
/**
|
||||
* 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.remoteopenhab.internal.data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Part of {@link RemoteopenhabItem} containing the command description
|
||||
*
|
||||
* @author Laurent Garnier - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class RemoteopenhabCommandDescription {
|
||||
|
||||
public @Nullable List<RemoteopenhabCommandOption> commandOptions;
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/**
|
||||
* 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.remoteopenhab.internal.data;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Part of {@link RemoteopenhabCommandDescription} containing one command option
|
||||
*
|
||||
* @author Laurent Garnier - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class RemoteopenhabCommandOption {
|
||||
|
||||
public String command = "";
|
||||
public String label = "";
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
/**
|
||||
* 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.remoteopenhab.internal.data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Match content of field value from {@link RemoteopenhabChannelDescriptionChangedEvent) event payload when event is for
|
||||
* COMMAND_OPTIONS
|
||||
*
|
||||
* @author Laurent Garnier - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class RemoteopenhabCommandOptions {
|
||||
|
||||
public List<RemoteopenhabCommandOption> options = List.of();
|
||||
}
|
@ -29,4 +29,5 @@ public class RemoteopenhabItem {
|
||||
public String state = "";
|
||||
public String groupType = "";
|
||||
public @Nullable RemoteopenhabStateDescription stateDescription;
|
||||
public @Nullable RemoteopenhabCommandDescription commandDescription;
|
||||
}
|
||||
|
@ -0,0 +1,29 @@
|
||||
/**
|
||||
* 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.remoteopenhab.internal.data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Match content of field value from {@link RemoteopenhabChannelDescriptionChangedEvent) event payload when event is for
|
||||
* STATE_OPTIONS
|
||||
*
|
||||
* @author Laurent Garnier - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class RemoteopenhabStateOptions {
|
||||
|
||||
public List<RemoteopenhabStateOption> options = List.of();
|
||||
}
|
@ -32,8 +32,11 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.openhab.binding.remoteopenhab.internal.RemoteopenhabChannelTypeProvider;
|
||||
import org.openhab.binding.remoteopenhab.internal.RemoteopenhabCommandDescriptionOptionProvider;
|
||||
import org.openhab.binding.remoteopenhab.internal.RemoteopenhabStateDescriptionOptionProvider;
|
||||
import org.openhab.binding.remoteopenhab.internal.config.RemoteopenhabServerConfiguration;
|
||||
import org.openhab.binding.remoteopenhab.internal.data.RemoteopenhabCommandDescription;
|
||||
import org.openhab.binding.remoteopenhab.internal.data.RemoteopenhabCommandOption;
|
||||
import org.openhab.binding.remoteopenhab.internal.data.RemoteopenhabItem;
|
||||
import org.openhab.binding.remoteopenhab.internal.data.RemoteopenhabStateDescription;
|
||||
import org.openhab.binding.remoteopenhab.internal.data.RemoteopenhabStateOption;
|
||||
@ -69,6 +72,7 @@ import org.openhab.core.thing.type.ChannelType;
|
||||
import org.openhab.core.thing.type.ChannelTypeBuilder;
|
||||
import org.openhab.core.thing.type.ChannelTypeUID;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.CommandOption;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.StateDescriptionFragmentBuilder;
|
||||
@ -101,6 +105,7 @@ public class RemoteopenhabBridgeHandler extends BaseBridgeHandler
|
||||
private final HttpClient httpClientTrustingCert;
|
||||
private final RemoteopenhabChannelTypeProvider channelTypeProvider;
|
||||
private final RemoteopenhabStateDescriptionOptionProvider stateDescriptionProvider;
|
||||
private final RemoteopenhabCommandDescriptionOptionProvider commandDescriptionProvider;
|
||||
|
||||
private final Object updateThingLock = new Object();
|
||||
|
||||
@ -114,11 +119,13 @@ public class RemoteopenhabBridgeHandler extends BaseBridgeHandler
|
||||
public RemoteopenhabBridgeHandler(Bridge bridge, HttpClient httpClient, HttpClient httpClientTrustingCert,
|
||||
ClientBuilder clientBuilder, SseEventSourceFactory eventSourceFactory,
|
||||
RemoteopenhabChannelTypeProvider channelTypeProvider,
|
||||
RemoteopenhabStateDescriptionOptionProvider stateDescriptionProvider, final Gson jsonParser) {
|
||||
RemoteopenhabStateDescriptionOptionProvider stateDescriptionProvider,
|
||||
RemoteopenhabCommandDescriptionOptionProvider commandDescriptionProvider, final Gson jsonParser) {
|
||||
super(bridge);
|
||||
this.httpClientTrustingCert = httpClientTrustingCert;
|
||||
this.channelTypeProvider = channelTypeProvider;
|
||||
this.stateDescriptionProvider = stateDescriptionProvider;
|
||||
this.commandDescriptionProvider = commandDescriptionProvider;
|
||||
this.restClient = new RemoteopenhabRestClient(httpClient, clientBuilder, eventSourceFactory, jsonParser);
|
||||
}
|
||||
|
||||
@ -314,18 +321,31 @@ public class RemoteopenhabBridgeHandler extends BaseBridgeHandler
|
||||
}
|
||||
}
|
||||
|
||||
private void setStateOptions(List<RemoteopenhabItem> items) {
|
||||
private void setDynamicOptions(List<RemoteopenhabItem> items) {
|
||||
for (RemoteopenhabItem item : items) {
|
||||
Channel channel = getThing().getChannel(item.name);
|
||||
RemoteopenhabStateDescription descr = item.stateDescription;
|
||||
List<RemoteopenhabStateOption> options = descr == null ? null : descr.options;
|
||||
if (channel != null && options != null && !options.isEmpty()) {
|
||||
List<StateOption> stateOptions = new ArrayList<>();
|
||||
for (RemoteopenhabStateOption option : options) {
|
||||
stateOptions.add(new StateOption(option.value, option.label));
|
||||
if (channel == null) {
|
||||
continue;
|
||||
}
|
||||
RemoteopenhabStateDescription stateDescr = item.stateDescription;
|
||||
List<RemoteopenhabStateOption> stateOptions = stateDescr == null ? null : stateDescr.options;
|
||||
if (stateOptions != null && !stateOptions.isEmpty()) {
|
||||
List<StateOption> options = new ArrayList<>();
|
||||
for (RemoteopenhabStateOption option : stateOptions) {
|
||||
options.add(new StateOption(option.value, option.label));
|
||||
}
|
||||
stateDescriptionProvider.setStateOptions(channel.getUID(), stateOptions);
|
||||
logger.trace("{} options set for the channel {}", options.size(), channel.getUID());
|
||||
stateDescriptionProvider.setStateOptions(channel.getUID(), options);
|
||||
logger.trace("{} state options set for the channel {}", options.size(), channel.getUID());
|
||||
}
|
||||
RemoteopenhabCommandDescription commandDescr = item.commandDescription;
|
||||
List<RemoteopenhabCommandOption> commandOptions = commandDescr == null ? null : commandDescr.commandOptions;
|
||||
if (commandOptions != null && !commandOptions.isEmpty()) {
|
||||
List<CommandOption> options = new ArrayList<>();
|
||||
for (RemoteopenhabCommandOption option : commandOptions) {
|
||||
options.add(new CommandOption(option.command, option.label));
|
||||
}
|
||||
commandDescriptionProvider.setCommandOptions(channel.getUID(), options);
|
||||
logger.trace("{} command options set for the channel {}", options.size(), channel.getUID());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -341,7 +361,7 @@ public class RemoteopenhabBridgeHandler extends BaseBridgeHandler
|
||||
List<RemoteopenhabItem> items = restClient.getRemoteItems("name,type,groupType,state,stateDescription");
|
||||
|
||||
if (createChannels(items, true)) {
|
||||
setStateOptions(items);
|
||||
setDynamicOptions(items);
|
||||
for (RemoteopenhabItem item : items) {
|
||||
updateChannelState(item.name, null, item.state, false);
|
||||
}
|
||||
@ -467,6 +487,11 @@ public class RemoteopenhabBridgeHandler extends BaseBridgeHandler
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemOptionsUpdatedd(RemoteopenhabItem item) {
|
||||
setDynamicOptions(List.of(item));
|
||||
}
|
||||
|
||||
private void updateChannelState(String itemName, @Nullable String stateType, String state,
|
||||
boolean onlyIfStateChanged) {
|
||||
Channel channel = getThing().getChannel(itemName);
|
||||
|
@ -43,4 +43,9 @@ public interface RemoteopenhabItemsDataListener {
|
||||
* A new ItemUpdatedEvent was published.
|
||||
*/
|
||||
void onItemUpdated(RemoteopenhabItem newItem, RemoteopenhabItem oldItem);
|
||||
|
||||
/**
|
||||
* A new ChannelDescriptionChangedEvent with updated state options or updated command options was published.
|
||||
*/
|
||||
void onItemOptionsUpdatedd(RemoteopenhabItem item);
|
||||
}
|
||||
|
@ -46,11 +46,16 @@ import org.eclipse.jetty.client.util.InputStreamResponseListener;
|
||||
import org.eclipse.jetty.client.util.StringContentProvider;
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.openhab.binding.remoteopenhab.internal.data.RemoteopenhabChannelDescriptionChangedEvent;
|
||||
import org.openhab.binding.remoteopenhab.internal.data.RemoteopenhabChannelTriggerEvent;
|
||||
import org.openhab.binding.remoteopenhab.internal.data.RemoteopenhabCommandDescription;
|
||||
import org.openhab.binding.remoteopenhab.internal.data.RemoteopenhabCommandOptions;
|
||||
import org.openhab.binding.remoteopenhab.internal.data.RemoteopenhabEvent;
|
||||
import org.openhab.binding.remoteopenhab.internal.data.RemoteopenhabEventPayload;
|
||||
import org.openhab.binding.remoteopenhab.internal.data.RemoteopenhabItem;
|
||||
import org.openhab.binding.remoteopenhab.internal.data.RemoteopenhabRestApi;
|
||||
import org.openhab.binding.remoteopenhab.internal.data.RemoteopenhabStateDescription;
|
||||
import org.openhab.binding.remoteopenhab.internal.data.RemoteopenhabStateOptions;
|
||||
import org.openhab.binding.remoteopenhab.internal.data.RemoteopenhabStatusInfo;
|
||||
import org.openhab.binding.remoteopenhab.internal.data.RemoteopenhabThing;
|
||||
import org.openhab.binding.remoteopenhab.internal.exceptions.RemoteopenhabException;
|
||||
@ -321,8 +326,9 @@ public class RemoteopenhabRestClient {
|
||||
|
||||
String url;
|
||||
try {
|
||||
url = String.format("%s?topics=%s/items/*/*,%s/things/*/*,%s/channels/*/triggered", getRestApiUrl("events"),
|
||||
getTopicNamespace(), getTopicNamespace(), getTopicNamespace());
|
||||
url = String.format(
|
||||
"%s?topics=%s/items/*/*,%s/things/*/*,%s/channels/*/triggered,openhab/channels/*/descriptionchanged",
|
||||
getRestApiUrl("events"), getTopicNamespace(), getTopicNamespace(), getTopicNamespace());
|
||||
} catch (RemoteopenhabException e) {
|
||||
logger.debug("{}", e.getMessage());
|
||||
return;
|
||||
@ -383,7 +389,7 @@ public class RemoteopenhabRestClient {
|
||||
private void onEvent(InboundSseEvent inboundEvent) {
|
||||
String name = inboundEvent.getName();
|
||||
String data = inboundEvent.readData();
|
||||
logger.trace("Received event name {} date {}", name, data);
|
||||
logger.trace("Received event name {} data {}", name, data);
|
||||
|
||||
lastEventTimestamp = System.currentTimeMillis();
|
||||
if (!connected) {
|
||||
@ -468,6 +474,35 @@ public class RemoteopenhabRestClient {
|
||||
thingsListeners
|
||||
.forEach(listener -> listener.onChannelTriggered(triggerEvent.channel, triggerEvent.event));
|
||||
break;
|
||||
case "ChannelDescriptionChangedEvent":
|
||||
RemoteopenhabStateDescription stateDescription = new RemoteopenhabStateDescription();
|
||||
RemoteopenhabCommandDescription commandDescription = new RemoteopenhabCommandDescription();
|
||||
RemoteopenhabChannelDescriptionChangedEvent descriptionChanged = Objects.requireNonNull(
|
||||
jsonParser.fromJson(event.payload, RemoteopenhabChannelDescriptionChangedEvent.class));
|
||||
switch (descriptionChanged.field) {
|
||||
case "STATE_OPTIONS":
|
||||
RemoteopenhabStateOptions stateOptions = Objects.requireNonNull(
|
||||
jsonParser.fromJson(descriptionChanged.value, RemoteopenhabStateOptions.class));
|
||||
stateDescription.options = stateOptions.options;
|
||||
break;
|
||||
case "COMMAND_OPTIONS":
|
||||
RemoteopenhabCommandOptions commandOptions = Objects.requireNonNull(
|
||||
jsonParser.fromJson(descriptionChanged.value, RemoteopenhabCommandOptions.class));
|
||||
commandDescription.commandOptions = commandOptions.options;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (stateDescription.options != null || commandDescription.commandOptions != null) {
|
||||
descriptionChanged.linkedItemNames.forEach(linkedItemName -> {
|
||||
RemoteopenhabItem item1 = new RemoteopenhabItem();
|
||||
item1.name = linkedItemName;
|
||||
item1.stateDescription = stateDescription;
|
||||
item1.commandDescription = commandDescription;
|
||||
itemsListeners.forEach(listener -> listener.onItemOptionsUpdatedd(item1));
|
||||
});
|
||||
}
|
||||
break;
|
||||
case "ItemStatePredictedEvent":
|
||||
case "ItemCommandEvent":
|
||||
case "ThingStatusInfoEvent":
|
||||
|
Loading…
Reference in New Issue
Block a user