[actiontemplatehli] Initial contribution (#12260)
* [actiontemplatehli] initial contribution Signed-off-by: Miguel Álvarez <miguelwork92@gmail.com>
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.voice.actiontemplatehli-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
|
||||
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
|
||||
|
||||
<feature name="openhab-voice-actiontemplatehli" description="Action Template Interpreter" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.voice.actiontemplatehli/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 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.voice.actiontemplatehli.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link ActionTemplateInterpreterConfiguration} class contains fields mapping thing configuration parameters.
|
||||
*
|
||||
* @author Miguel Álvarez - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ActionTemplateInterpreterConfiguration {
|
||||
/**
|
||||
* Convert the input text to lower case before processing
|
||||
*/
|
||||
public boolean lowerText = false;
|
||||
/**
|
||||
* Enable case sensitivity for pos and ner static values.
|
||||
*/
|
||||
public boolean caseSensitive = false;
|
||||
/**
|
||||
* Message for successful command
|
||||
*/
|
||||
public String commandSentMessage = "Done";
|
||||
/**
|
||||
* Message for unsuccessful processing
|
||||
*/
|
||||
public String unhandledMessage = "I can not do that";
|
||||
/**
|
||||
* Message for error during processing
|
||||
*/
|
||||
public String failureMessage = "There was an error";
|
||||
/**
|
||||
* POS tags that will be optional when comparing
|
||||
*/
|
||||
public String optionalLanguageTags = "";
|
||||
/**
|
||||
* Prefer simple tokenizer over white space tokenizer
|
||||
*/
|
||||
public boolean useSimpleTokenizer = false;
|
||||
/**
|
||||
* Enables build-in detokenization based on original text, otherwise string join by space is used
|
||||
*/
|
||||
public boolean detokenizeOptimization = true;
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 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.voice.actiontemplatehli.internal;
|
||||
|
||||
import static org.openhab.voice.actiontemplatehli.internal.ActionTemplateInterpreter.getPlaceholderSymbol;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.OpenHAB;
|
||||
|
||||
/**
|
||||
* The {@link ActionTemplateInterpreterConstants} class defines common constants, which are
|
||||
* used across the whole binding.
|
||||
*
|
||||
* @author Miguel Álvarez - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ActionTemplateInterpreterConstants {
|
||||
/**
|
||||
* Service name
|
||||
*/
|
||||
public static final String SERVICE_NAME = "Action Template Interpreter";
|
||||
/**
|
||||
* Service id
|
||||
*/
|
||||
public static final String SERVICE_ID = "actiontemplatehli";
|
||||
|
||||
/**
|
||||
* Service category
|
||||
*/
|
||||
public static final String SERVICE_CATEGORY = "voice";
|
||||
/**
|
||||
* Service pid
|
||||
*/
|
||||
public static final String SERVICE_PID = "org.openhab." + SERVICE_CATEGORY + "." + SERVICE_ID;
|
||||
/**
|
||||
* Root service folder
|
||||
*/
|
||||
public static final String NLP_FOLDER = Path.of(OpenHAB.getUserDataFolder(), "actiontemplatehli").toString();
|
||||
/**
|
||||
* NER folder for dictionaries and models
|
||||
*/
|
||||
public static final String NER_FOLDER = Path.of(NLP_FOLDER, "ner").toString();
|
||||
/**
|
||||
* POS folder for dictionaries and models
|
||||
*/
|
||||
public static final String POS_FOLDER = Path.of(NLP_FOLDER, "pos").toString();
|
||||
/**
|
||||
* Folder for type action configurations
|
||||
*/
|
||||
public static final String TYPE_ACTION_CONFIGS_FOLDER = Path.of(NLP_FOLDER, "type_actions").toString();
|
||||
/**
|
||||
* ItemLabel placeholder name
|
||||
*/
|
||||
public static final String ITEM_LABEL_PLACEHOLDER = "itemLabel";
|
||||
/**
|
||||
* ItemLabel placeholder symbol
|
||||
*/
|
||||
public static final String ITEM_LABEL_PLACEHOLDER_SYMBOL = getPlaceholderSymbol(ITEM_LABEL_PLACEHOLDER);
|
||||
/**
|
||||
* State placeholder name
|
||||
*/
|
||||
public static final String STATE_PLACEHOLDER = "state";
|
||||
/**
|
||||
* State placeholder symbol
|
||||
*/
|
||||
public static final String STATE_PLACEHOLDER_SYMBOL = getPlaceholderSymbol(STATE_PLACEHOLDER);
|
||||
/**
|
||||
* Item option placeholder name
|
||||
*/
|
||||
public static final String ITEM_OPTION_PLACEHOLDER = "itemOption";
|
||||
/**
|
||||
* State placeholder symbol
|
||||
*/
|
||||
public static final String ITEM_OPTION_PLACEHOLDER_SYMBOL = getPlaceholderSymbol(ITEM_OPTION_PLACEHOLDER);
|
||||
/**
|
||||
* Dynamic placeholder name
|
||||
*/
|
||||
public static final String DYNAMIC_PLACEHOLDER = "*";
|
||||
/**
|
||||
* Dynamic placeholder symbol
|
||||
*/
|
||||
public static final String DYNAMIC_PLACEHOLDER_SYMBOL = getPlaceholderSymbol(DYNAMIC_PLACEHOLDER);
|
||||
/**
|
||||
* GroupLabel placeholder name
|
||||
*/
|
||||
public static final String GROUP_LABEL_PLACEHOLDER = "groupLabel";
|
||||
/**
|
||||
* GroupLabel placeholder symbol
|
||||
*/
|
||||
public static final String GROUP_LABEL_PLACEHOLDER_SYMBOL = getPlaceholderSymbol(GROUP_LABEL_PLACEHOLDER);
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 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.voice.actiontemplatehli.internal.configuration;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.items.Metadata;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
/**
|
||||
* The {@link ActionTemplateConfiguration} class represents each configured action
|
||||
*
|
||||
* @author Miguel Álvarez - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ActionTemplateConfiguration {
|
||||
@JsonProperty("type")
|
||||
public String type = "tokens";
|
||||
@JsonProperty("read")
|
||||
public boolean read = false;
|
||||
@JsonProperty(value = "template", required = true)
|
||||
public String template = "";
|
||||
@JsonProperty("value")
|
||||
public @Nullable Object value = null;
|
||||
@JsonProperty("emptyValue")
|
||||
public String emptyValue = "";
|
||||
@JsonProperty("placeholders")
|
||||
public List<ActionTemplatePlaceholder> placeholders = List.of();
|
||||
@JsonProperty("requiredTags")
|
||||
public String[] requiredItemTags = new String[] {};
|
||||
@JsonProperty("silent")
|
||||
public boolean silent = false;
|
||||
@JsonProperty("memberTargets")
|
||||
public @Nullable ActionTemplateGroupTargets memberTargets = null;
|
||||
|
||||
public static ActionTemplateConfiguration[] fromMetadata(Metadata metadata) throws JsonProcessingException {
|
||||
var configuration = metadata.getConfiguration();
|
||||
var multipleValues = configuration.get("multiple");
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
if (multipleValues != null) {
|
||||
return mapper.readValue(mapper.writeValueAsString(multipleValues), ActionTemplateConfiguration[].class);
|
||||
} else {
|
||||
var actionConfig = mapper.readValue(mapper.writeValueAsString(configuration),
|
||||
ActionTemplateConfiguration.class);
|
||||
return new ActionTemplateConfiguration[] { actionConfig };
|
||||
}
|
||||
}
|
||||
|
||||
public static ActionTemplateConfiguration[] fromJSON(File jsonFile) throws IOException {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
return mapper.readValue(jsonFile, ActionTemplateConfiguration[].class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 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.voice.actiontemplatehli.internal.configuration;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
* The {@link ActionTemplateGroupTargets} class filters the item targets when targeting an item group.
|
||||
*
|
||||
* @author Miguel Álvarez - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ActionTemplateGroupTargets {
|
||||
@JsonProperty("itemName")
|
||||
public String itemName = "";
|
||||
@JsonProperty("itemType")
|
||||
public String itemType = "";
|
||||
@JsonProperty("requiredTags")
|
||||
public String[] requiredItemTags = new String[] {};
|
||||
@JsonProperty("mergeState")
|
||||
public boolean mergeState = false;
|
||||
@JsonProperty("recursive")
|
||||
public boolean recursive = true;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 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.voice.actiontemplatehli.internal.configuration;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
* The {@link ActionTemplatePlaceholder} class configures placeholders for the action template
|
||||
*
|
||||
* @author Miguel Álvarez - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ActionTemplatePlaceholder {
|
||||
@JsonProperty(value = "label", required = true)
|
||||
public String label = "";
|
||||
@JsonProperty("ner")
|
||||
public @Nullable String nerFile = null;
|
||||
@JsonProperty("nerValues")
|
||||
public String @Nullable [] nerStaticValues = null;
|
||||
@JsonProperty("pos")
|
||||
public @Nullable String posFile = null;
|
||||
@JsonProperty("posValues")
|
||||
public @Nullable Map<String, String> posStaticValues = null;
|
||||
|
||||
public static ActionTemplatePlaceholder withLabel(String label) {
|
||||
var placeholder = new ActionTemplatePlaceholder();
|
||||
placeholder.label = label;
|
||||
return placeholder;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<config-description:config-descriptions
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0
|
||||
https://openhab.org/schemas/config-description-1.0.0.xsd">
|
||||
<config-description uri="voice:actiontemplatehli">
|
||||
<parameter-group name="nlp">
|
||||
<label>Neural Language Processing</label>
|
||||
<description>Configure natural language processing.</description>
|
||||
</parameter-group>
|
||||
<parameter-group name="messages">
|
||||
<label>Response Messages</label>
|
||||
<description>Configure interpreter responses.</description>
|
||||
</parameter-group>
|
||||
<parameter name="lowerText" type="boolean" groupName="nlp">
|
||||
<label>Lower Text</label>
|
||||
<description>Convert the input text to lowercase before processing.</description>
|
||||
<default>false</default>
|
||||
</parameter>
|
||||
<parameter name="caseSensitive" type="boolean" groupName="nlp">
|
||||
<label>Case Sensitive</label>
|
||||
<description>Enable case sensitivity, do not apply to dictionaries and models, do not apply to the 'itemLabel'
|
||||
placeholder.</description>
|
||||
<default>false</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="useSimpleTokenizer" type="boolean" groupName="nlp">
|
||||
<label>Use Simple Tokenizer</label>
|
||||
<description>Prefer simple tokenizer over white space tokenizer.</description>
|
||||
<default>false</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="detokenizeOptimization" type="boolean" groupName="nlp">
|
||||
<label>Detokenize Optimization</label>
|
||||
<description>Enables build-in detokenization based on original text, otherwise string join by space is used.</description>
|
||||
<default>true</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="optionalLanguageTags" type="text" groupName="nlp">
|
||||
<label>Optional Language Tags</label>
|
||||
<description>Comma separated POS language tags that will be optional when comparing.</description>
|
||||
</parameter>
|
||||
<parameter name="commandSentMessage" type="text" groupName="messages">
|
||||
<label>Command Sent Message</label>
|
||||
<description>Message for successful command.</description>
|
||||
<default>Done</default>
|
||||
</parameter>
|
||||
<parameter name="unhandledMessage" type="text" groupName="messages">
|
||||
<label>Unhandled Message</label>
|
||||
<description>Message for unsuccessful processing.</description>
|
||||
<default>I can not do that</default>
|
||||
</parameter>
|
||||
<parameter name="failureMessage" type="text" groupName="messages">
|
||||
<label>Failure Message</label>
|
||||
<description>Message for error during processing.</description>
|
||||
<default>There was an error</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</config-description:config-descriptions>
|
||||
@@ -0,0 +1,24 @@
|
||||
voice.config.actiontemplatehli.caseSensitive.label = Case Sensitive
|
||||
voice.config.actiontemplatehli.caseSensitive.description = Enable case sensitivity, do not apply to dictionaries and models, do not apply to the 'itemLabel' placeholder.
|
||||
voice.config.actiontemplatehli.commandSentMessage.label = Command Sent Message
|
||||
voice.config.actiontemplatehli.commandSentMessage.description = Message for successful command.
|
||||
voice.config.actiontemplatehli.detokenizeOptimization.label = Detokenize Optimization
|
||||
voice.config.actiontemplatehli.detokenizeOptimization.description = Enables build-in detokenization based on original text, otherwise string join by space is used.
|
||||
voice.config.actiontemplatehli.failureMessage.label = Failure Message
|
||||
voice.config.actiontemplatehli.failureMessage.description = Message for error during processing.
|
||||
voice.config.actiontemplatehli.group.messages.label = Response Messages
|
||||
voice.config.actiontemplatehli.group.messages.description = Configure interpreter responses.
|
||||
voice.config.actiontemplatehli.group.nlp.label = Neural Language Processing
|
||||
voice.config.actiontemplatehli.group.nlp.description = Configure natural language processing.
|
||||
voice.config.actiontemplatehli.lowerText.label = Lower Text
|
||||
voice.config.actiontemplatehli.lowerText.description = Convert the input text to lowercase before processing.
|
||||
voice.config.actiontemplatehli.optionalLanguageTags.label = Optional Language Tags
|
||||
voice.config.actiontemplatehli.optionalLanguageTags.description = Comma separated POS language tags that will be optional when comparing.
|
||||
voice.config.actiontemplatehli.unhandledMessage.label = Unhandled Message
|
||||
voice.config.actiontemplatehli.unhandledMessage.description = Message for unsuccessful processing.
|
||||
voice.config.actiontemplatehli.useSimpleTokenizer.label = Use Simple Tokenizer
|
||||
voice.config.actiontemplatehli.useSimpleTokenizer.description = Prefer simple tokenizer over white space tokenizer.
|
||||
|
||||
# service
|
||||
|
||||
service.voice.actiontemplatehli.label = Action Template Interpreter
|
||||
@@ -0,0 +1,248 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 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.voice.actiontemplatehli.internal;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.core.Is.is;
|
||||
import static org.openhab.voice.actiontemplatehli.internal.ActionTemplateInterpreterConstants.SERVICE_ID;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.openhab.core.events.EventPublisher;
|
||||
import org.openhab.core.items.GroupItem;
|
||||
import org.openhab.core.items.ItemRegistry;
|
||||
import org.openhab.core.items.Metadata;
|
||||
import org.openhab.core.items.MetadataKey;
|
||||
import org.openhab.core.items.MetadataRegistry;
|
||||
import org.openhab.core.items.events.ItemEventFactory;
|
||||
import org.openhab.core.library.items.NumberItem;
|
||||
import org.openhab.core.library.items.StringItem;
|
||||
import org.openhab.core.library.items.SwitchItem;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.types.StateDescriptionFragmentBuilder;
|
||||
import org.openhab.core.types.StateOption;
|
||||
import org.openhab.core.voice.text.InterpretationException;
|
||||
import org.openhab.voice.actiontemplatehli.internal.configuration.ActionTemplateConfiguration;
|
||||
import org.openhab.voice.actiontemplatehli.internal.configuration.ActionTemplateGroupTargets;
|
||||
import org.openhab.voice.actiontemplatehli.internal.configuration.ActionTemplatePlaceholder;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
/**
|
||||
* The {@link ActionTemplateInterpreterTest} class contains the tests for the interpreter
|
||||
*
|
||||
* @author Miguel Álvarez - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ActionTemplateInterpreterTest {
|
||||
private @Mock @NonNullByDefault({}) ItemRegistry itemRegistryMock;
|
||||
private @Mock @NonNullByDefault({}) MetadataRegistry metadataRegistryMock;
|
||||
private @Mock @NonNullByDefault({}) EventPublisher eventPublisherMock;
|
||||
private @NonNullByDefault({}) ActionTemplateInterpreter interpreter;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() throws IOException {
|
||||
MockitoAnnotations.openMocks(this);
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
// Prepare Switch
|
||||
var switchItem = new SwitchItem("testSwitch");
|
||||
switchItem.setState(OnOffType.OFF);
|
||||
switchItem.setLabel("bedroom light");
|
||||
switchItem.addTag("Light");
|
||||
Mockito.when(itemRegistryMock.get(switchItem.getName())).thenReturn(switchItem);
|
||||
// Prepare Switch Write action
|
||||
var switchNPLWriteAction = new ActionTemplateConfiguration();
|
||||
switchNPLWriteAction.template = "$onOff $itemLabel";
|
||||
switchNPLWriteAction.value = "$onOff";
|
||||
var onOffPlaceholder = new ActionTemplatePlaceholder();
|
||||
onOffPlaceholder.label = "onOff";
|
||||
onOffPlaceholder.nerStaticValues = new String[] { "turn on", "turn off" };
|
||||
onOffPlaceholder.posStaticValues = Map.of("turn__on", "ON", "turn__off", "OFF");
|
||||
switchNPLWriteAction.placeholders = List.of(onOffPlaceholder);
|
||||
// Prepare Switch Read action
|
||||
var switchNPLReadAction = new ActionTemplateConfiguration();
|
||||
switchNPLReadAction.read = true;
|
||||
switchNPLReadAction.template = "how is the $itemLabel";
|
||||
switchNPLReadAction.value = "$itemLabel is $state";
|
||||
// Prepare Group
|
||||
var groupItem = new GroupItem("testGroup");
|
||||
groupItem.setLabel("bedroom");
|
||||
groupItem.addTag("Location");
|
||||
Mockito.when(itemRegistryMock.get(groupItem.getName())).thenReturn(groupItem);
|
||||
// TV channel
|
||||
var numberItem = new NumberItem("testNumber");
|
||||
numberItem.setState(DecimalType.valueOf("1"));
|
||||
numberItem.setLabel("channel");
|
||||
numberItem.addTag("tv_channel");
|
||||
numberItem
|
||||
.setStateDescriptionService((text, locale) -> StateDescriptionFragmentBuilder
|
||||
.create().withOptions(List.of(new StateOption("1", "channel one"),
|
||||
new StateOption("2", "channel two"), new StateOption("3", "channel three")))
|
||||
.build().toStateDescription());
|
||||
Mockito.when(itemRegistryMock.get(numberItem.getName())).thenReturn(numberItem);
|
||||
// Prepare Group Write action
|
||||
var groupNPLWriteAction = new ActionTemplateConfiguration();
|
||||
groupNPLWriteAction.template = "turn on $itemLabel lights";
|
||||
groupNPLWriteAction.requiredItemTags = new String[] { "Location" };
|
||||
groupNPLWriteAction.value = "ON";
|
||||
groupNPLWriteAction.memberTargets = new ActionTemplateGroupTargets();
|
||||
groupNPLWriteAction.memberTargets.itemType = "Switch";
|
||||
groupNPLWriteAction.memberTargets.requiredItemTags = new String[] { "Light" };
|
||||
// Prepare Group Read action
|
||||
var groupNPLReadAction = new ActionTemplateConfiguration();
|
||||
groupNPLReadAction.read = true;
|
||||
groupNPLReadAction.requiredItemTags = new String[] { "Location" };
|
||||
groupNPLReadAction.template = "how is the light in the $itemLabel";
|
||||
groupNPLReadAction.value = "$itemLabel in $groupLabel is $state";
|
||||
var statePlaceholder = new ActionTemplatePlaceholder();
|
||||
statePlaceholder.label = "state";
|
||||
statePlaceholder.posStaticValues = Map.of("ON", "on", "OFF", "off");
|
||||
groupNPLReadAction.placeholders = List.of(statePlaceholder);
|
||||
groupNPLReadAction.memberTargets = new ActionTemplateGroupTargets();
|
||||
groupNPLReadAction.memberTargets.itemName = switchItem.getName();
|
||||
groupNPLReadAction.memberTargets.requiredItemTags = new String[] { "Light" };
|
||||
// Prepare group write action using item option
|
||||
var groupNPLOptionWriteAction = new ActionTemplateConfiguration();
|
||||
groupNPLOptionWriteAction.template = "set $itemLabel channel to $itemOption";
|
||||
groupNPLOptionWriteAction.requiredItemTags = new String[] { "Location" };
|
||||
groupNPLOptionWriteAction.value = "$itemOption";
|
||||
groupNPLOptionWriteAction.memberTargets = new ActionTemplateGroupTargets();
|
||||
groupNPLOptionWriteAction.memberTargets.itemType = "Number";
|
||||
groupNPLOptionWriteAction.memberTargets.requiredItemTags = new String[] { "tv_channel" };
|
||||
// Prepare group read action using item option
|
||||
var groupNPLOptionReadAction = new ActionTemplateConfiguration();
|
||||
groupNPLOptionReadAction.read = true;
|
||||
groupNPLOptionReadAction.requiredItemTags = new String[] { "Location" };
|
||||
groupNPLOptionReadAction.template = "what channel is on the $itemLabel tv";
|
||||
groupNPLOptionReadAction.value = "$groupLabel tv is on $itemOption";
|
||||
groupNPLOptionReadAction.memberTargets = new ActionTemplateGroupTargets();
|
||||
groupNPLOptionReadAction.memberTargets.itemType = "Number";
|
||||
groupNPLOptionReadAction.memberTargets.requiredItemTags = new String[] { "tv_channel" };
|
||||
// Add switch member to group
|
||||
groupItem.addMember(switchItem);
|
||||
// Add number member to group
|
||||
groupItem.addMember(numberItem);
|
||||
// Prepare string
|
||||
var stringItem = new StringItem("testString");
|
||||
stringItem.setLabel("message example");
|
||||
Mockito.when(itemRegistryMock.get(stringItem.getName())).thenReturn(stringItem);
|
||||
// Prepare string write action
|
||||
var stringNPLWriteAction = new ActionTemplateConfiguration();
|
||||
stringNPLWriteAction.template = "send message $* to $contact";
|
||||
stringNPLWriteAction.value = "$contact:$*";
|
||||
stringNPLWriteAction.silent = true;
|
||||
var contactPlaceholder = new ActionTemplatePlaceholder();
|
||||
contactPlaceholder.label = "contact";
|
||||
contactPlaceholder.nerStaticValues = new String[] { "Mark", "Andrea" };
|
||||
contactPlaceholder.posStaticValues = Map.of("Mark", "+34000000000", "Andrea", "+34000000001");
|
||||
stringNPLWriteAction.placeholders = List.of(contactPlaceholder);
|
||||
var stringConfig = mapper.readValue(mapper.writeValueAsString(stringNPLWriteAction), Map.class);
|
||||
// Mock metadata for 'testString'
|
||||
Mockito.when(metadataRegistryMock.get(new MetadataKey(SERVICE_ID, stringItem.getName())))
|
||||
.thenReturn(new Metadata(new MetadataKey(SERVICE_ID, stringItem.getName()), "", stringConfig));
|
||||
// Mock items
|
||||
Mockito.when(itemRegistryMock.getAll()).thenReturn(List.of(switchItem, stringItem, groupItem, numberItem));
|
||||
|
||||
interpreter = new ActionTemplateInterpreter(itemRegistryMock, metadataRegistryMock, eventPublisherMock) {
|
||||
@Override
|
||||
protected ActionTemplateConfiguration[] getTypeActionConfigs(String itemType) {
|
||||
// mock type actions for testing
|
||||
if ("Switch".equals(itemType)) {
|
||||
return new ActionTemplateConfiguration[] { switchNPLWriteAction, switchNPLReadAction };
|
||||
}
|
||||
if ("Group".equals(itemType)) {
|
||||
return new ActionTemplateConfiguration[] { groupNPLWriteAction, groupNPLReadAction,
|
||||
groupNPLOptionReadAction, groupNPLOptionWriteAction };
|
||||
}
|
||||
return new ActionTemplateConfiguration[] {};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Test type write action
|
||||
*/
|
||||
@Test
|
||||
public void switchItemOnOffTest() throws InterpretationException {
|
||||
var response = interpreter.interpret(Locale.ENGLISH, "turn on bedroom light");
|
||||
assertThat(response, is("Done"));
|
||||
Mockito.verify(eventPublisherMock).post(ItemEventFactory.createCommandEvent("testSwitch", OnOffType.ON));
|
||||
response = interpreter.interpret(Locale.ENGLISH, "turn off bedroom light");
|
||||
assertThat(response, is("Done"));
|
||||
Mockito.verify(eventPublisherMock).post(ItemEventFactory.createCommandEvent("testSwitch", OnOffType.OFF));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test type read action
|
||||
*/
|
||||
@Test
|
||||
public void switchItemReadTest() throws InterpretationException {
|
||||
var response = interpreter.interpret(Locale.ENGLISH, "how is the bedroom light");
|
||||
assertThat(response, is("bedroom light is OFF"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test group write action targeting members
|
||||
*/
|
||||
@Test
|
||||
public void groupItemMemberOnTest() throws InterpretationException {
|
||||
var response = interpreter.interpret(Locale.ENGLISH, "turn on bedroom lights");
|
||||
assertThat(response, is("Done"));
|
||||
Mockito.verify(eventPublisherMock).post(ItemEventFactory.createCommandEvent("testSwitch", OnOffType.ON));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test group read action targeting members
|
||||
*/
|
||||
@Test
|
||||
public void groupItemMemberReadTest() throws InterpretationException {
|
||||
var response = interpreter.interpret(Locale.ENGLISH, "how is the light in the bedroom");
|
||||
assertThat(response, is("bedroom light in bedroom is off"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test target a group member item using the itemOption placeholder
|
||||
*/
|
||||
@Test
|
||||
public void groupItemOptionTest() throws InterpretationException {
|
||||
var response = interpreter.interpret(Locale.ENGLISH, "what channel is on the bedroom tv");
|
||||
assertThat(response, is("bedroom tv is on channel one"));
|
||||
response = interpreter.interpret(Locale.ENGLISH, "set bedroom channel to channel two");
|
||||
assertThat(response, is("Done"));
|
||||
Mockito.verify(eventPublisherMock)
|
||||
.post(ItemEventFactory.createCommandEvent("testNumber", new DecimalType("2")));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test write action using the dynamic label
|
||||
*/
|
||||
@Test
|
||||
public void messageTest() throws InterpretationException {
|
||||
var response = interpreter.interpret(Locale.ENGLISH, "send message please turn off the bedroom light to mark");
|
||||
// silent mode is enabled so no response
|
||||
assertThat(response, is(""));
|
||||
Mockito.verify(eventPublisherMock).post(ItemEventFactory.createCommandEvent("testString",
|
||||
new StringType("+34000000000:please turn off the bedroom light")));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user