--- /dev/null
+/**
+ * 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;
+ }
+}
private final SseEventSourceFactory eventSourceFactory;
private final RemoteopenhabChannelTypeProvider channelTypeProvider;
private final RemoteopenhabStateDescriptionOptionProvider stateDescriptionProvider;
+ private final RemoteopenhabCommandDescriptionOptionProvider commandDescriptionProvider;
private final Gson jsonParser;
private HttpClient httpClientTrustingCert;
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 {
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);
}
--- /dev/null
+/**
+ * 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 = "";
+}
--- /dev/null
+/**
+ * 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;
+}
--- /dev/null
+/**
+ * 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 = "";
+}
--- /dev/null
+/**
+ * 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();
+}
public String state = "";
public String groupType = "";
public @Nullable RemoteopenhabStateDescription stateDescription;
+ public @Nullable RemoteopenhabCommandDescription commandDescription;
}
--- /dev/null
+/**
+ * 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();
+}
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;
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;
private final HttpClient httpClientTrustingCert;
private final RemoteopenhabChannelTypeProvider channelTypeProvider;
private final RemoteopenhabStateDescriptionOptionProvider stateDescriptionProvider;
+ private final RemoteopenhabCommandDescriptionOptionProvider commandDescriptionProvider;
private final Object updateThingLock = new Object();
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);
}
}
}
- 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(), 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));
}
- stateDescriptionProvider.setStateOptions(channel.getUID(), stateOptions);
- logger.trace("{} options set for the channel {}", options.size(), channel.getUID());
+ commandDescriptionProvider.setCommandOptions(channel.getUID(), options);
+ logger.trace("{} command options set for the channel {}", options.size(), channel.getUID());
}
}
}
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);
}
}
}
+ @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);
* 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);
}
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;
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;
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) {
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":