[actiontemplatehli] Initial contribution (#12260)

* [actiontemplatehli] initial contribution

Signed-off-by: Miguel Álvarez <miguelwork92@gmail.com>
This commit is contained in:
GiviMAD
2022-06-19 13:39:31 +02:00
committed by GitHub
parent 11aa3207a6
commit daea9ae5b2
16 changed files with 2377 additions and 0 deletions

View File

@@ -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>

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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>

View File

@@ -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

View File

@@ -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")));
}
}