Skip to content

Commit

Permalink
[Sitemap] Add optional conditional rules for icon
Browse files Browse the repository at this point in the history
This allows dynamic icons based on items states even with non OH icon sources.
This also allows overwritting the default handling with state done by the icon servlet.

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 Oct 2, 2023
1 parent dda021a commit 6b3e5e9
Show file tree
Hide file tree
Showing 10 changed files with 461 additions and 24 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
*/
public class PageChangeListener implements EventSubscriber {

Expand Down Expand Up @@ -119,6 +123,10 @@ private Set<Item> getAllItems(EList<Widget> widgets) {
if (widget instanceof Frame frame) {
items.addAll(getAllItems(frame.getChildren()));
}
// now scan icon rules
for (IconRule rule : widget.getIconRules()) {
addItemsFromConditions(items, rule.getConditions());
}
// now scan visibility rules
for (VisibilityRule rule : widget.getVisibility()) {
addItemWithName(items, rule.getItem());
Expand All @@ -140,6 +148,14 @@ private Set<Item> getAllItems(EList<Widget> widgets) {
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);
event.icon = itemUIRegistry.getCategory(widget);
event.staticIcon = widget.getStaticIcon() != null || !widget.getIconRules().isEmpty();
event.reloadIcon = widget.getStaticIcon() == null;
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) {
private boolean definesVisibilityOrColorOrIcon(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()));
|| w.getIconColor().stream().anyMatch(r -> name.equals(r.getItem()))
|| w.getIconRules().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 @@ -80,7 +80,9 @@
import org.openhab.core.model.sitemap.sitemap.Buttongrid;
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 @@ -135,6 +137,7 @@
* @author Mark Herwege - Added pattern and unit fields
* @author Laurent Garnier - Added support for new sitemap element Buttongrid
* @author Laurent Garnier - Added icon field for mappings used for switch element
* @author Laurent Garnier - New widget icon parameter based on conditional rules
*/
@Component(service = { RESTResource.class, EventSubscriber.class })
@JaxrsResource
Expand Down Expand Up @@ -527,7 +530,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.getIconRules().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 @@ -757,6 +760,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.getIconRules()));
// Consider items involved in any visibility, labelcolor, valuecolor and iconcolor condition
items.addAll(getItemsInVisibilityCond(widget.getVisibility()));
items.addAll(getItemsInColorCond(widget.getLabelColor()));
Expand Down Expand Up @@ -802,6 +807,32 @@ private Set<GenericItem> getItemsInColorCond(EList<ColorArray> colorList) {
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
}
}
}
}
}

@Override
public Set<String> getSubscribedEventTypes() {
return Set.of(ItemStateChangedEvent.TYPE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,21 @@
*
* @author Kai Kreuzer - Initial contribution
* @author Laurent Garnier - New field iconcolor
* @author Laurent Garnier - New fields staticIcon and reloadIcon
*/
public class SitemapWidgetEvent extends SitemapEvent {

public String widgetId;

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 boolean reloadIcon;
public String labelcolor;
public String valuecolor;
public String iconcolor;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,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 6b3e5e9

Please sign in to comment.