Skip to content
This repository has been archived by the owner on May 23, 2019. It is now read-only.

Commit

Permalink
Transition to metadata & semantic tags understanding
Browse files Browse the repository at this point in the history
This introduces BREAKING CHANGES!

Following the discussion in
eclipse-archived/smarthome#1093
the mechanism matching the entities extracted by
OpenNLP from the natural language query to ESH items
is being altered in this way:

1. Tags are not the primary conduit for item identification:
this change introduces the concept of "Named Attributes"
which will be implicitely affixed to items by a new class/
OSGi component, the `ItemNamedAttributesResolver`

2. Tags are now expected to conform to a semantic
tag library: the current version is at
https://github.com/eclipse/smarthome/wiki/Semantic-Tag-Library
HABot has internal translations for the most useful semantic
tags in the languages it understands and will derive named
attributes for items from those
`tagattributes_{locale].properties` resource bundles

3. In addition to tags, users may specify additional "monikers"
for items by using metadata in the "habot" namespace:

```
Group FF_ChildsRoom { habot="Amy's room" [type="location"] }
```

Those monikers will also be added to item's named attributes set.
The "type" configuration property is optional: if left unspecified,
monikers will have the "object" type.

4. Inheritance is still assumed for applied tags and monikers specified
in metadata for Group items, EXCEPT if the inheritTags configuration
property in the "habot" metadata namespace prevents it (for tags
only, metadata monikers are always inherited), like so:
```
Group Kitchen ["object:room"] { habot="Cuisine" [ inheritTags=false ] }
```

5. "habot:" prefixed tags will move gradually to the "habot" item
metadata namespace.

Signed-off-by: Yannick Schaus <[email protected]>
  • Loading branch information
ghys committed Jun 15, 2018
1 parent c3de12d commit 272d186
Show file tree
Hide file tree
Showing 21 changed files with 694 additions and 170 deletions.
31 changes: 19 additions & 12 deletions src/main/java/org/openhab/ui/habot/card/CardBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
package org.openhab.ui.habot.card;

import java.util.Collection;
import java.util.IllegalFormatConversionException;
import java.util.Set;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -184,7 +185,8 @@ public Card buildCard(Intent intent, Collection<Item> matchedItems) {
}

} else {
card.setTitle(getCardTitleFromGroupLabels(tags));
card.setTitle(String.join(", ", intent.getEntities().values()));
// card.setTitle(getCardTitleFromGroupLabels(tags));
card.setSubtitle(matchedItems.size() + " items"); // TODO: i18n

// TODO: detect images and build a HbCarousel with them - for webcams etc.
Expand Down Expand Up @@ -229,7 +231,8 @@ public Card buildChartCard(Intent intent, Collection<Item> matchedItems, String
card.setTitle(item.getLabel());
card.setSubtitle(item.getName());
} else {
card.setTitle(getCardTitleFromGroupLabels(tags));
card.setTitle(String.join(", ", intent.getEntities().values()));
// card.setTitle(getCardTitleFromGroupLabels(tags));
card.setSubtitle(matchedItems.size() + " items"); // TODO: i18n
}

Expand Down Expand Up @@ -261,18 +264,22 @@ private String getCardTitleFromGroupLabels(Set<String> tags) {

private String formatState(Item item, State state) throws TransformationException {
if (item.getStateDescription() != null) {
StateDescription stateDescription = item.getStateDescription();
if (stateDescription != null && stateDescription.getPattern() != null) {
String transformedState = TransformationHelper.transform(
FrameworkUtil.getBundle(HistoryLastChangesSkill.class).getBundleContext(),
stateDescription.getPattern(), state.toString());
if (transformedState.equals(state.toString())) {
return state.format(stateDescription.getPattern());
try {
StateDescription stateDescription = item.getStateDescription();
if (stateDescription != null && stateDescription.getPattern() != null) {
String transformedState = TransformationHelper.transform(
FrameworkUtil.getBundle(HistoryLastChangesSkill.class).getBundleContext(),
stateDescription.getPattern(), state.toString());
if (transformedState.equals(state.toString())) {
return state.format(stateDescription.getPattern());
} else {
return transformedState;
}

} else {
return transformedState;
return state.toString();
}

} else {
} catch (IllegalFormatConversionException e) {
return state.toString();
}
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,13 @@
package org.openhab.ui.habot.nlp;

import java.io.InputStream;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;

import org.eclipse.smarthome.core.items.GroupItem;
import org.eclipse.smarthome.core.items.Item;
import org.eclipse.smarthome.core.items.ItemRegistry;
import org.openhab.ui.habot.nlp.internal.AnswerFormatter;
import org.openhab.ui.habot.nlp.internal.ItemNamedAttributesResolver;

/**
* An abstract implmentation of a {@link Skill} with helper methods to find items matching an {@link Intent}
Expand All @@ -30,68 +28,24 @@
public abstract class AbstractItemIntentInterpreter implements Skill {

protected ItemRegistry itemRegistry;
protected ItemNamedAttributesResolver itemNamedAttributesResolver;
protected AnswerFormatter answerFormatter;

/**
* Returns the items matching the entities in the intent by looking for tags prefixed by "object:" or "location:".
* Group items are expanded and the tags, and tags are inherited to members.
* Returns the items matching the entities in the intent.
* It delegates this task to the {@link ItemNamedAttributesResolver} to find named attributes
* matching the entities.
*
* The resulting items should match the object AND the location if both are provided.
*
* @param intent the {@link Intent} containing the entities to match to items' tags.
* @return the set of matching items
*/
protected Set<Item> findItems(Intent intent) {
Collection<Item> itemsWithLocationTag = null;
if (intent.entities.containsKey("location")) {
itemsWithLocationTag = itemRegistry.getItemsByTag("location:" + intent.entities.get("location"));
}

Collection<Item> itemsWithObjectTag = null;
if (intent.entities.containsKey("object")) {
itemsWithObjectTag = itemRegistry.getItemsByTag("object:" + intent.entities.get("object"));
}

HashSet<Item> itemsMatchingLocationSlot = null;
if (itemsWithLocationTag != null) {
itemsMatchingLocationSlot = new HashSet<Item>();
for (Item item : itemsWithLocationTag) {
if (item instanceof GroupItem) {
GroupItem groupItem = (GroupItem) item;
for (Item member : groupItem.getAllMembers()) {
itemsMatchingLocationSlot.add(member);
}
} else {
itemsMatchingLocationSlot.add(item);
}
}
}
String object = intent.getEntities().get("object");
String location = intent.getEntities().get("location");

HashSet<Item> itemsMatchingObjectSlot = null;
if (itemsWithObjectTag != null) {
itemsMatchingObjectSlot = new HashSet<Item>();
for (Item item : itemsWithObjectTag) {
if (item instanceof GroupItem) {
GroupItem groupItem = (GroupItem) item;
for (Item member : groupItem.getAllMembers()) {
itemsMatchingObjectSlot.add(member);
}
} else {
itemsMatchingObjectSlot.add(item);
}
}
}

if (itemsMatchingLocationSlot == null && itemsMatchingObjectSlot == null) {
return null;
} else if (itemsMatchingObjectSlot == null) {
return itemsMatchingLocationSlot;
} else if (itemsMatchingLocationSlot == null) {
return itemsMatchingObjectSlot;
} else {
return itemsMatchingLocationSlot.stream().filter(itemsMatchingObjectSlot::contains)
.collect(Collectors.toSet());
}
return this.itemNamedAttributesResolver.getMatchingItems(object, location).collect(Collectors.toSet());
}

@Override
Expand All @@ -107,5 +61,4 @@ public InputStream getTrainingData(String language) throws UnsupportedLanguageEx

return trainingData;
}

}
109 changes: 109 additions & 0 deletions src/main/java/org/openhab/ui/habot/nlp/ItemNamedAttribute.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/**
* Copyright (c) 2010-2018 by the respective copyright holders.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.openhab.ui.habot.nlp;

public class ItemNamedAttribute {
public enum AttributeSource {
TAG,
METADATA
}

public ItemNamedAttribute(String type, String value, boolean inherited, AttributeSource source) {
super();
this.type = type;
this.value = value;
this.inherited = inherited;
this.source = source;
}

String type;
String value;
boolean inherited;
AttributeSource source;

public String getType() {
return type;
}

public void setType(String type) {
this.type = type;
}

public String getValue() {
return value;
}

public void setValue(String value) {
this.value = value;
}

public boolean isInherited() {
return inherited;
}

public void setInherited(boolean inherited) {
this.inherited = inherited;
}

public AttributeSource getSource() {
return source;
}

public void setSource(AttributeSource source) {
this.source = source;
}

@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (inherited ? 1231 : 1237);
result = prime * result + ((source == null) ? 0 : source.hashCode());
result = prime * result + ((type == null) ? 0 : type.hashCode());
result = prime * result + ((value == null) ? 0 : value.hashCode());
return result;
}

@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
ItemNamedAttribute other = (ItemNamedAttribute) obj;
// doesn't matter
// if (inherited != other.inherited) {
// return false;
// }
// if (source != other.source) {
// return false;
// }
if (type == null) {
if (other.type != null) {
return false;
}
} else if (!type.equals(other.type)) {
return false;
}
if (value == null) {
if (other.value != null) {
return false;
}
} else if (!value.equals(other.value)) {
return false;
}
return true;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,12 @@ public UnsupportedLanguageException(String language) {
this.language = Locale.forLanguageTag(language);
}

public UnsupportedLanguageException(Locale locale) {
this.language = locale;
}

@Override
public String getMessage() {
return "Unsupported language: " + language.toString();
return String.format("Unsupported language: %s", language.toString());
}
}
Loading

0 comments on commit 272d186

Please sign in to comment.