Skip to content

Commit

Permalink
WIP: Extend ChannelType description by command options
Browse files Browse the repository at this point in the history
This is WIP and open for discussion.

This addresses eclipse-archived#5099 by adding command options as an alternative to a state description.
The implementation is naive and straigt forward.
Command options will be rendered as push buttons by UIs and send the corresponding command value as a command to the channel.
With this proposal, the state of the channel will not be represented in the UI, so ThingHandelers may not even update the state.

Signed-off-by: Henning Treu <[email protected]>
  • Loading branch information
Henning Treu committed Feb 21, 2018
1 parent 3462cda commit 997f2be
Show file tree
Hide file tree
Showing 12 changed files with 188 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -112,13 +112,13 @@ public void setup() {
final ChannelType channelType2 = new ChannelType(new ChannelTypeUID("hue:num"), false, "Number", " ", "", null,
null, state2, null);
final ChannelType channelType3 = new ChannelType(new ChannelTypeUID("hue:info"), true, "String", " ", "", null,
null, null, null);
null, (StateDescription) null, null);
final ChannelType channelType4 = new ChannelType(new ChannelTypeUID("hue:color"), false, "Color", "Color", "",
"ColorLight", null, null, null);
"ColorLight", null, (StateDescription) null, null);
final ChannelType channelType5 = new ChannelType(new ChannelTypeUID("hue:brightness"), false, "Dimmer",
"Brightness", "", "DimmableLight", null, null, null);
"Brightness", "", "DimmableLight", null, (StateDescription) null, null);
final ChannelType channelType6 = new ChannelType(new ChannelTypeUID("hue:switch"), false, "Switch", "Switch",
"", "Light", null, null, null);
"", "Light", null, (StateDescription) null, null);
final ChannelType channelType7 = new ChannelType(new ChannelTypeUID("hue:num-dynamic"), false, "Number", " ",
"", "Light", null, state, null);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,23 @@
package org.eclipse.smarthome.core.thing.xml.internal;

import java.net.URI;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.smarthome.config.core.ConfigDescription;
import org.eclipse.smarthome.config.xml.util.ConverterAttributeMapValidator;
import org.eclipse.smarthome.config.xml.util.NodeIterator;
import org.eclipse.smarthome.config.xml.util.NodeValue;
import org.eclipse.smarthome.core.thing.type.ChannelKind;
import org.eclipse.smarthome.core.thing.type.ChannelType;
import org.eclipse.smarthome.core.thing.type.ChannelTypeUID;
import org.eclipse.smarthome.core.types.CommandOption;
import org.eclipse.smarthome.core.types.EventDescription;
import org.eclipse.smarthome.core.types.StateDescription;

import com.thoughtworks.xstream.converters.ConversionException;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
Expand Down Expand Up @@ -127,6 +131,33 @@ private EventDescription readEventDescription(NodeIterator nodeIterator) {
return null;
}

private List<CommandOption> readCommandOptions(NodeIterator nodeIterator) throws ConversionException {
List<CommandOption> commandOptions = null;

List<?> commandOptionsNode = nodeIterator.nextList("command-options", false);

if (commandOptionsNode != null) {
commandOptions = new ArrayList<>(commandOptionsNode.size());

for (Object coNodeObject : commandOptionsNode) {
NodeValue commandOptionNode = (NodeValue) coNodeObject;

if ("option".equals(commandOptionNode.getNodeName())) {
String name = (String) commandOptionNode.getValue();
String command = commandOptionNode.getAttributes().get("value");

if (name != null && command != null) {
commandOptions.add(new CommandOption(command, name));
}
} else {
throw new ConversionException("The 'command-options' node must only contain 'option' nodes!");
}
}
}

return commandOptions;
}

@Override
protected ChannelTypeXmlResult unmarshalType(HierarchicalStreamReader reader, UnmarshallingContext context,
Map<String, String> attributes, NodeIterator nodeIterator) throws ConversionException {
Expand All @@ -142,6 +173,7 @@ protected ChannelTypeXmlResult unmarshalType(HierarchicalStreamReader reader, Un
String description = super.readDescription(nodeIterator);
String category = readCategory(nodeIterator);
Set<String> tags = readTags(nodeIterator);
List<CommandOption> commandOptions = readCommandOptions(nodeIterator);

StateDescription stateDescription = readStateDescription(nodeIterator);
EventDescription eventDescription = readEventDescription(nodeIterator);
Expand All @@ -153,8 +185,14 @@ protected ChannelTypeXmlResult unmarshalType(HierarchicalStreamReader reader, Un
kind = "state";
}

ChannelType channelType = new ChannelType(channelTypeUID, advanced, itemType, ChannelKind.parse(kind), label,
description, category, tags, stateDescription, eventDescription, (URI) configDescriptionObjects[0]);
ChannelType channelType;
if (stateDescription == null && kind.equalsIgnoreCase("state") && commandOptions != null) {
channelType = new ChannelType(channelTypeUID, advanced, itemType, label, description, category, tags,
commandOptions, (URI) configDescriptionObjects[0]);
} else {
channelType = new ChannelType(channelTypeUID, advanced, itemType, ChannelKind.parse(kind), label,
description, category, tags, stateDescription, eventDescription, (URI) configDescriptionObjects[0]);
}

ChannelTypeXmlResult channelTypeXmlResult = new ChannelTypeXmlResult(channelType,
(ConfigDescription) configDescriptionObjects[1], system);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ public void registerAliases(XStream xstream) {
xstream.alias("properties", NodeList.class);
xstream.alias("property", NodeValue.class);
xstream.alias("representation-property", NodeValue.class);
xstream.alias("command-options", NodeList.class);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.eclipse.smarthome.core.thing.i18n.ThingTypeI18nUtil;
import org.eclipse.smarthome.core.thing.type.ChannelGroupType;
import org.eclipse.smarthome.core.thing.type.ChannelGroupTypeUID;
import org.eclipse.smarthome.core.thing.type.ChannelKind;
import org.eclipse.smarthome.core.thing.type.ChannelType;
import org.eclipse.smarthome.core.thing.type.ChannelTypeProvider;
import org.eclipse.smarthome.core.thing.type.ChannelTypeUID;
Expand Down Expand Up @@ -108,6 +109,12 @@ protected ChannelType localize(Bundle bundle, ChannelType channelType, Locale lo
channelType.getDescription(), locale);
StateDescription state = createLocalizedChannelState(bundle, channelType, channelTypeUID, locale);

if (state == null && channelType.getKind() == ChannelKind.STATE && channelType.getCommandOptions() != null) {
return new ChannelType(channelTypeUID, channelType.isAdvanced(), channelType.getItemType(), label,
description, channelType.getCategory(), channelType.getTags(), channelType.getCommandOptions(),
channelType.getEvent(), channelType.getConfigDescriptionURI());
}

return new ChannelType(channelTypeUID, channelType.isAdvanced(), channelType.getItemType(),
channelType.getKind(), label, description, channelType.getCategory(), channelType.getTags(), state,
channelType.getEvent(), channelType.getConfigDescriptionURI());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,10 @@
<xs:element name="description" type="xs:string" minOccurs="0"/>
<xs:element name="category" type="xs:string" minOccurs="0"/>
<xs:element name="tags" type="thing-description:tags" minOccurs="0"/>
<xs:element name="state" type="thing-description:state" minOccurs="0"/>
<xs:choice minOccurs="0">
<xs:element name="state" type="thing-description:state"/>
<xs:element name="command-options" type="thing-description:command-options"/>
</xs:choice>
<xs:element name="event" type="thing-description:event" minOccurs="0"/>
<xs:choice minOccurs="0">
<xs:element name="config-description" type="config-description:configDescription"/>
Expand Down Expand Up @@ -160,6 +163,12 @@
</xs:sequence>
</xs:complexType>

<xs:complexType name="command-options">
<xs:sequence>
<xs:element name="option" type="thing-description:option" minOccurs="1" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>

<xs:complexType name="option">
<xs:simpleContent>
<xs:extension base="xs:string">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@

import java.util.List;
import java.util.Set;

import org.eclipse.smarthome.config.core.dto.ConfigDescriptionParameterDTO;
import org.eclipse.smarthome.config.core.dto.ConfigDescriptionParameterGroupDTO;
import org.eclipse.smarthome.core.thing.type.ChannelKind;
import org.eclipse.smarthome.core.types.CommandOption;
import org.eclipse.smarthome.core.types.StateDescription;

/**
Expand All @@ -38,14 +40,15 @@ public class ChannelTypeDTO {
public Set<String> tags;
public String UID;
public boolean advanced;
public List<CommandOption> commandOptions;

public ChannelTypeDTO() {
}

public ChannelTypeDTO(String UID, String label, String description, String category, String itemType,
ChannelKind kind, List<ConfigDescriptionParameterDTO> parameters,
List<ConfigDescriptionParameterGroupDTO> parameterGroups, StateDescription stateDescription,
Set<String> tags, boolean advanced) {
Set<String> tags, boolean advanced, List<CommandOption> commandOptions) {
this.UID = UID;
this.label = label;
this.description = description;
Expand All @@ -57,5 +60,6 @@ public ChannelTypeDTO(String UID, String label, String description, String categ
this.kind = kind.toString();
this.itemType = itemType;
this.advanced = advanced;
this.commandOptions = commandOptions;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@
import java.net.URI;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.eclipse.smarthome.config.core.ConfigDescription;
import org.eclipse.smarthome.core.thing.Channel;
import org.eclipse.smarthome.core.types.CommandOption;
import org.eclipse.smarthome.core.types.EventDescription;
import org.eclipse.smarthome.core.types.StateDescription;

Expand All @@ -29,6 +32,7 @@
* <b>Hint:</b> This class is immutable.
*
* @author Michael Grammling - Initial Contribution
* @author Henning Treu - add command options
*/
public class ChannelType extends AbstractDescriptionType {

Expand All @@ -38,6 +42,7 @@ public class ChannelType extends AbstractDescriptionType {
private final Set<String> tags;
private final String category;
private final StateDescription state;
private final List<CommandOption> commandOptions;
private final EventDescription event;
private final URI configDescriptionURI;

Expand Down Expand Up @@ -70,6 +75,38 @@ public ChannelType(ChannelTypeUID uid, boolean advanced, String itemType, String
}
}

/**
* Creates a new instance of a "write-only" {@link ChannelType} with command options. The purpose of this
* {@link ChannelType} is to send command to a device without updating the state of the corresponding channel.
* E.g. activate a special device mode which is not represented as a definitive state.
*
* @param uid the unique identifier which identifies this Channel type within
* the overall system (must neither be null, nor empty)
* @param advanced true if this channel type contains advanced features, otherwise false
* @param itemType the item type of this Channel type, e.g. {@code ColorItem} (must neither be null nor empty)
* @param label the human readable label for the according type
* (must neither be null nor empty)
* @param description the human readable description for the according type
* (could be null or empty)
* @param category the category of this Channel type, e.g. {@code TEMPERATURE} (could be null or empty)
* @param tags all tags of this {@link ChannelType}, e.g. {@code Alarm} (could be null or empty)
* @param commandOptions a list of {@link CommandOption}s which should be rendered as push-buttons. The command
* values will be send to the channel from this {@link ChannelType}.
* @param configDescriptionURI the link to the concrete ConfigDescription (could be null)
*/
public ChannelType(ChannelTypeUID uid, boolean advanced, String itemType, String label, String description,
String category, Set<String> tags, List<CommandOption> commandOptions, URI configDescriptionURI) {
this(uid, advanced, itemType, ChannelKind.STATE, label, description, category, tags, null, commandOptions, null,
configDescriptionURI);
}

public ChannelType(ChannelTypeUID channelTypeUID, boolean advanced, String itemType, String label,
String description, String category, Set<String> tags, List<CommandOption> commandOptions,
EventDescription event, URI configDescriptionURI) {
this(channelTypeUID, advanced, itemType, ChannelKind.STATE, label, description, category, tags, null,
commandOptions, event, configDescriptionURI);
}

/**
* Creates a new instance of this class with the specified parameters.
*
Expand All @@ -93,6 +130,14 @@ public ChannelType(ChannelTypeUID uid, boolean advanced, String itemType, String
public ChannelType(ChannelTypeUID uid, boolean advanced, String itemType, ChannelKind kind, String label,
String description, String category, Set<String> tags, StateDescription state, EventDescription event,
URI configDescriptionURI) throws IllegalArgumentException {
this(uid, advanced, itemType, kind, label, description, category, tags, state, null, event,
configDescriptionURI);
}

private ChannelType(ChannelTypeUID uid, boolean advanced, String itemType, ChannelKind kind, String label,
String description, String category, Set<String> tags, StateDescription state,
List<CommandOption> commandOptions, EventDescription event, URI configDescriptionURI)
throws IllegalArgumentException {
super(uid, label, description);

if (kind == null) {
Expand All @@ -119,6 +164,11 @@ public ChannelType(ChannelTypeUID uid, boolean advanced, String itemType, Channe
this.advanced = advanced;
this.category = category;
this.state = state;
if (state == null && commandOptions != null) {
this.commandOptions = commandOptions;
} else {
this.commandOptions = null;
}
this.event = event;
}

Expand Down Expand Up @@ -209,4 +259,8 @@ public String getCategory() {
return category;
}

public List<CommandOption> getCommandOptions() {
return commandOptions;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* Copyright (c) 2014,2018 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* 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.eclipse.smarthome.core.types;

import java.nio.channels.Channel;

import org.eclipse.jdt.annotation.NonNullByDefault;

/**
* Represents a command option for "write only" command {@link Channel}s. CommandOptions will be rendered as
* push-buttons in the UI and will not represent a state.
*
* @author Henning Treu - initial contribution
*
*/
@NonNullByDefault
public class CommandOption {

/**
* The command which will be send to the Channel
*/
private final String command;

/**
* The name of the command which will be displayed in the UI.
*/
private final String name;

public CommandOption(String command, String name) {
this.command = command;
this.name = name;
}

public String getCommand() {
return command;
}

public String getName() {
return name;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,8 @@ private ChannelTypeDTO convertToChannelTypeDTO(ChannelType channelType, Locale l

return new ChannelTypeDTO(channelType.getUID().toString(), channelType.getLabel(), channelType.getDescription(),
channelType.getCategory(), channelType.getItemType(), channelType.getKind(), parameters,
parameterGroups, channelType.getState(), channelType.getTags(), channelType.isAdvanced());
parameterGroups, channelType.getState(), channelType.getTags(), channelType.isAdvanced(),
channelType.getCommandOptions());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.eclipse.smarthome.core.thing.type.ChannelType;
import org.eclipse.smarthome.core.thing.type.ChannelTypeProvider;
import org.eclipse.smarthome.core.thing.type.ChannelTypeUID;
import org.eclipse.smarthome.core.types.StateDescription;

/**
*
Expand All @@ -47,14 +48,14 @@ public class TestHueChannelTypeProvider implements ChannelTypeProvider {
public TestHueChannelTypeProvider() {
try {
ChannelType ctColor = new ChannelType(COLOR_CHANNEL_TYPE_UID, false, "Color", "colorLabel", "description",
null, null, null, new URI("hue", "LCT001:color", null));
null, null, (StateDescription) null, new URI("hue", "LCT001:color", null));
ChannelType ctColorTemperature = new ChannelType(COLOR_TEMP_CHANNEL_TYPE_UID, false, "Dimmer",
"colorTemperatureLabel", "description", null, null, null,
"colorTemperatureLabel", "description", null, null, (StateDescription) null,
new URI("hue", "LCT001:color_temperature", null));
ChannelType ctColorX = new ChannelType(COLORX_CHANNEL_TYPE_UID, false, "Color", "colorLabel", "description",
null, null, null, new URI("Xhue", "XLCT001:Xcolor", null));
null, null, (StateDescription) null, new URI("Xhue", "XLCT001:Xcolor", null));
ChannelType ctColorTemperatureX = new ChannelType(COLORX_TEMP_CHANNEL_TYPE_UID, false, "Dimmer",
"colorTemperatureLabel", "description", null, null, null,
"colorTemperatureLabel", "description", null, null, (StateDescription) null,
new URI("Xhue", "XLCT001:Xcolor_temperature", null));
channelTypes = Arrays.asList(ctColor, ctColorTemperature, ctColorX, ctColorTemperatureX);

Expand Down
Loading

0 comments on commit 997f2be

Please sign in to comment.