Skip to content

Commit

Permalink
[sitemap] AND operator accepted in any condition + added optional con…
Browse files Browse the repository at this point in the history
…ditional rules for icon

Allow multiple conditions with AND operator in visibility/color/icon rules

Closes #3058

Also allow dynamic icon based on other item states.
Allow dynamic icon even with non OH icon sources.

Example: icon=[item1>0=temperature,==0=material:settings,f7:house]

Related to openhab/openhab-webui#1938

Signed-off-by: Laurent Garnier <[email protected]>
  • Loading branch information
lolodomo committed Sep 30, 2023
1 parent af4fce1 commit 53abb23
Show file tree
Hide file tree
Showing 8 changed files with 352 additions and 178 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import java.util.stream.Collectors;

import org.eclipse.emf.common.util.EList;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.common.ThreadPoolManager;
import org.openhab.core.events.Event;
import org.openhab.core.events.EventSubscriber;
Expand All @@ -36,7 +37,9 @@
import org.openhab.core.library.CoreItemFactory;
import org.openhab.core.model.sitemap.sitemap.Chart;
import org.openhab.core.model.sitemap.sitemap.ColorArray;
import org.openhab.core.model.sitemap.sitemap.Condition;
import org.openhab.core.model.sitemap.sitemap.Frame;
import org.openhab.core.model.sitemap.sitemap.IconRule;
import org.openhab.core.model.sitemap.sitemap.VisibilityRule;
import org.openhab.core.model.sitemap.sitemap.Widget;
import org.openhab.core.types.State;
Expand All @@ -47,6 +50,7 @@
*
* @author Kai Kreuzer - Initial contribution
* @author Laurent Garnier - Added support for icon color
* @author Laurent Garnier - New widget icon parameter based on conditional rules + multiple AND conditions
*/
public class PageChangeListener implements EventSubscriber {

Expand Down Expand Up @@ -119,27 +123,39 @@ private Set<Item> getAllItems(EList<Widget> widgets) {
if (widget instanceof Frame frame) {
items.addAll(getAllItems(frame.getChildren()));
}
// now scan dynamic icon rules
for (IconRule rule : widget.getDynamicIcon()) {
addItemsFromConditions(items, rule.getConditions());
}
// now scan visibility rules
for (VisibilityRule rule : widget.getVisibility()) {
addItemWithName(items, rule.getItem());
addItemsFromConditions(items, rule.getConditions());
}
// now scan label color rules
for (ColorArray rule : widget.getLabelColor()) {
addItemWithName(items, rule.getItem());
addItemsFromConditions(items, rule.getConditions());
}
// now scan value color rules
for (ColorArray rule : widget.getValueColor()) {
addItemWithName(items, rule.getItem());
addItemsFromConditions(items, rule.getConditions());
}
// now scan value icon rules
// now scan icon color rules
for (ColorArray rule : widget.getIconColor()) {
addItemWithName(items, rule.getItem());
addItemsFromConditions(items, rule.getConditions());
}
}
}
return items;
}

private void addItemsFromConditions(Set<Item> items, @Nullable EList<Condition> conditions) {
if (conditions != null) {
for (Condition condition : conditions) {
addItemWithName(items, condition.getItem());
}
}
}

private void addItemWithName(Set<Item> items, String itemName) {
if (itemName != null) {
try {
Expand Down Expand Up @@ -183,7 +199,7 @@ private Set<SitemapEvent> constructSitemapEvents(Item item, State state, List<Wi
if (!skipWidget && w instanceof Chart chartWidget) {
skipWidget = chartWidget.getRefresh() > 0;
}
if (!skipWidget || definesVisibilityOrColor(w, item.getName())) {
if (!skipWidget || definesVisibilityOrColorOrIcon(w, item.getName())) {
SitemapWidgetEvent event = constructSitemapEventForWidget(item, state, w);
events.add(event);
}
Expand All @@ -197,6 +213,9 @@ private SitemapWidgetEvent constructSitemapEventForWidget(Item item, State state
event.pageId = pageId;
event.label = itemUIRegistry.getLabel(widget);
event.widgetId = itemUIRegistry.getWidgetId(widget);
if (widget.getStaticIcon() == null) {
event.icon = itemUIRegistry.getCategory(widget);
}
event.visibility = itemUIRegistry.getVisiblity(widget);
event.descriptionChanged = false;
// event.item contains the (potentially changed) data of the item belonging to
Expand Down Expand Up @@ -237,11 +256,16 @@ private Item getItemForWidget(Widget w) {
return null;
}

private boolean definesVisibilityOrColor(Widget w, String name) {
return w.getVisibility().stream().anyMatch(r -> name.equals(r.getItem()))
|| w.getLabelColor().stream().anyMatch(r -> name.equals(r.getItem()))
|| w.getValueColor().stream().anyMatch(r -> name.equals(r.getItem()))
|| w.getIconColor().stream().anyMatch(r -> name.equals(r.getItem()));
private boolean definesVisibilityOrColorOrIcon(Widget w, String name) {
return w.getVisibility().stream().anyMatch(r -> conditionsDependsOnItem(r.getConditions(), name))
|| w.getLabelColor().stream().anyMatch(r -> conditionsDependsOnItem(r.getConditions(), name))
|| w.getValueColor().stream().anyMatch(r -> conditionsDependsOnItem(r.getConditions(), name))
|| w.getIconColor().stream().anyMatch(r -> conditionsDependsOnItem(r.getConditions(), name))
|| w.getDynamicIcon().stream().anyMatch(r -> conditionsDependsOnItem(r.getConditions(), name));
}

private boolean conditionsDependsOnItem(@Nullable EList<Condition> conditions, String name) {
return conditions != null && conditions.stream().anyMatch(c -> name.equals(c.getItem()));
}

public void sitemapContentChanged(EList<Widget> widgets) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,9 @@
import org.openhab.core.model.sitemap.SitemapProvider;
import org.openhab.core.model.sitemap.sitemap.Chart;
import org.openhab.core.model.sitemap.sitemap.ColorArray;
import org.openhab.core.model.sitemap.sitemap.Condition;
import org.openhab.core.model.sitemap.sitemap.Frame;
import org.openhab.core.model.sitemap.sitemap.IconRule;
import org.openhab.core.model.sitemap.sitemap.Image;
import org.openhab.core.model.sitemap.sitemap.Input;
import org.openhab.core.model.sitemap.sitemap.LinkableWidget;
Expand Down Expand Up @@ -131,6 +133,7 @@
* @author Wouter Born - Migrated to OpenAPI annotations
* @author Laurent Garnier - Added support for icon color
* @author Mark Herwege - Added pattern and unit fields
* @author Laurent Garnier - New widget icon parameter based on conditional rules + multiple AND conditions
*/
@Component(service = { RESTResource.class, EventSubscriber.class })
@JaxrsResource
Expand Down Expand Up @@ -523,7 +526,7 @@ private PageDTO createPageBean(String sitemapName, @Nullable String title, @Null
}
bean.widgetId = widgetId;
bean.icon = itemUIRegistry.getCategory(widget);
bean.staticIcon = widget.getStaticIcon() != null;
bean.staticIcon = widget.getStaticIcon() != null || !widget.getDynamicIcon().isEmpty();
bean.labelcolor = convertItemValueColor(itemUIRegistry.getLabelColor(widget), itemState);
bean.valuecolor = convertItemValueColor(itemUIRegistry.getValueColor(widget), itemState);
bean.iconcolor = convertItemValueColor(itemUIRegistry.getIconColor(widget), itemState);
Expand Down Expand Up @@ -741,6 +744,8 @@ private Set<GenericItem> getAllItems(EList<Widget> widgets) {
if (widget instanceof Frame frame) {
items.addAll(getAllItems(frame.getChildren()));
}
// Consider items involved in any icon condition
items.addAll(getItemsInIconCond(widget.getDynamicIcon()));
// Consider items involved in any visibility, labelcolor, valuecolor and iconcolor condition
items.addAll(getItemsInVisibilityCond(widget.getVisibility()));
items.addAll(getItemsInColorCond(widget.getLabelColor()));
Expand All @@ -753,37 +758,43 @@ private Set<GenericItem> getAllItems(EList<Widget> widgets) {
private Set<GenericItem> getItemsInVisibilityCond(EList<VisibilityRule> ruleList) {
Set<GenericItem> items = new HashSet<>();
for (VisibilityRule rule : ruleList) {
String itemName = rule.getItem();
if (itemName != null) {
try {
Item item = itemUIRegistry.getItem(itemName);
if (item instanceof GenericItem genericItem) {
items.add(genericItem);
}
} catch (ItemNotFoundException e) {
// ignore
}
}
getItemsInConditions(rule.getConditions(), items);
}
return items;
}

private Set<GenericItem> getItemsInColorCond(EList<ColorArray> colorList) {
Set<GenericItem> items = new HashSet<>();
for (ColorArray color : colorList) {
String itemName = color.getItem();
if (itemName != null) {
try {
Item item = itemUIRegistry.getItem(itemName);
if (item instanceof GenericItem genericItem) {
items.add(genericItem);
for (ColorArray rule : colorList) {
getItemsInConditions(rule.getConditions(), items);
}
return items;
}

private Set<GenericItem> getItemsInIconCond(EList<IconRule> ruleList) {
Set<GenericItem> items = new HashSet<>();
for (IconRule rule : ruleList) {
getItemsInConditions(rule.getConditions(), items);
}
return items;
}

private void getItemsInConditions(@Nullable EList<Condition> conditions, Set<GenericItem> items) {
if (conditions != null) {
for (Condition condition : conditions) {
String itemName = condition.getItem();
if (itemName != null) {
try {
Item item = itemUIRegistry.getItem(itemName);
if (item instanceof GenericItem genericItem) {
items.add(genericItem);
}
} catch (ItemNotFoundException e) {
// ignore
}
} catch (ItemNotFoundException e) {
// ignore
}
}
}
return items;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
import org.openhab.core.library.types.PercentType;
import org.openhab.core.model.sitemap.SitemapProvider;
import org.openhab.core.model.sitemap.sitemap.ColorArray;
import org.openhab.core.model.sitemap.sitemap.Condition;
import org.openhab.core.model.sitemap.sitemap.Sitemap;
import org.openhab.core.model.sitemap.sitemap.VisibilityRule;
import org.openhab.core.model.sitemap.sitemap.Widget;
Expand Down Expand Up @@ -330,31 +331,49 @@ private EList<Widget> initSitemapWidgets() {
when(w1.eClass()).thenReturn(sliderEClass);
when(w1.getLabel()).thenReturn(WIDGET1_LABEL);
when(w1.getItem()).thenReturn(ITEM_NAME);
when(w1.getDynamicIcon()).thenReturn(new BasicEList<>());
when(w1.getStaticIcon()).thenReturn(null);

// add visibility rules to the mock widget:
VisibilityRule visibilityRule = mock(VisibilityRule.class);
when(visibilityRule.getItem()).thenReturn(VISIBILITY_RULE_ITEM_NAME);
Condition conditon = mock(Condition.class);
when(conditon.getItem()).thenReturn(VISIBILITY_RULE_ITEM_NAME);
BasicEList<Condition> conditions = new BasicEList<>();
conditions.add(conditon);
when(visibilityRule.getConditions()).thenReturn(conditions);
BasicEList<VisibilityRule> visibilityRules = new BasicEList<>(1);
visibilityRules.add(visibilityRule);
when(w1.getVisibility()).thenReturn(visibilityRules);

// add label color conditions to the item:
ColorArray labelColor = mock(ColorArray.class);
when(labelColor.getItem()).thenReturn(LABEL_COLOR_ITEM_NAME);
Condition conditon1 = mock(Condition.class);
when(conditon1.getItem()).thenReturn(LABEL_COLOR_ITEM_NAME);
BasicEList<Condition> conditions1 = new BasicEList<>();
conditions1.add(conditon1);
when(labelColor.getConditions()).thenReturn(conditions1);
EList<ColorArray> labelColors = new BasicEList<>();
labelColors.add(labelColor);
when(w1.getLabelColor()).thenReturn(labelColors);

// add value color conditions to the item:
ColorArray valueColor = mock(ColorArray.class);
when(valueColor.getItem()).thenReturn(VALUE_COLOR_ITEM_NAME);
Condition conditon2 = mock(Condition.class);
when(conditon2.getItem()).thenReturn(VALUE_COLOR_ITEM_NAME);
BasicEList<Condition> conditions2 = new BasicEList<>();
conditions2.add(conditon2);
when(valueColor.getConditions()).thenReturn(conditions2);
EList<ColorArray> valueColors = new BasicEList<>();
valueColors.add(valueColor);
when(w1.getValueColor()).thenReturn(valueColors);

// add icon color conditions to the item:
ColorArray iconColor = mock(ColorArray.class);
when(iconColor.getItem()).thenReturn(ICON_COLOR_ITEM_NAME);
Condition conditon3 = mock(Condition.class);
when(conditon3.getItem()).thenReturn(ICON_COLOR_ITEM_NAME);
BasicEList<Condition> conditions3 = new BasicEList<>();
conditions3.add(conditon3);
when(iconColor.getConditions()).thenReturn(conditions3);
EList<ColorArray> iconColors = new BasicEList<>();
iconColors.add(iconColor);
when(w1.getIconColor()).thenReturn(iconColors);
Expand All @@ -371,6 +390,8 @@ private EList<Widget> initSitemapWidgets() {
when(w2.eClass()).thenReturn(switchEClass);
when(w2.getLabel()).thenReturn(WIDGET2_LABEL);
when(w2.getItem()).thenReturn(ITEM_NAME);
when(w2.getDynamicIcon()).thenReturn(new BasicEList<>());
when(w2.getStaticIcon()).thenReturn(null);
when(w2.getVisibility()).thenReturn(visibilityRules);
when(w2.getLabelColor()).thenReturn(labelColors);
when(w2.getValueColor()).thenReturn(valueColors);
Expand Down
Loading

0 comments on commit 53abb23

Please sign in to comment.