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 openhab#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

Unit tests added or extended to cover the new features.

Signed-off-by: Laurent Garnier <lg.hc@free.fr>
  • Loading branch information
lolodomo committed Oct 1, 2023
1 parent af4fce1 commit 10d85f4
Show file tree
Hide file tree
Showing 9 changed files with 767 additions and 226 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 @@ -35,6 +35,11 @@ public class WidgetDTO {

public String label;
public String icon;
/**
* staticIcon is a boolean indicating if the widget state must be ignored when requesting the icon.
* It is set to true when the widget has either the staticIcon property set or the icon property set
* with conditional rules.
*/
public Boolean staticIcon;
public String labelcolor;
public String valuecolor;
Expand Down
Loading

0 comments on commit 10d85f4

Please sign in to comment.